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); | |
98e399f8 | 62 | skb_reset_mac_header(skb); |
1da177e4 LT |
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 | ||
dd00cc48 | 234 | sv = kzalloc(sizeof(struct sv11_device), GFP_KERNEL); |
1da177e4 LT |
235 | if(!sv) |
236 | goto fail3; | |
237 | ||
1da177e4 LT |
238 | sv->if_ptr=&sv->netdev; |
239 | ||
240 | sv->netdev.dev = alloc_netdev(0, "hdlc%d", sv11_setup); | |
241 | if(!sv->netdev.dev) | |
242 | goto fail2; | |
243 | ||
1da177e4 LT |
244 | dev=&sv->sync; |
245 | ||
246 | /* | |
247 | * Stuff in the I/O addressing | |
248 | */ | |
249 | ||
250 | dev->active = 0; | |
251 | ||
252 | dev->chanA.ctrlio=iobase+1; | |
253 | dev->chanA.dataio=iobase+3; | |
254 | dev->chanB.ctrlio=-1; | |
255 | dev->chanB.dataio=-1; | |
256 | dev->chanA.irqs=&z8530_nop; | |
257 | dev->chanB.irqs=&z8530_nop; | |
258 | ||
259 | outb(0, iobase+4); /* DMA off */ | |
260 | ||
261 | /* We want a fast IRQ for this device. Actually we'd like an even faster | |
262 | IRQ ;) - This is one driver RtLinux is made for */ | |
263 | ||
1fb9df5d | 264 | if(request_irq(irq, &z8530_interrupt, IRQF_DISABLED, "Hostess SV11", dev)<0) |
1da177e4 LT |
265 | { |
266 | printk(KERN_WARNING "hostess: IRQ %d already in use.\n", irq); | |
267 | goto fail1; | |
268 | } | |
269 | ||
270 | dev->irq=irq; | |
271 | dev->chanA.private=sv; | |
272 | dev->chanA.netdevice=sv->netdev.dev; | |
273 | dev->chanA.dev=dev; | |
274 | dev->chanB.dev=dev; | |
275 | ||
276 | if(dma) | |
277 | { | |
278 | /* | |
279 | * You can have DMA off or 1 and 3 thats the lot | |
280 | * on the Comtrol. | |
281 | */ | |
282 | dev->chanA.txdma=3; | |
283 | dev->chanA.rxdma=1; | |
284 | outb(0x03|0x08, iobase+4); /* DMA on */ | |
285 | if(request_dma(dev->chanA.txdma, "Hostess SV/11 (TX)")!=0) | |
286 | goto fail; | |
287 | ||
288 | if(dma==1) | |
289 | { | |
290 | if(request_dma(dev->chanA.rxdma, "Hostess SV/11 (RX)")!=0) | |
291 | goto dmafail; | |
292 | } | |
293 | } | |
294 | ||
295 | /* Kill our private IRQ line the hostess can end up chattering | |
296 | until the configuration is set */ | |
297 | disable_irq(irq); | |
298 | ||
299 | /* | |
300 | * Begin normal initialise | |
301 | */ | |
302 | ||
303 | if(z8530_init(dev)!=0) | |
304 | { | |
305 | printk(KERN_ERR "Z8530 series device not found.\n"); | |
306 | enable_irq(irq); | |
307 | goto dmafail2; | |
308 | } | |
309 | z8530_channel_load(&dev->chanB, z8530_dead_port); | |
310 | if(dev->type==Z85C30) | |
311 | z8530_channel_load(&dev->chanA, z8530_hdlc_kilostream); | |
312 | else | |
313 | z8530_channel_load(&dev->chanA, z8530_hdlc_kilostream_85230); | |
314 | ||
315 | enable_irq(irq); | |
316 | ||
317 | ||
318 | /* | |
319 | * Now we can take the IRQ | |
320 | */ | |
321 | if(dev_alloc_name(dev->chanA.netdevice,"hdlc%d")>=0) | |
322 | { | |
323 | struct net_device *d=dev->chanA.netdevice; | |
324 | ||
325 | /* | |
326 | * Initialise the PPP components | |
327 | */ | |
328 | sppp_attach(&sv->netdev); | |
329 | ||
330 | /* | |
331 | * Local fields | |
332 | */ | |
333 | ||
334 | d->base_addr = iobase; | |
335 | d->irq = irq; | |
336 | d->priv = sv; | |
337 | ||
338 | if(register_netdev(d)) | |
339 | { | |
340 | printk(KERN_ERR "%s: unable to register device.\n", | |
341 | d->name); | |
342 | sppp_detach(d); | |
343 | goto dmafail2; | |
344 | } | |
345 | ||
346 | z8530_describe(dev, "I/O", iobase); | |
347 | dev->active=1; | |
348 | return sv; | |
349 | } | |
350 | dmafail2: | |
351 | if(dma==1) | |
352 | free_dma(dev->chanA.rxdma); | |
353 | dmafail: | |
354 | if(dma) | |
355 | free_dma(dev->chanA.txdma); | |
356 | fail: | |
357 | free_irq(irq, dev); | |
358 | fail1: | |
359 | free_netdev(sv->netdev.dev); | |
360 | fail2: | |
361 | kfree(sv); | |
362 | fail3: | |
363 | release_region(iobase,8); | |
364 | return NULL; | |
365 | } | |
366 | ||
367 | static void sv11_shutdown(struct sv11_device *dev) | |
368 | { | |
369 | sppp_detach(dev->netdev.dev); | |
370 | unregister_netdev(dev->netdev.dev); | |
371 | z8530_shutdown(&dev->sync); | |
372 | free_irq(dev->sync.irq, dev); | |
373 | if(dma) | |
374 | { | |
375 | if(dma==1) | |
376 | free_dma(dev->sync.chanA.rxdma); | |
377 | free_dma(dev->sync.chanA.txdma); | |
378 | } | |
379 | release_region(dev->sync.chanA.ctrlio-1, 8); | |
380 | free_netdev(dev->netdev.dev); | |
381 | kfree(dev); | |
382 | } | |
383 | ||
384 | #ifdef MODULE | |
385 | ||
386 | static int io=0x200; | |
387 | static int irq=9; | |
388 | ||
389 | module_param(io, int, 0); | |
390 | MODULE_PARM_DESC(io, "The I/O base of the Comtrol Hostess SV11 card"); | |
391 | module_param(dma, int, 0); | |
392 | MODULE_PARM_DESC(dma, "Set this to 1 to use DMA1/DMA3 for TX/RX"); | |
393 | module_param(irq, int, 0); | |
394 | MODULE_PARM_DESC(irq, "The interrupt line setting for the Comtrol Hostess SV11 card"); | |
395 | ||
396 | MODULE_AUTHOR("Alan Cox"); | |
397 | MODULE_LICENSE("GPL"); | |
398 | MODULE_DESCRIPTION("Modular driver for the Comtrol Hostess SV11"); | |
399 | ||
400 | static struct sv11_device *sv11_unit; | |
401 | ||
402 | int init_module(void) | |
403 | { | |
404 | printk(KERN_INFO "SV-11 Z85230 Synchronous Driver v 0.03.\n"); | |
405 | printk(KERN_INFO "(c) Copyright 2001, Red Hat Inc.\n"); | |
406 | if((sv11_unit=sv11_init(io,irq))==NULL) | |
407 | return -ENODEV; | |
408 | return 0; | |
409 | } | |
410 | ||
411 | void cleanup_module(void) | |
412 | { | |
413 | if(sv11_unit) | |
414 | sv11_shutdown(sv11_unit); | |
415 | } | |
416 | ||
417 | #endif | |
418 |