sealevel.c 7.9 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10
/*
 *	Sealevel Systems 4021 driver.
 *
 *	This program is free software; you can redistribute it and/or
 *	modify it under the terms of the GNU General Public License
 *	as published by the Free Software Foundation; either version
 *	2 of the License, or (at your option) any later version.
 *
 *	(c) Copyright 1999, 2001 Alan Cox
 *	(c) Copyright 2001 Red Hat Inc.
11
 *	Generic HDLC port Copyright (C) 2008 Krzysztof Halasa <khc@pm.waw.pl>
L
Linus Torvalds 已提交
12 13 14 15 16 17 18 19 20 21 22
 *
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/net.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/delay.h>
23
#include <linux/hdlc.h>
L
Linus Torvalds 已提交
24 25 26 27
#include <linux/ioport.h>
#include <linux/init.h>
#include <net/arp.h>

28
#include <asm/irq.h>
L
Linus Torvalds 已提交
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/byteorder.h>
#include "z85230.h"


struct slvl_device
{
	struct z8530_channel *chan;
	int channel;
};


struct slvl_board
{
44
	struct slvl_device dev[2];
L
Linus Torvalds 已提交
45 46 47 48 49 50 51 52
	struct z8530_dev board;
	int iobase;
};

/*
 *	Network driver support routines
 */

53 54 55 56 57
static inline struct slvl_device* dev_to_chan(struct net_device *dev)
{
	return (struct slvl_device *)dev_to_hdlc(dev)->priv;
}

L
Linus Torvalds 已提交
58
/*
59
 *	Frame receive. Simple for our card as we do HDLC and there
L
Linus Torvalds 已提交
60 61
 *	is no funny garbage involved
 */
62

L
Linus Torvalds 已提交
63 64 65
static void sealevel_input(struct z8530_channel *c, struct sk_buff *skb)
{
	/* Drop the CRC - it's not a good idea to try and negotiate it ;) */
66 67
	skb_trim(skb, skb->len - 2);
	skb->protocol = hdlc_type_trans(skb, c->netdevice);
68
	skb_reset_mac_header(skb);
69
	skb->dev = c->netdevice;
L
Linus Torvalds 已提交
70 71
	netif_rx(skb);
}
72

L
Linus Torvalds 已提交
73 74
/*
 *	We've been placed in the UP state
75 76
 */

L
Linus Torvalds 已提交
77 78
static int sealevel_open(struct net_device *d)
{
79
	struct slvl_device *slvl = dev_to_chan(d);
L
Linus Torvalds 已提交
80 81
	int err = -1;
	int unit = slvl->channel;
82

L
Linus Torvalds 已提交
83
	/*
84
	 *	Link layer up.
L
Linus Torvalds 已提交
85 86
	 */

87
	switch (unit)
L
Linus Torvalds 已提交
88 89
	{
		case 0:
90
			err = z8530_sync_dma_open(d, slvl->chan);
L
Linus Torvalds 已提交
91 92
			break;
		case 1:
93
			err = z8530_sync_open(d, slvl->chan);
L
Linus Torvalds 已提交
94 95
			break;
	}
96 97

	if (err)
L
Linus Torvalds 已提交
98
		return err;
99 100 101 102

	err = hdlc_open(d);
	if (err) {
		switch (unit) {
L
Linus Torvalds 已提交
103 104 105 106 107 108
			case 0:
				z8530_sync_dma_close(d, slvl->chan);
				break;
			case 1:
				z8530_sync_close(d, slvl->chan);
				break;
109
		}
L
Linus Torvalds 已提交
110 111
		return err;
	}
112 113 114

	slvl->chan->rx_function = sealevel_input;

L
Linus Torvalds 已提交
115 116 117 118 119 120 121 122 123
	/*
	 *	Go go go
	 */
	netif_start_queue(d);
	return 0;
}

static int sealevel_close(struct net_device *d)
{
124
	struct slvl_device *slvl = dev_to_chan(d);
L
Linus Torvalds 已提交
125
	int unit = slvl->channel;
126

L
Linus Torvalds 已提交
127 128 129 130
	/*
	 *	Discard new frames
	 */

131 132 133
	slvl->chan->rx_function = z8530_null_rx;

	hdlc_close(d);
L
Linus Torvalds 已提交
134
	netif_stop_queue(d);
135 136

	switch (unit)
L
Linus Torvalds 已提交
137 138 139 140 141 142 143 144 145 146 147 148 149
	{
		case 0:
			z8530_sync_dma_close(d, slvl->chan);
			break;
		case 1:
			z8530_sync_close(d, slvl->chan);
			break;
	}
	return 0;
}

static int sealevel_ioctl(struct net_device *d, struct ifreq *ifr, int cmd)
{
150
	/* struct slvl_device *slvl=dev_to_chan(d);
L
Linus Torvalds 已提交
151
	   z8530_ioctl(d,&slvl->sync.chanA,ifr,cmd) */
152
	return hdlc_ioctl(d, ifr, cmd);
L
Linus Torvalds 已提交
153 154 155
}

/*
156
 *	Passed network frames, fire them downwind.
L
Linus Torvalds 已提交
157
 */
158

159 160
static netdev_tx_t sealevel_queue_xmit(struct sk_buff *skb,
					     struct net_device *d)
L
Linus Torvalds 已提交
161
{
162
	return z8530_queue_xmit(dev_to_chan(d)->chan, skb);
L
Linus Torvalds 已提交
163 164
}

165 166
static int sealevel_attach(struct net_device *dev, unsigned short encoding,
			   unsigned short parity)
L
Linus Torvalds 已提交
167
{
168 169 170
	if (encoding == ENCODING_NRZ && parity == PARITY_CRC16_PR1_CCITT)
		return 0;
	return -EINVAL;
L
Linus Torvalds 已提交
171 172
}

173 174 175 176 177 178 179 180
static const struct net_device_ops sealevel_ops = {
	.ndo_open       = sealevel_open,
	.ndo_stop       = sealevel_close,
	.ndo_change_mtu = hdlc_change_mtu,
	.ndo_start_xmit = hdlc_start_xmit,
	.ndo_do_ioctl   = sealevel_ioctl,
};

181
static int slvl_setup(struct slvl_device *sv, int iobase, int irq)
L
Linus Torvalds 已提交
182
{
183 184 185 186 187 188
	struct net_device *dev = alloc_hdlcdev(sv);
	if (!dev)
		return -1;

	dev_to_hdlc(dev)->attach = sealevel_attach;
	dev_to_hdlc(dev)->xmit = sealevel_queue_xmit;
189
	dev->netdev_ops = &sealevel_ops;
190 191 192 193 194 195 196
	dev->base_addr = iobase;
	dev->irq = irq;

	if (register_hdlc_device(dev)) {
		printk(KERN_ERR "sealevel: unable to register HDLC device\n");
		free_netdev(dev);
		return -1;
L
Linus Torvalds 已提交
197 198
	}

199
	sv->chan->netdevice = dev;
L
Linus Torvalds 已提交
200 201 202 203 204 205 206
	return 0;
}


/*
 *	Allocate and setup Sealevel board.
 */
207 208

static __init struct slvl_board *slvl_init(int iobase, int irq,
L
Linus Torvalds 已提交
209 210 211 212
					   int txdma, int rxdma, int slow)
{
	struct z8530_dev *dev;
	struct slvl_board *b;
213

L
Linus Torvalds 已提交
214 215 216 217
	/*
	 *	Get the needed I/O space
	 */

218 219 220
	if (!request_region(iobase, 8, "Sealevel 4021")) {
		printk(KERN_WARNING "sealevel: I/O 0x%X already in use.\n",
		       iobase);
L
Linus Torvalds 已提交
221 222 223
		return NULL;
	}

224 225 226
	b = kzalloc(sizeof(struct slvl_board), GFP_KERNEL);
	if (!b)
		goto err_kzalloc;
L
Linus Torvalds 已提交
227

228 229
	b->dev[0].chan = &b->board.chanA;
	b->dev[0].channel = 0;
L
Linus Torvalds 已提交
230

231 232
	b->dev[1].chan = &b->board.chanB;
	b->dev[1].channel = 1;
L
Linus Torvalds 已提交
233 234

	dev = &b->board;
235

L
Linus Torvalds 已提交
236 237 238
	/*
	 *	Stuff in the I/O addressing
	 */
239

L
Linus Torvalds 已提交
240 241 242
	dev->active = 0;

	b->iobase = iobase;
243

L
Linus Torvalds 已提交
244 245 246
	/*
	 *	Select 8530 delays for the old board
	 */
247 248

	if (slow)
L
Linus Torvalds 已提交
249
		iobase |= Z8530_PORT_SLEEP;
250 251 252 253 254 255 256 257 258

	dev->chanA.ctrlio = iobase + 1;
	dev->chanA.dataio = iobase;
	dev->chanB.ctrlio = iobase + 3;
	dev->chanB.dataio = iobase + 2;

	dev->chanA.irqs = &z8530_nop;
	dev->chanB.irqs = &z8530_nop;

L
Linus Torvalds 已提交
259 260 261
	/*
	 *	Assert DTR enable DMA
	 */
262 263 264

	outb(3 | (1 << 7), b->iobase + 4);

L
Linus Torvalds 已提交
265 266 267

	/* We want a fast IRQ for this device. Actually we'd like an even faster
	   IRQ ;) - This is one driver RtLinux is made for */
268 269 270

	if (request_irq(irq, &z8530_interrupt, IRQF_DISABLED,
			"SeaLevel", dev) < 0) {
L
Linus Torvalds 已提交
271
		printk(KERN_WARNING "sealevel: IRQ %d already in use.\n", irq);
272
		goto err_request_irq;
L
Linus Torvalds 已提交
273
	}
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288

	dev->irq = irq;
	dev->chanA.private = &b->dev[0];
	dev->chanB.private = &b->dev[1];
	dev->chanA.dev = dev;
	dev->chanB.dev = dev;

	dev->chanA.txdma = 3;
	dev->chanA.rxdma = 1;
	if (request_dma(dev->chanA.txdma, "SeaLevel (TX)"))
		goto err_dma_tx;

	if (request_dma(dev->chanA.rxdma, "SeaLevel (RX)"))
		goto err_dma_rx;

L
Linus Torvalds 已提交
289
	disable_irq(irq);
290

L
Linus Torvalds 已提交
291 292 293
	/*
	 *	Begin normal initialise
	 */
294 295

	if (z8530_init(dev) != 0) {
L
Linus Torvalds 已提交
296 297
		printk(KERN_ERR "Z8530 series device not found.\n");
		enable_irq(irq);
298
		goto free_hw;
L
Linus Torvalds 已提交
299
	}
300
	if (dev->type == Z85C30) {
L
Linus Torvalds 已提交
301 302
		z8530_channel_load(&dev->chanA, z8530_hdlc_kilostream);
		z8530_channel_load(&dev->chanB, z8530_hdlc_kilostream);
303
	} else {
L
Linus Torvalds 已提交
304 305 306 307 308 309 310
		z8530_channel_load(&dev->chanA, z8530_hdlc_kilostream_85230);
		z8530_channel_load(&dev->chanB, z8530_hdlc_kilostream_85230);
	}

	/*
	 *	Now we can take the IRQ
	 */
311

L
Linus Torvalds 已提交
312 313
	enable_irq(irq);

314 315 316 317
	if (slvl_setup(&b->dev[0], iobase, irq))
		goto free_hw;
	if (slvl_setup(&b->dev[1], iobase, irq))
		goto free_netdev0;
L
Linus Torvalds 已提交
318 319

	z8530_describe(dev, "I/O", iobase);
320
	dev->active = 1;
L
Linus Torvalds 已提交
321 322
	return b;

323 324 325 326
free_netdev0:
	unregister_hdlc_device(b->dev[0].chan->netdevice);
	free_netdev(b->dev[0].chan->netdevice);
free_hw:
L
Linus Torvalds 已提交
327
	free_dma(dev->chanA.rxdma);
328
err_dma_rx:
L
Linus Torvalds 已提交
329
	free_dma(dev->chanA.txdma);
330
err_dma_tx:
L
Linus Torvalds 已提交
331
	free_irq(irq, dev);
332
err_request_irq:
L
Linus Torvalds 已提交
333
	kfree(b);
334 335
err_kzalloc:
	release_region(iobase, 8);
L
Linus Torvalds 已提交
336 337 338 339 340 341 342 343
	return NULL;
}

static void __exit slvl_shutdown(struct slvl_board *b)
{
	int u;

	z8530_shutdown(&b->board);
344 345

	for (u = 0; u < 2; u++)
L
Linus Torvalds 已提交
346
	{
347 348
		struct net_device *d = b->dev[u].chan->netdevice;
		unregister_hdlc_device(d);
L
Linus Torvalds 已提交
349 350
		free_netdev(d);
	}
351

L
Linus Torvalds 已提交
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
	free_irq(b->board.irq, &b->board);
	free_dma(b->board.chanA.rxdma);
	free_dma(b->board.chanA.txdma);
	/* DMA off on the card, drop DTR */
	outb(0, b->iobase);
	release_region(b->iobase, 8);
	kfree(b);
}


static int io=0x238;
static int txdma=1;
static int rxdma=3;
static int irq=5;
static int slow=0;

module_param(io, int, 0);
MODULE_PARM_DESC(io, "The I/O base of the Sealevel card");
module_param(txdma, int, 0);
MODULE_PARM_DESC(txdma, "Transmit DMA channel");
module_param(rxdma, int, 0);
MODULE_PARM_DESC(rxdma, "Receive DMA channel");
module_param(irq, int, 0);
MODULE_PARM_DESC(irq, "The interrupt line setting for the SeaLevel card");
module_param(slow, bool, 0);
MODULE_PARM_DESC(slow, "Set this for an older Sealevel card such as the 4012");

MODULE_AUTHOR("Alan Cox");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Modular driver for the SeaLevel 4021");

static struct slvl_board *slvl_unit;

static int __init slvl_init_module(void)
{
	slvl_unit = slvl_init(io, irq, txdma, rxdma, slow);

	return slvl_unit ? 0 : -ENODEV;
}

static void __exit slvl_cleanup_module(void)
{
	if(slvl_unit)
		slvl_shutdown(slvl_unit);
}

module_init(slvl_init_module);
module_exit(slvl_cleanup_module);