]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Comtrol SV11 card driver | |
3 | * | |
4 | * This is a slightly odd Z85230 synchronous driver. All you need to | |
5 | * know basically is | |
6 | * | |
7 | * Its a genuine Z85230 | |
8 | * | |
9 | * It supports DMA using two DMA channels in SYNC mode. The driver doesn't | |
10 | * use these facilities | |
11 | * | |
12 | * The control port is at io+1, the data at io+3 and turning off the DMA | |
13 | * is done by writing 0 to io+4 | |
14 | * | |
15 | * The hardware does the bus handling to avoid the need for delays between | |
16 | * touching control registers. | |
17 | * | |
18 | * Port B isnt wired (why - beats me) | |
19 | */ | |
20 | ||
21 | #include <linux/module.h> | |
22 | #include <linux/kernel.h> | |
23 | #include <linux/mm.h> | |
24 | #include <linux/net.h> | |
25 | #include <linux/skbuff.h> | |
26 | #include <linux/netdevice.h> | |
27 | #include <linux/if_arp.h> | |
28 | #include <linux/delay.h> | |
29 | #include <linux/ioport.h> | |
30 | #include <net/arp.h> | |
31 | ||
82729971 | 32 | #include <asm/irq.h> |
1da177e4 LT |
33 | #include <asm/io.h> |
34 | #include <asm/dma.h> | |
35 | #include <asm/byteorder.h> | |
36 | #include <net/syncppp.h> | |
37 | #include "z85230.h" | |
38 | ||
39 | static int dma; | |
40 | ||
41 | struct sv11_device | |
42 | { | |
43 | void *if_ptr; /* General purpose pointer (used by SPPP) */ | |
44 | struct z8530_dev sync; | |
45 | struct ppp_device netdev; | |
46 | }; | |
47 | ||
48 | /* | |
49 | * Network driver support routines | |
50 | */ | |
51 | ||
52 | /* | |
53 | * Frame receive. Simple for our card as we do sync ppp and there | |
54 | * is no funny garbage involved | |
55 | */ | |
56 | ||
57 | static void hostess_input(struct z8530_channel *c, struct sk_buff *skb) | |
58 | { | |
59 | /* Drop the CRC - it's not a good idea to try and negotiate it ;) */ | |
60 | skb_trim(skb, skb->len-2); | |
61 | skb->protocol=__constant_htons(ETH_P_WAN_PPP); | |
62 | skb->mac.raw=skb->data; | |
63 | skb->dev=c->netdevice; | |
64 | /* | |
65 | * Send it to the PPP layer. We don't have time to process | |
66 | * it right now. | |
67 | */ | |
68 | netif_rx(skb); | |
69 | c->netdevice->last_rx = jiffies; | |
70 | } | |
71 | ||
72 | /* | |
73 | * We've been placed in the UP state | |
74 | */ | |
75 | ||
76 | static int hostess_open(struct net_device *d) | |
77 | { | |
78 | struct sv11_device *sv11=d->priv; | |
79 | int err = -1; | |
80 | ||
81 | /* | |
82 | * Link layer up | |
83 | */ | |
84 | switch(dma) | |
85 | { | |
86 | case 0: | |
87 | err=z8530_sync_open(d, &sv11->sync.chanA); | |
88 | break; | |
89 | case 1: | |
90 | err=z8530_sync_dma_open(d, &sv11->sync.chanA); | |
91 | break; | |
92 | case 2: | |
93 | err=z8530_sync_txdma_open(d, &sv11->sync.chanA); | |
94 | break; | |
95 | } | |
96 | ||
97 | if(err) | |
98 | return err; | |
99 | /* | |
100 | * Begin PPP | |
101 | */ | |
102 | err=sppp_open(d); | |
103 | if(err) | |
104 | { | |
105 | switch(dma) | |
106 | { | |
107 | case 0: | |
108 | z8530_sync_close(d, &sv11->sync.chanA); | |
109 | break; | |
110 | case 1: | |
111 | z8530_sync_dma_close(d, &sv11->sync.chanA); | |
112 | break; | |
113 | case 2: | |
114 | z8530_sync_txdma_close(d, &sv11->sync.chanA); | |
115 | break; | |
116 | } | |
117 | return err; | |
118 | } | |
119 | sv11->sync.chanA.rx_function=hostess_input; | |
120 | ||
121 | /* | |
122 | * Go go go | |
123 | */ | |
124 | ||
125 | netif_start_queue(d); | |
126 | return 0; | |
127 | } | |
128 | ||
129 | static int hostess_close(struct net_device *d) | |
130 | { | |
131 | struct sv11_device *sv11=d->priv; | |
132 | /* | |
133 | * Discard new frames | |
134 | */ | |
135 | sv11->sync.chanA.rx_function=z8530_null_rx; | |
136 | /* | |
137 | * PPP off | |
138 | */ | |
139 | sppp_close(d); | |
140 | /* | |
141 | * Link layer down | |
142 | */ | |
143 | netif_stop_queue(d); | |
144 | ||
145 | switch(dma) | |
146 | { | |
147 | case 0: | |
148 | z8530_sync_close(d, &sv11->sync.chanA); | |
149 | break; | |
150 | case 1: | |
151 | z8530_sync_dma_close(d, &sv11->sync.chanA); | |
152 | break; | |
153 | case 2: | |
154 | z8530_sync_txdma_close(d, &sv11->sync.chanA); | |
155 | break; | |
156 | } | |
157 | return 0; | |
158 | } | |
159 | ||
160 | static int hostess_ioctl(struct net_device *d, struct ifreq *ifr, int cmd) | |
161 | { | |
162 | /* struct sv11_device *sv11=d->priv; | |
163 | z8530_ioctl(d,&sv11->sync.chanA,ifr,cmd) */ | |
164 | return sppp_do_ioctl(d, ifr,cmd); | |
165 | } | |
166 | ||
167 | static struct net_device_stats *hostess_get_stats(struct net_device *d) | |
168 | { | |
169 | struct sv11_device *sv11=d->priv; | |
170 | if(sv11) | |
171 | return z8530_get_stats(&sv11->sync.chanA); | |
172 | else | |
173 | return NULL; | |
174 | } | |
175 | ||
176 | /* | |
177 | * Passed PPP frames, fire them downwind. | |
178 | */ | |
179 | ||
180 | static int hostess_queue_xmit(struct sk_buff *skb, struct net_device *d) | |
181 | { | |
182 | struct sv11_device *sv11=d->priv; | |
183 | return z8530_queue_xmit(&sv11->sync.chanA, skb); | |
184 | } | |
185 | ||
186 | static int hostess_neigh_setup(struct neighbour *n) | |
187 | { | |
188 | if (n->nud_state == NUD_NONE) { | |
189 | n->ops = &arp_broken_ops; | |
190 | n->output = n->ops->output; | |
191 | } | |
192 | return 0; | |
193 | } | |
194 | ||
195 | static int hostess_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p) | |
196 | { | |
197 | if (p->tbl->family == AF_INET) { | |
198 | p->neigh_setup = hostess_neigh_setup; | |
199 | p->ucast_probes = 0; | |
200 | p->mcast_probes = 0; | |
201 | } | |
202 | return 0; | |
203 | } | |
204 | ||
205 | static void sv11_setup(struct net_device *dev) | |
206 | { | |
207 | dev->open = hostess_open; | |
208 | dev->stop = hostess_close; | |
209 | dev->hard_start_xmit = hostess_queue_xmit; | |
210 | dev->get_stats = hostess_get_stats; | |
211 | dev->do_ioctl = hostess_ioctl; | |
212 | dev->neigh_setup = hostess_neigh_setup_dev; | |
213 | } | |
214 | ||
215 | /* | |
216 | * Description block for a Comtrol Hostess SV11 card | |
217 | */ | |
218 | ||
219 | static struct sv11_device *sv11_init(int iobase, int irq) | |
220 | { | |
221 | struct z8530_dev *dev; | |
222 | struct sv11_device *sv; | |
223 | ||
224 | /* | |
225 | * Get the needed I/O space | |
226 | */ | |
227 | ||
228 | if(!request_region(iobase, 8, "Comtrol SV11")) | |
229 | { | |
230 | printk(KERN_WARNING "hostess: I/O 0x%X already in use.\n", iobase); | |
231 | return NULL; | |
232 | } | |
233 | ||
234 | sv=(struct sv11_device *)kmalloc(sizeof(struct sv11_device), GFP_KERNEL); | |
235 | if(!sv) | |
236 | goto fail3; | |
237 | ||
238 | memset(sv, 0, sizeof(*sv)); | |
239 | sv->if_ptr=&sv->netdev; | |
240 | ||
241 | sv->netdev.dev = alloc_netdev(0, "hdlc%d", sv11_setup); | |
242 | if(!sv->netdev.dev) | |
243 | goto fail2; | |
244 | ||
245 | SET_MODULE_OWNER(sv->netdev.dev); | |
246 | ||
247 | dev=&sv->sync; | |
248 | ||
249 | /* | |
250 | * Stuff in the I/O addressing | |
251 | */ | |
252 | ||
253 | dev->active = 0; | |
254 | ||
255 | dev->chanA.ctrlio=iobase+1; | |
256 | dev->chanA.dataio=iobase+3; | |
257 | dev->chanB.ctrlio=-1; | |
258 | dev->chanB.dataio=-1; | |
259 | dev->chanA.irqs=&z8530_nop; | |
260 | dev->chanB.irqs=&z8530_nop; | |
261 | ||
262 | outb(0, iobase+4); /* DMA off */ | |
263 | ||
264 | /* We want a fast IRQ for this device. Actually we'd like an even faster | |
265 | IRQ ;) - This is one driver RtLinux is made for */ | |
266 | ||
1fb9df5d | 267 | if(request_irq(irq, &z8530_interrupt, IRQF_DISABLED, "Hostess SV11", dev)<0) |
1da177e4 LT |
268 | { |
269 | printk(KERN_WARNING "hostess: IRQ %d already in use.\n", irq); | |
270 | goto fail1; | |
271 | } | |
272 | ||
273 | dev->irq=irq; | |
274 | dev->chanA.private=sv; | |
275 | dev->chanA.netdevice=sv->netdev.dev; | |
276 | dev->chanA.dev=dev; | |
277 | dev->chanB.dev=dev; | |
278 | ||
279 | if(dma) | |
280 | { | |
281 | /* | |
282 | * You can have DMA off or 1 and 3 thats the lot | |
283 | * on the Comtrol. | |
284 | */ | |
285 | dev->chanA.txdma=3; | |
286 | dev->chanA.rxdma=1; | |
287 | outb(0x03|0x08, iobase+4); /* DMA on */ | |
288 | if(request_dma(dev->chanA.txdma, "Hostess SV/11 (TX)")!=0) | |
289 | goto fail; | |
290 | ||
291 | if(dma==1) | |
292 | { | |
293 | if(request_dma(dev->chanA.rxdma, "Hostess SV/11 (RX)")!=0) | |
294 | goto dmafail; | |
295 | } | |
296 | } | |
297 | ||
298 | /* Kill our private IRQ line the hostess can end up chattering | |
299 | until the configuration is set */ | |
300 | disable_irq(irq); | |
301 | ||
302 | /* | |
303 | * Begin normal initialise | |
304 | */ | |
305 | ||
306 | if(z8530_init(dev)!=0) | |
307 | { | |
308 | printk(KERN_ERR "Z8530 series device not found.\n"); | |
309 | enable_irq(irq); | |
310 | goto dmafail2; | |
311 | } | |
312 | z8530_channel_load(&dev->chanB, z8530_dead_port); | |
313 | if(dev->type==Z85C30) | |
314 | z8530_channel_load(&dev->chanA, z8530_hdlc_kilostream); | |
315 | else | |
316 | z8530_channel_load(&dev->chanA, z8530_hdlc_kilostream_85230); | |
317 | ||
318 | enable_irq(irq); | |
319 | ||
320 | ||
321 | /* | |
322 | * Now we can take the IRQ | |
323 | */ | |
324 | if(dev_alloc_name(dev->chanA.netdevice,"hdlc%d")>=0) | |
325 | { | |
326 | struct net_device *d=dev->chanA.netdevice; | |
327 | ||
328 | /* | |
329 | * Initialise the PPP components | |
330 | */ | |
331 | sppp_attach(&sv->netdev); | |
332 | ||
333 | /* | |
334 | * Local fields | |
335 | */ | |
336 | ||
337 | d->base_addr = iobase; | |
338 | d->irq = irq; | |
339 | d->priv = sv; | |
340 | ||
341 | if(register_netdev(d)) | |
342 | { | |
343 | printk(KERN_ERR "%s: unable to register device.\n", | |
344 | d->name); | |
345 | sppp_detach(d); | |
346 | goto dmafail2; | |
347 | } | |
348 | ||
349 | z8530_describe(dev, "I/O", iobase); | |
350 | dev->active=1; | |
351 | return sv; | |
352 | } | |
353 | dmafail2: | |
354 | if(dma==1) | |
355 | free_dma(dev->chanA.rxdma); | |
356 | dmafail: | |
357 | if(dma) | |
358 | free_dma(dev->chanA.txdma); | |
359 | fail: | |
360 | free_irq(irq, dev); | |
361 | fail1: | |
362 | free_netdev(sv->netdev.dev); | |
363 | fail2: | |
364 | kfree(sv); | |
365 | fail3: | |
366 | release_region(iobase,8); | |
367 | return NULL; | |
368 | } | |
369 | ||
370 | static void sv11_shutdown(struct sv11_device *dev) | |
371 | { | |
372 | sppp_detach(dev->netdev.dev); | |
373 | unregister_netdev(dev->netdev.dev); | |
374 | z8530_shutdown(&dev->sync); | |
375 | free_irq(dev->sync.irq, dev); | |
376 | if(dma) | |
377 | { | |
378 | if(dma==1) | |
379 | free_dma(dev->sync.chanA.rxdma); | |
380 | free_dma(dev->sync.chanA.txdma); | |
381 | } | |
382 | release_region(dev->sync.chanA.ctrlio-1, 8); | |
383 | free_netdev(dev->netdev.dev); | |
384 | kfree(dev); | |
385 | } | |
386 | ||
387 | #ifdef MODULE | |
388 | ||
389 | static int io=0x200; | |
390 | static int irq=9; | |
391 | ||
392 | module_param(io, int, 0); | |
393 | MODULE_PARM_DESC(io, "The I/O base of the Comtrol Hostess SV11 card"); | |
394 | module_param(dma, int, 0); | |
395 | MODULE_PARM_DESC(dma, "Set this to 1 to use DMA1/DMA3 for TX/RX"); | |
396 | module_param(irq, int, 0); | |
397 | MODULE_PARM_DESC(irq, "The interrupt line setting for the Comtrol Hostess SV11 card"); | |
398 | ||
399 | MODULE_AUTHOR("Alan Cox"); | |
400 | MODULE_LICENSE("GPL"); | |
401 | MODULE_DESCRIPTION("Modular driver for the Comtrol Hostess SV11"); | |
402 | ||
403 | static struct sv11_device *sv11_unit; | |
404 | ||
405 | int init_module(void) | |
406 | { | |
407 | printk(KERN_INFO "SV-11 Z85230 Synchronous Driver v 0.03.\n"); | |
408 | printk(KERN_INFO "(c) Copyright 2001, Red Hat Inc.\n"); | |
409 | if((sv11_unit=sv11_init(io,irq))==NULL) | |
410 | return -ENODEV; | |
411 | return 0; | |
412 | } | |
413 | ||
414 | void cleanup_module(void) | |
415 | { | |
416 | if(sv11_unit) | |
417 | sv11_shutdown(sv11_unit); | |
418 | } | |
419 | ||
420 | #endif | |
421 |