]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/patm/if_patm.c
MFC r338669:
[FreeBSD/FreeBSD.git] / sys / dev / patm / if_patm.c
1 /*-
2  * Copyright (c) 2003
3  *      Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4  *      All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * Author: Hartmut Brandt <harti@freebsd.org>
28  *
29  * Driver for IDT77252 based cards like ProSum's.
30  */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include "opt_inet.h"
36 #include "opt_natm.h"
37
38 #include <sys/types.h>
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/malloc.h>
42 #include <sys/kernel.h>
43 #include <sys/bus.h>
44 #include <sys/errno.h>
45 #include <sys/conf.h>
46 #include <sys/module.h>
47 #include <sys/lock.h>
48 #include <sys/mutex.h>
49 #include <sys/sysctl.h>
50 #include <sys/queue.h>
51 #include <sys/condvar.h>
52 #include <sys/endian.h>
53 #include <vm/uma.h>
54
55 #include <sys/sockio.h>
56 #include <sys/mbuf.h>
57 #include <sys/socket.h>
58
59 #include <net/if.h>
60 #include <net/if_var.h>
61 #include <net/if_media.h>
62 #include <net/if_atm.h>
63 #include <net/route.h>
64 #include <netinet/in.h>
65 #include <netinet/if_atm.h>
66
67 #include <machine/bus.h>
68 #include <machine/resource.h>
69 #include <sys/bus.h>
70 #include <sys/rman.h>
71 #include <sys/mbpool.h>
72
73 #include <dev/utopia/utopia.h>
74 #include <dev/patm/idt77252reg.h>
75 #include <dev/patm/if_patmvar.h>
76
77 static void patm_tst_init(struct patm_softc *sc);
78 static void patm_scd_init(struct patm_softc *sc);
79
80 /*
81  * Start the card. This assumes the mutex to be held
82  */
83 void
84 patm_initialize(struct patm_softc *sc)
85 {
86         uint32_t cfg;
87         u_int i;
88
89         patm_debug(sc, ATTACH, "configuring...");
90
91         /* clear SRAM */
92         for (i = 0; i < sc->mmap->sram * 1024; i += 4)
93                 patm_sram_write4(sc, i, 0, 0, 0, 0);
94         patm_scd_init(sc);
95
96         /* configuration register. Setting NOIDLE makes the timing wrong! */
97         cfg = IDT_CFG_TXFIFO9 | IDT_CFG_RXQ512 | PATM_CFG_VPI |
98             /* IDT_CFG_NOIDLE | */ sc->mmap->rxtab;
99         if (!(sc->flags & PATM_UNASS))
100                 cfg |= IDT_CFG_IDLECLP;
101         patm_nor_write(sc, IDT_NOR_CFG, cfg);
102
103         /* clean all the status queues and the Raw handle */
104         memset(sc->tsq, 0, sc->sq_size);
105
106         /* initialize RSQ */
107         patm_debug(sc, ATTACH, "RSQ %llx", (unsigned long long)sc->rsq_phy);
108         patm_nor_write(sc, IDT_NOR_RSQB, sc->rsq_phy);
109         patm_nor_write(sc, IDT_NOR_RSQT, sc->rsq_phy);
110         patm_nor_write(sc, IDT_NOR_RSQH, 0);
111         sc->rsq_last = PATM_RSQ_SIZE - 1;
112
113         /* initialize TSTB */
114         patm_nor_write(sc, IDT_NOR_TSTB, sc->mmap->tst1base << 2);
115         patm_tst_init(sc);
116
117         /* initialize TSQ */
118         for (i = 0; i < IDT_TSQ_SIZE; i++)
119                 sc->tsq[i].stamp = htole32(IDT_TSQE_EMPTY);
120         patm_nor_write(sc, IDT_NOR_TSQB, sc->tsq_phy);
121         patm_nor_write(sc, IDT_NOR_TSQH, 0);
122         patm_nor_write(sc, IDT_NOR_TSQT, 0);
123         sc->tsq_next = sc->tsq;
124
125         /* GP */
126 #if BYTE_ORDER == BIG_ENDIAN && 0
127         patm_nor_write(sc, IDT_NOR_GP, IDT_GP_BIGE);
128 #else
129         patm_nor_write(sc, IDT_NOR_GP, 0);
130 #endif
131
132         /* VPM */
133         patm_nor_write(sc, IDT_NOR_VPM, 0);
134
135         /* RxFIFO */
136         patm_nor_write(sc, IDT_NOR_RXFD,
137             IDT_RXFD(sc->mmap->rxfifo_addr, sc->mmap->rxfifo_code));
138         patm_nor_write(sc, IDT_NOR_RXFT, 0);
139         patm_nor_write(sc, IDT_NOR_RXFH, 0);
140
141         /* RAWHND */
142         patm_debug(sc, ATTACH, "RWH %llx",
143             (unsigned long long)sc->rawhnd_phy);
144         patm_nor_write(sc, IDT_NOR_RAWHND, sc->rawhnd_phy);
145
146         /* ABRSTD */
147         patm_nor_write(sc, IDT_NOR_ABRSTD,
148             IDT_ABRSTD(sc->mmap->abrstd_addr, sc->mmap->abrstd_code));
149         for (i = 0; i < sc->mmap->abrstd_size; i++)
150                 patm_sram_write(sc, sc->mmap->abrstd_addr + i, 0);
151         patm_nor_write(sc, IDT_NOR_ABRRQ, 0);
152         patm_nor_write(sc, IDT_NOR_VBRRQ, 0);
153
154         /* rate tables */
155         if (sc->flags & PATM_25M) {
156                 for (i = 0; i < patm_rtables_size; i++)
157                         patm_sram_write(sc, sc->mmap->rtables + i,
158                             patm_rtables25[i]);
159         } else {
160                 for (i = 0; i < patm_rtables_size; i++)
161                         patm_sram_write(sc, sc->mmap->rtables + i,
162                             patm_rtables155[i]);
163         }
164         patm_nor_write(sc, IDT_NOR_RTBL, sc->mmap->rtables << 2);
165
166         /* Maximum deficit */
167         patm_nor_write(sc, IDT_NOR_MXDFCT, 32 | IDT_MDFCT_LCI | IDT_MDFCT_LNI);
168
169         /* Free buffer queues */
170         patm_nor_write(sc, IDT_NOR_FBQP0, 0);
171         patm_nor_write(sc, IDT_NOR_FBQP1, 0);
172         patm_nor_write(sc, IDT_NOR_FBQP2, 0);
173         patm_nor_write(sc, IDT_NOR_FBQP3, 0);
174
175         patm_nor_write(sc, IDT_NOR_FBQWP0, 0);
176         patm_nor_write(sc, IDT_NOR_FBQWP1, 0);
177         patm_nor_write(sc, IDT_NOR_FBQWP2, 0);
178         patm_nor_write(sc, IDT_NOR_FBQWP3, 0);
179
180         patm_nor_write(sc, IDT_NOR_FBQS0,
181             (SMBUF_THRESHOLD << 28) |
182             (SMBUF_NI_THRESH << 24) |
183             (SMBUF_CI_THRESH << 20) |
184             SMBUF_CELLS);
185         patm_nor_write(sc, IDT_NOR_FBQS1,
186             (LMBUF_THRESHOLD << 28) |
187             (LMBUF_NI_THRESH << 24) |
188             (LMBUF_CI_THRESH << 20) |
189             LMBUF_CELLS);
190         patm_nor_write(sc, IDT_NOR_FBQS2,
191             (VMBUF_THRESHOLD << 28) | VMBUF_CELLS);
192         patm_nor_write(sc, IDT_NOR_FBQS3, 0);
193
194         /* make SCD0 for UBR0 */
195         if ((sc->scd0 = patm_scd_alloc(sc)) == NULL) {
196                 patm_printf(sc, "cannot create UBR0 SCD\n");
197                 patm_reset(sc);
198                 return;
199         }
200         sc->scd0->q.ifq_maxlen = PATM_DLFT_MAXQ;
201
202         patm_scd_setup(sc, sc->scd0);
203         patm_tct_setup(sc, sc->scd0, NULL);
204
205         patm_debug(sc, ATTACH, "go...");
206
207         sc->utopia.flags &= ~UTP_FL_POLL_CARRIER;
208         sc->ifp->if_drv_flags |= IFF_DRV_RUNNING;
209
210         /* enable interrupts, Tx and Rx paths */
211         cfg |= IDT_CFG_RXPTH | IDT_CFG_RXIIMM | IDT_CFG_RAWIE | IDT_CFG_RQFIE |
212             IDT_CFG_TIMOIE | IDT_CFG_FBIE | IDT_CFG_TXENB | IDT_CFG_TXINT |
213             IDT_CFG_TXUIE | IDT_CFG_TXSFI | IDT_CFG_PHYIE;
214         patm_nor_write(sc, IDT_NOR_CFG, cfg);
215
216         for (i = 0; i < sc->mmap->max_conn; i++)
217                 if (sc->vccs[i] != NULL)
218                         patm_load_vc(sc, sc->vccs[i], 1);
219
220         ATMEV_SEND_IFSTATE_CHANGED(IFP2IFATM(sc->ifp),
221             sc->utopia.carrier == UTP_CARR_OK);
222 }
223
224 /*
225  * External callable start function
226  */
227 void
228 patm_init(void *p)
229 {
230         struct patm_softc *sc = p;
231
232         mtx_lock(&sc->mtx);
233         patm_stop(sc);
234         patm_initialize(sc);
235         mtx_unlock(&sc->mtx);
236 }
237
238 /*
239  * Stop the interface
240  */
241 void
242 patm_stop(struct patm_softc *sc)
243 {
244         u_int i;
245         struct mbuf *m;
246         struct patm_txmap *map;
247         struct patm_scd *scd;
248
249         sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
250         sc->utopia.flags |= UTP_FL_POLL_CARRIER;
251
252         patm_reset(sc);
253
254         mtx_lock(&sc->tst_lock);
255         i = sc->tst_state;
256         sc->tst_state = 0;
257         callout_stop(&sc->tst_callout);
258         mtx_unlock(&sc->tst_lock);
259
260         if (i != 0) {
261                 /* this means we are just entering or leaving the timeout.
262                  * wait a little bit. Doing this correctly would be more
263                  * involved */
264                 DELAY(1000);
265         }
266
267         /*
268          * Give any waiters on closing a VCC a chance. They will stop
269          * to wait if they see that IFF_DRV_RUNNING disappeared.
270          */
271         cv_broadcast(&sc->vcc_cv);
272
273         /* free large buffers */
274         patm_debug(sc, ATTACH, "freeing large buffers...");
275         for (i = 0; i < sc->lbuf_max; i++)
276                 if (sc->lbufs[i].m != NULL)
277                         patm_lbuf_free(sc, &sc->lbufs[i]);
278
279         /* free small buffers that are on the card */
280         patm_debug(sc, ATTACH, "freeing small buffers...");
281         mbp_card_free(sc->sbuf_pool);
282
283         /* free aal0 buffers that are on the card */
284         patm_debug(sc, ATTACH, "freeing aal0 buffers...");
285         mbp_card_free(sc->vbuf_pool);
286
287         /* freeing partial receive chains and reset vcc state */
288         for (i = 0; i < sc->mmap->max_conn; i++) {
289                 if (sc->vccs[i] != NULL) {
290                         if (sc->vccs[i]->chain != NULL) {
291                                 m_freem(sc->vccs[i]->chain);
292                                 sc->vccs[i]->chain = NULL;
293                                 sc->vccs[i]->last = NULL;
294                         }
295
296                         if (sc->vccs[i]->vflags & (PATM_VCC_RX_CLOSING |
297                             PATM_VCC_TX_CLOSING)) {
298                                 uma_zfree(sc->vcc_zone, sc->vccs[i]);
299                                 sc->vccs[i] = NULL;
300                         } else {
301                                 /* keep */
302                                 sc->vccs[i]->vflags &= ~PATM_VCC_OPEN;
303                                 sc->vccs[i]->cps = 0;
304                                 sc->vccs[i]->scd = NULL;
305                         }
306                 }
307         }
308
309         /* stop all active SCDs */
310         while ((scd = LIST_FIRST(&sc->scd_list)) != NULL) {
311                 /* free queue packets */
312                 for (;;) {
313                         _IF_DEQUEUE(&scd->q, m);
314                         if (m == NULL)
315                                 break;
316                         m_freem(m);
317                 }
318
319                 /* free transmitting packets */
320                 for (i = 0; i < IDT_TSQE_TAG_SPACE; i++) {
321                         if ((m = scd->on_card[i]) != NULL) {
322                                 scd->on_card[i] = 0;
323                                 map = m->m_pkthdr.PH_loc.ptr;
324
325                                 bus_dmamap_unload(sc->tx_tag, map->map);
326                                 SLIST_INSERT_HEAD(&sc->tx_maps_free, map, link);
327                                 m_freem(m);
328                         }
329                 }
330                 patm_scd_free(sc, scd);
331         }
332         sc->scd0 = NULL;
333
334         sc->flags &= ~PATM_CLR;
335
336         /* reset raw cell queue */
337         sc->rawh = NULL;
338
339         ATMEV_SEND_IFSTATE_CHANGED(IFP2IFATM(sc->ifp),
340             sc->utopia.carrier == UTP_CARR_OK);
341 }
342
343 /*
344  * Stop the card and reset it
345  */
346 void
347 patm_reset(struct patm_softc *sc)
348 {
349
350         patm_debug(sc, ATTACH, "resetting...");
351
352         patm_nor_write(sc, IDT_NOR_CFG, IDT_CFG_SWRST);
353         DELAY(200);
354         patm_nor_write(sc, IDT_NOR_CFG, 0);
355         DELAY(200);
356
357         patm_nor_write(sc, IDT_NOR_RSQH, 0);
358         patm_nor_write(sc, IDT_NOR_TSQH, 0);
359
360         patm_nor_write(sc, IDT_NOR_GP, IDT_GP_PHY_RST);
361         DELAY(50);
362         patm_nor_write(sc, IDT_NOR_GP, IDT_GP_EEDO | IDT_GP_EECS);
363         DELAY(50);
364 }
365
366 /*
367  * Initialize the soft TST to contain only ABR scheduling and
368  * write it to SRAM
369  */
370 static void
371 patm_tst_init(struct patm_softc *sc)
372 {
373         u_int i;
374         u_int base, idle;
375
376         base = sc->mmap->tst1base;
377         idle = sc->mmap->tst1base + sc->mmap->tst_size;
378
379         /* soft */
380         for (i = 0; i < sc->mmap->tst_size - 1; i++)
381                 sc->tst_soft[i] = IDT_TST_VBR;
382
383         sc->tst_state = 0;
384         sc->tst_jump[0] = base + sc->mmap->tst_size - 1;
385         sc->tst_jump[1] = idle + sc->mmap->tst_size - 1;
386         sc->tst_base[0] = base;
387         sc->tst_base[1] = idle;
388
389         /* TST1 */
390         for (i = 0; i < sc->mmap->tst_size - 1; i++)
391                 patm_sram_write(sc, base + i, IDT_TST_VBR);
392         patm_sram_write(sc, sc->tst_jump[0], IDT_TST_BR | (base << 2));
393
394         /* TST2 */
395         for (i = 0; i < sc->mmap->tst_size - 1; i++)
396                 patm_sram_write(sc, idle + i, IDT_TST_VBR);
397         patm_sram_write(sc, sc->tst_jump[1], IDT_TST_BR | (idle << 2));
398
399         sc->tst_free = sc->mmap->tst_size - 1;
400         sc->tst_reserve = sc->tst_free * PATM_TST_RESERVE / 100;
401         sc->bwrem = IFP2IFATM(sc->ifp)->mib.pcr;
402 }
403
404 /*
405  * Initialize the SCDs. This is done by building a list of all free
406  * SCDs in SRAM. The first word of each potential SCD is used as a
407  * link to the next free SCD. The list is rooted in softc.
408  */
409 static void
410 patm_scd_init(struct patm_softc *sc)
411 {
412         u_int s;        /* SRAM address of current SCD */
413
414         sc->scd_free = 0;
415         for (s = sc->mmap->scd_base; s + 12 <= sc->mmap->tst1base; s += 12) {
416                 patm_sram_write(sc, s, sc->scd_free);
417                 sc->scd_free = s;
418         }
419 }
420
421 /*
422  * allocate an SCQ
423  */
424 struct patm_scd *
425 patm_scd_alloc(struct patm_softc *sc)
426 {
427         u_int sram, next;       /* SRAM address of this and next SCD */
428         int error;
429         void *p;
430         struct patm_scd *scd;
431         bus_dmamap_t map;
432         bus_addr_t phy;
433
434         /* get an SCD from the free list */
435         if ((sram = sc->scd_free) == 0)
436                 return (NULL);
437         next = patm_sram_read(sc, sram);
438
439         /* allocate memory for the queue and our host stuff */
440         error = bus_dmamem_alloc(sc->scd_tag, &p, BUS_DMA_NOWAIT, &map);
441         if (error != 0)
442                 return (NULL);
443         phy = 0x3ff;
444         error = bus_dmamap_load(sc->scd_tag, map, p, sizeof(scd->scq),
445             patm_load_callback, &phy, BUS_DMA_NOWAIT);
446         if (error != 0) {
447                 bus_dmamem_free(sc->scd_tag, p, map);
448                 return (NULL);
449         }
450         KASSERT((phy & 0x1ff) == 0, ("SCD not aligned %lx", (u_long)phy));
451
452         scd = p;
453         bzero(scd, sizeof(*scd));
454
455         scd->sram = sram;
456         scd->phy = phy;
457         scd->map = map;
458         scd->space = IDT_SCQ_SIZE;
459         scd->last_tag = IDT_TSQE_TAG_SPACE - 1;
460         scd->q.ifq_maxlen = PATM_TX_IFQLEN;
461
462         /* remove the scd from the free list */
463         sc->scd_free = next;
464         LIST_INSERT_HEAD(&sc->scd_list, scd, link);
465
466         return (scd);
467 }
468
469 /*
470  * Free an SCD
471  */
472 void
473 patm_scd_free(struct patm_softc *sc, struct patm_scd *scd)
474 {
475
476         LIST_REMOVE(scd, link);
477
478         /* clear SCD and insert link word */
479         patm_sram_write4(sc, scd->sram, sc->scd_free, 0, 0, 0);
480         patm_sram_write4(sc, scd->sram, 0, 0, 0, 0);
481         patm_sram_write4(sc, scd->sram, 0, 0, 0, 0);
482
483         /* put on free list */
484         sc->scd_free = scd->sram;
485
486         /* free memory */
487         bus_dmamap_unload(sc->scd_tag, scd->map);
488         bus_dmamem_free(sc->scd_tag, scd, scd->map);
489 }
490
491 /*
492  * DMA loading helper function. This function handles the loading of
493  * all one segment DMA maps. The argument is a pointer to a bus_addr_t
494  * which must contain the desired alignment of the address as a bitmap.
495  */
496 void
497 patm_load_callback(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
498 {
499         bus_addr_t *phy = arg;
500
501         if (error)
502                 return;
503
504         KASSERT(nsegs == 1,
505             ("too many segments for DMA: %d", nsegs));
506         KASSERT(segs[0].ds_addr <= 0xffffffffUL,
507             ("phys addr too large %lx", (u_long)segs[0].ds_addr));
508         KASSERT((segs[0].ds_addr & *phy) == 0,
509             ("bad alignment %lx:%lx",  (u_long)segs[0].ds_addr, (u_long)*phy));
510
511         *phy = segs[0].ds_addr;
512 }