jazzsonic.c 8.1 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
/*
2 3 4 5 6 7
 * jazzsonic.c
 *
 * (C) 2005 Finn Thain
 *
 * Converted to DMA API, and (from the mac68k project) introduced
 * dhd's support for 16-bit cards.
Linus Torvalds's avatar
Linus Torvalds committed
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
 *
 * (C) 1996,1998 by Thomas Bogendoerfer (tsbogend@alpha.franken.de)
 * 
 * This driver is based on work from Andreas Busse, but most of
 * the code is rewritten.
 * 
 * (C) 1995 by Andreas Busse (andy@waldorf-gmbh.de)
 *
 * A driver for the onboard Sonic ethernet controller on Mips Jazz
 * systems (Acer Pica-61, Mips Magnum 4000, Olivetti M700 and
 * perhaps others, too)
 */

#include <linux/kernel.h>
#include <linux/module.h>
#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/errno.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
36
#include <linux/platform_device.h>
37
#include <linux/dma-mapping.h>
Linus Torvalds's avatar
Linus Torvalds committed
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56

#include <asm/bootinfo.h>
#include <asm/system.h>
#include <asm/pgtable.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/jazz.h>
#include <asm/jazzdma.h>

static char jazz_sonic_string[] = "jazzsonic";
static struct platform_device *jazz_sonic_device;

#define SONIC_MEM_SIZE	0x100

#include "sonic.h"

/*
 * Macros to access SONIC registers
 */
57
#define SONIC_READ(reg) (*((volatile unsigned int *)dev->base_addr+reg))
Linus Torvalds's avatar
Linus Torvalds committed
58 59 60

#define SONIC_WRITE(reg,val)						\
do {									\
61
	*((volatile unsigned int *)dev->base_addr+(reg)) = (val);		\
Linus Torvalds's avatar
Linus Torvalds committed
62 63 64
} while (0)


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

/*
 * Base address and interrupt of the SONIC controller on JAZZ boards
 */
static struct {
	unsigned int port;
	unsigned int irq;
} sonic_portlist[] = { {JAZZ_ETHERNET_BASE, JAZZ_ETHERNET_IRQ}, {0, 0}};

/*
 * We cannot use station (ethernet) address prefixes to detect the
 * sonic controller since these are board manufacturer depended.
 * So we check for known Silicon Revision IDs instead. 
 */
static unsigned short known_revisions[] =
{
	0x04,			/* Mips Magnum 4000 */
	0xffff			/* end of list */
};

91
static int __init sonic_probe1(struct net_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
92 93 94 95
{
	static unsigned version_printed;
	unsigned int silicon_revision;
	unsigned int val;
96
	struct sonic_local *lp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
97 98 99
	int err = -ENODEV;
	int i;

100
	if (!request_mem_region(dev->base_addr, SONIC_MEM_SIZE, jazz_sonic_string))
Linus Torvalds's avatar
Linus Torvalds committed
101
		return -EBUSY;
102

Linus Torvalds's avatar
Linus Torvalds committed
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
	/*
	 * get the Silicon Revision ID. If this is one of the known
	 * one assume that we found a SONIC ethernet controller at
	 * the expected location.
	 */
	silicon_revision = SONIC_READ(SONIC_SR);
	if (sonic_debug > 1)
		printk("SONIC Silicon Revision = 0x%04x\n",silicon_revision);

	i = 0;
	while (known_revisions[i] != 0xffff
	       && known_revisions[i] != silicon_revision)
		i++;

	if (known_revisions[i] == 0xffff) {
		printk("SONIC ethernet controller not found (0x%4x)\n",
		       silicon_revision);
		goto out;
	}
    
	if (sonic_debug  &&  version_printed++ == 0)
		printk(version);

126
	printk(KERN_INFO "%s: Sonic ethernet found at 0x%08lx, ", lp->device->bus_id, dev->base_addr);
Linus Torvalds's avatar
Linus Torvalds committed
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143

	/*
	 * Put the sonic into software reset, then
	 * retrieve and print the ethernet address.
	 */
	SONIC_WRITE(SONIC_CMD,SONIC_CR_RST);
	SONIC_WRITE(SONIC_CEP,0);
	for (i=0; i<3; i++) {
		val = SONIC_READ(SONIC_CAP0-i);
		dev->dev_addr[i*2] = val;
		dev->dev_addr[i*2+1] = val >> 8;
	}

	err = -ENOMEM;
    
	/* Initialize the device structure. */

144
	lp->dma_bitmode = SONIC_BITMODE32;
Linus Torvalds's avatar
Linus Torvalds committed
145

146 147 148 149 150 151 152
	/* Allocate the entire chunk of memory for the descriptors.
           Note that this cannot cross a 64K boundary. */
	if ((lp->descriptors = dma_alloc_coherent(lp->device,
				SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode),
				&lp->descriptors_laddr, GFP_KERNEL)) == NULL) {
		printk(KERN_ERR "%s: couldn't alloc DMA memory for descriptors.\n", lp->device->bus_id);
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
153 154
	}

155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
	/* Now set up the pointers to point to the appropriate places */
	lp->cda = lp->descriptors;
	lp->tda = lp->cda + (SIZEOF_SONIC_CDA
	                     * SONIC_BUS_SCALE(lp->dma_bitmode));
	lp->rda = lp->tda + (SIZEOF_SONIC_TD * SONIC_NUM_TDS
	                     * SONIC_BUS_SCALE(lp->dma_bitmode));
	lp->rra = lp->rda + (SIZEOF_SONIC_RD * SONIC_NUM_RDS
	                     * SONIC_BUS_SCALE(lp->dma_bitmode));

	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
172 173 174
	dev->open = sonic_open;
	dev->stop = sonic_close;
	dev->hard_start_xmit = sonic_send_packet;
175
	dev->get_stats = sonic_get_stats;
Linus Torvalds's avatar
Linus Torvalds committed
176
	dev->set_multicast_list = &sonic_multicast_list;
177
	dev->tx_timeout = sonic_tx_timeout;
Linus Torvalds's avatar
Linus Torvalds committed
178 179 180 181 182 183 184 185 186 187 188
	dev->watchdog_timeo = TX_TIMEOUT;

	/*
	 * clear tally counter
	 */
	SONIC_WRITE(SONIC_CRCT,0xffff);
	SONIC_WRITE(SONIC_FAET,0xffff);
	SONIC_WRITE(SONIC_MPT,0xffff);

	return 0;
out:
189
	release_region(dev->base_addr, SONIC_MEM_SIZE);
Linus Torvalds's avatar
Linus Torvalds committed
190 191 192 193 194 195 196
	return err;
}

/*
 * Probe for a SONIC ethernet controller on a Mips Jazz board.
 * Actually probing is superfluous but we're paranoid.
 */
197
static int __init jazz_sonic_probe(struct platform_device *pdev)
Linus Torvalds's avatar
Linus Torvalds committed
198 199 200 201 202 203 204 205 206 207 208 209
{
	struct net_device *dev;
	struct sonic_local *lp;
	int err = 0;
	int i;

	/*
	 * Don't probe if we're not running on a Jazz board.
	 */
	if (mips_machgroup != MACH_GROUP_JAZZ)
		return -ENODEV;

210
	dev = alloc_etherdev(sizeof(struct sonic_local));
Linus Torvalds's avatar
Linus Torvalds committed
211 212 213
	if (!dev)
		return -ENOMEM;

214
	lp = netdev_priv(dev);
215 216
	lp->device = &pdev->dev;
	SET_NETDEV_DEV(dev, &pdev->dev);
217 218
 	SET_MODULE_OWNER(dev);

Linus Torvalds's avatar
Linus Torvalds committed
219 220
	netdev_boot_setup_check(dev);

221 222 223
	if (dev->base_addr >= KSEG0) { /* Check a single specified location. */
		err = sonic_probe1(dev);
	} else if (dev->base_addr != 0) { /* Don't probe at all. */
Linus Torvalds's avatar
Linus Torvalds committed
224 225 226
		err = -ENXIO;
	} else {
		for (i = 0; sonic_portlist[i].port; i++) {
227 228 229
			dev->base_addr = sonic_portlist[i].port;
			dev->irq = sonic_portlist[i].irq;
			if (sonic_probe1(dev) == 0)
Linus Torvalds's avatar
Linus Torvalds committed
230 231 232 233 234 235 236 237 238 239 240
				break;
		}
		if (!sonic_portlist[i].port)
			err = -ENODEV;
	}
	if (err)
		goto out;
	err = register_netdev(dev);
	if (err)
		goto out1;

241 242 243 244 245 246 247 248
	printk("%s: MAC ", dev->name);
	for (i = 0; i < 6; i++) {
		printk("%2.2x", dev->dev_addr[i]);
		if (i < 5)
			printk(":");
	}
	printk(" IRQ %d\n", dev->irq);

Linus Torvalds's avatar
Linus Torvalds committed
249 250 251 252 253 254 255 256 257 258
	return 0;

out1:
	release_region(dev->base_addr, SONIC_MEM_SIZE);
out:
	free_netdev(dev);

	return err;
}

259 260 261
MODULE_DESCRIPTION("Jazz SONIC ethernet driver");
module_param(sonic_debug, int, 0);
MODULE_PARM_DESC(sonic_debug, "jazzsonic debug level (1-4)");
Linus Torvalds's avatar
Linus Torvalds committed
262

263
#define SONIC_IRQ_FLAG SA_INTERRUPT
Linus Torvalds's avatar
Linus Torvalds committed
264 265 266

#include "sonic.c"

267
static int __devexit jazz_sonic_device_remove (struct platform_device *pdev)
Linus Torvalds's avatar
Linus Torvalds committed
268
{
269
	struct net_device *dev = platform_get_drvdata(pdev);
270
	struct sonic_local* lp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
271 272

	unregister_netdev (dev);
273 274
	dma_free_coherent(lp->device, SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode),
	                  lp->descriptors, lp->descriptors_laddr);
Linus Torvalds's avatar
Linus Torvalds committed
275 276 277 278 279 280
	release_region (dev->base_addr, SONIC_MEM_SIZE);
	free_netdev (dev);

	return 0;
}

281
static struct platform_driver jazz_sonic_driver = {
Linus Torvalds's avatar
Linus Torvalds committed
282 283
	.probe	= jazz_sonic_probe,
	.remove	= __devexit_p(jazz_sonic_device_remove),
284 285 286
	.driver	= {
		.name	= jazz_sonic_string,
	},
Linus Torvalds's avatar
Linus Torvalds committed
287
};
288

Linus Torvalds's avatar
Linus Torvalds committed
289 290
static int __init jazz_sonic_init_module(void)
{
291
	int err;
Linus Torvalds's avatar
Linus Torvalds committed
292

293
	if ((err = platform_driver_register(&jazz_sonic_driver))) {
Linus Torvalds's avatar
Linus Torvalds committed
294
		printk(KERN_ERR "Driver registration failed\n");
295
		return err;
Linus Torvalds's avatar
Linus Torvalds committed
296 297
	}

Russell King's avatar
Russell King committed
298
	jazz_sonic_device = platform_device_alloc(jazz_sonic_string, 0);
299
	if (!jazz_sonic_device)
Linus Torvalds's avatar
Linus Torvalds committed
300 301
		goto out_unregister;

Russell King's avatar
Russell King committed
302 303
	if (platform_device_add(jazz_sonic_device)) {
		platform_device_put(jazz_sonic_device);
Linus Torvalds's avatar
Linus Torvalds committed
304 305 306 307 308 309
		jazz_sonic_device = NULL;
	}

	return 0;

out_unregister:
310
	platform_driver_unregister(&jazz_sonic_driver);
Linus Torvalds's avatar
Linus Torvalds committed
311 312 313 314 315 316

	return -ENOMEM;
}

static void __exit jazz_sonic_cleanup_module(void)
{
317
	platform_driver_unregister(&jazz_sonic_driver);
Linus Torvalds's avatar
Linus Torvalds committed
318 319 320 321 322 323 324 325 326

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

module_init(jazz_sonic_init_module);
module_exit(jazz_sonic_cleanup_module);