From owner-freebsd-bugs Thu Aug 10 8: 0:18 2000 Delivered-To: freebsd-bugs@freebsd.org Received: from freefall.freebsd.org (freefall.FreeBSD.ORG [204.216.27.21]) by hub.freebsd.org (Postfix) with ESMTP id 9396737BEAC for ; Thu, 10 Aug 2000 08:00:01 -0700 (PDT) (envelope-from gnats@FreeBSD.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.9.3/8.9.2) id IAA95669; Thu, 10 Aug 2000 08:00:01 -0700 (PDT) (envelope-from gnats@FreeBSD.org) Received: from hanoi.cronyx.ru (hanoi.cronyx.ru [144.206.181.53]) by hub.freebsd.org (Postfix) with ESMTP id B86B637B69B for ; Thu, 10 Aug 2000 07:48:55 -0700 (PDT) (envelope-from root@crox.cronyx.ru) Received: from crox.cronyx.ru by hanoi.cronyx.ru with ESMTP id SAA02178; (8.9.3/vak/2.1) Thu, 10 Aug 2000 18:44:42 +0400 (MSD) Received: by crox.cronyx.ru id SAA00350; (8.9.3/vak/2.1) Thu, 10 Aug 2000 18:40:56 +0400 (MSD) Message-Id: <200008101440.SAA00350@crox.cronyx.ru> Date: Thu, 10 Aug 2000 18:40:56 +0400 (MSD) From: vak@cronyx.ru To: FreeBSD-gnats-submit@freebsd.org X-Send-Pr-Version: 3.2 Subject: kern/20523: [patch] -current, sio driver, support for PCI multiport cards Sender: owner-freebsd-bugs@FreeBSD.ORG Precedence: bulk X-Loop: FreeBSD.org >Number: 20523 >Category: kern >Synopsis: Support for PCI multiport cards for sio driver >Confidential: no >Severity: serious >Priority: medium >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: change-request >Submitter-Id: current-users >Arrival-Date: Thu Aug 10 08:00:01 PDT 2000 >Closed-Date: >Last-Modified: >Originator: Serge Vakulenko >Release: FreeBSD-current i386 >Organization: Cronyx Engineering Ltd. >Environment: FreeBSD-current, Cronyx-Omega-PCI serial multiport adapters. >Description: 1) Split the sioattach() routine. Leave the isa-dependent part in sioattach(), and build the bus-independent attachment function sio_attach_unit(). 2) Change the com_addr macro. Use the array of pointers, not the devclass_get_softc routine, which is inadequate for multiport adapters. 3) Change sio_pci_attach, handle multiport adapters. For a new adapter type, add a string to pci_ids[]. The adapters are parameterized by a number of ports, fifo size, hardware rts/cts support and i/o address step. Pci ports get unit numbers, starting from the last isa unit number. No sio_pci_kludge_unit() is needed. The option COM_MULTIPORT is not needed for pci multiport adapters. 4) Add sio_pci_intr() routine for handling interrupts from PCI multiport adapters. Tested on Cronyx Omega-PCI 8-channel adapter. >How-To-Repeat: >Fix: --- sio-current.c Wed Jul 5 02:17:45 2000 +++ sio.c Thu Aug 10 18:17:55 2000 @@ -288,17 +288,31 @@ u_char obuf2[256]; }; +#define MAXCHAN 8 /* up to 8 channels per multiport */ + +struct multicom { + int nchan; /* channels per adapter */ + struct resource *res; + struct resource *irq; + void *intrhand; + struct com_s com [MAXCHAN]; +}; + #ifdef COM_ESP static int espattach __P((struct com_s *com, Port_t esp_port)); #endif static int sioattach __P((device_t dev, int rid)); static int sio_isa_attach __P((device_t dev)); +static int sio_attach_unit __P((struct com_s *com, int unit, Port_t iobase, + u_int flags, bool_t no_irq, + bus_space_tag_t t, bus_space_handle_t h)); static timeout_t siobusycheck; static timeout_t siodtrwakeup; static void comhardclose __P((struct com_s *com)); static void sioinput __P((struct com_s *com)); static void siointr1 __P((struct com_s *com)); +static void sio_pci_intr __P((void *arg)); static void siointr __P((void *arg)); static int commctl __P((struct com_s *com, int bits, int how)); static int comparam __P((struct tty *tp, struct termios *t)); @@ -321,7 +335,6 @@ #if NPCI > 0 static int sio_pci_attach __P((device_t dev)); -static void sio_pci_kludge_unit __P((device_t dev)); static int sio_pci_probe __P((device_t dev)); #endif /* NPCI > 0 */ @@ -329,8 +342,9 @@ /* table and macro for fast conversion from a unit number to its com struct */ static devclass_t sio_devclass; -#define com_addr(unit) ((struct com_s *) \ - devclass_get_softc(sio_devclass, unit)) +#define SIO_MAXUNITS 64 /* up to 64 ports */ +#define com_addr(unit) (p_com_addr[unit]) +static struct com_s *p_com_addr[SIO_MAXUNITS]; static device_method_t sio_isa_methods[] = { /* Device interface */ @@ -373,9 +387,9 @@ }; static driver_t sio_pci_driver = { - driver_name, + "siopci", sio_pci_methods, - sizeof(struct com_s), + sizeof(struct multicom), }; #endif /* NPCI > 0 */ @@ -419,6 +433,7 @@ static struct callout_handle sio_timeout_handle = CALLOUT_HANDLE_INITIALIZER(&sio_timeout_handle); static int sio_numunits; +static int sio_pci_numunits = NSIO; static struct speedtab comspeedtab[] = { { 0, 0 }, @@ -576,12 +591,17 @@ struct pci_ids { u_int32_t type; const char *desc; - int rid; + int rid; /* pci base address register */ + int nchan; /* channels per adapter */ + int iostep; /* addresses per channel */ + int fifo_size; /* fifo size in bytes */ + int hw_rts_cts; /* hardware rts/cts support */ }; static struct pci_ids pci_ids[] = { { 0x100812b9, "3COM PCI FaxModem", 0x10 }, { 0x048011c1, "ActionTec 56k FAX PCI Modem", 0x14 }, + { 0xc00110b5, "Cronyx Omega-PCI Serial Adapter", 0x18, 8, 8, 64, 1 }, { 0x00000000, NULL, 0 } }; @@ -591,6 +611,12 @@ { u_int32_t type; struct pci_ids *id; + struct multicom *d = device_get_softc(dev); + struct com_s *com; + Port_t iobase; + bus_space_tag_t bst; + bus_space_handle_t bsh, bsh_port; + int rid, offset, s, err = 0; type = pci_get_devid(dev); id = pci_ids; @@ -598,37 +624,75 @@ id++; if (id->desc == NULL) return (ENXIO); - sio_pci_kludge_unit(dev); - return (sioattach(dev, id->rid)); -} -/* - * Don't cut and paste this to other drivers. It is a horrible kludge - * which will fail to work and also be unnecessary in future versions. - */ -static void -sio_pci_kludge_unit(dev) - device_t dev; -{ - devclass_t dc; - int err; - int start; - int unit; + bzero((char*)d, sizeof(*d)); + s = splimp(); + d->nchan = id->nchan > 1 ? id->nchan : 1; + + /* Allocate i/o region. */ + rid = id->rid; + d->res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, + RF_ACTIVE); + if (! d->res) { + printf("sio%d: couldn't map ports/memory\n", sio_pci_numunits); + err = ENXIO; + goto fail; + } - unit = 0; - start = 0; - while (resource_int_value("sio", unit, "port", &start) == 0 && - start > 0) - unit++; - if (device_get_unit(dev) < unit) { - dc = device_get_devclass(dev); - while (devclass_get_device(dc, unit)) - unit++; - device_printf(dev, "moving to sio%d\n", unit); - err = device_set_unit(dev, unit); /* EVIL DO NOT COPY */ - if (err) - device_printf(dev, "error moving device %d\n", err); + /* Allocate interrupt. */ + rid = 0; + d->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + if (! d->irq) { + printf("sio%d: couldn't map interrupt\n", sio_pci_numunits); + err = ENXIO; + goto fail; + } + + /* Register the interrupt handler. */ + err = bus_setup_intr(dev, d->irq, INTR_TYPE_TTY | INTR_TYPE_FAST, + sio_pci_intr, d, &d->intrhand); + if (err) { + printf("sio%d: couldn't set up irq\n", sio_pci_numunits); +fail: if (d->res) + bus_release_resource(dev, SYS_RES_IOPORT, + id->rid, d->res); + if (d->irq) + bus_release_resource(dev, SYS_RES_IRQ, 0, d->irq); + goto done; + } + + /* Attach sio ports. */ + bst = rman_get_bustag(d->res); + bsh = rman_get_bushandle(d->res); + iobase = rman_get_start(d->res); + offset = 0; + for (com=d->com; comcom+d->nchan; ++com, offset+=id->iostep) { + /* Get a handle for a subregion of an already-mapped + * area of bus space. */ +#if waiting_for_somebody_to_implement_bus_space_subregion + if (bus_space_subregion (bst, bsh, offset, 8, &bsh_port) != 0) { + printf("sio%d: cannot get bus subregion\n", + sio_pci_numunits); + continue; } +#else + bsh_port = bsh + offset; +#endif + /* Interrupt enable, do it _before_ sio_attach_unit. */ + outb (iobase + offset + com_mcr, + d->nchan > 1 ? MCR_IENABLE : 0); + + if (sio_attach_unit(com, sio_pci_numunits++, iobase + offset, + id->fifo_size << 24, 0, bst, bsh_port) != 0) + printf("sio%d: cannot attach\n", sio_pci_numunits); + + /* Must do this _after_ sio_attach_unit. */ + com->st16650a = id->hw_rts_cts; + } +done: + splx (s); + return err; } static int @@ -645,7 +709,7 @@ if (id->desc == NULL) return (ENXIO); device_set_desc(dev, id->desc); - return (sioprobe(dev, id->rid)); + return (0); } #endif /* NPCI > 0 */ @@ -1096,15 +1160,13 @@ int xrid; { struct com_s *com; -#ifdef COM_ESP - Port_t *espp; -#endif Port_t iobase; int unit; u_int flags; int rid; struct resource *port; int ret; + int no_irq; rid = xrid; port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, @@ -1117,6 +1179,66 @@ com = device_get_softc(dev); flags = device_get_flags(dev); +#ifdef COM_MULTIPORT + if (COM_ISMULTIPORT(flags)) { + device_t masterdev; + + masterdev = devclass_get_device(sio_devclass, + COM_MPMASTER(flags)); + no_irq = (masterdev == NULL || bus_get_resource(masterdev, + SYS_RES_IRQ, 0, NULL, NULL) != 0); + } else +#endif /* COM_MULTIPORT */ + no_irq = bus_get_resource(dev, SYS_RES_IRQ, 0, NULL, NULL) != 0; + + ret = sio_attach_unit(com, unit, iobase, flags, no_irq, + rman_get_bustag(port), rman_get_bushandle(port)); + + if (ret != 0) { + /* Leave i/o resources allocated if this is a `cn'-level + * console, so that other devices can't snarf them. */ + if (iobase != siocniobase) + bus_release_resource(dev, SYS_RES_IOPORT, rid, port); + return ret; + } + + rid = 0; + com->irqres = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0ul, ~0ul, 1, + RF_ACTIVE); + if (com->irqres) { + ret = BUS_SETUP_INTR(device_get_parent(dev), dev, com->irqres, + INTR_TYPE_TTY | INTR_TYPE_FAST, + siointr, com, &com->cookie); + if (ret) { + ret = BUS_SETUP_INTR(device_get_parent(dev), dev, + com->irqres, INTR_TYPE_TTY, + siointr, com, &com->cookie); + if (ret == 0) + device_printf(dev, "unable to activate interrupt in fast mode - using normal mode"); + } + if (ret) + device_printf(dev, "could not activate interrupt\n"); + } + com->ioportres = port; + return 0; +} + +int +sio_attach_unit(com, unit, iobase, flags, no_irq, bst, bsh) + struct com_s *com; + int unit; + Port_t iobase; + u_int flags; + bool_t no_irq; + bus_space_tag_t bst; + bus_space_handle_t bsh; +{ +#ifdef COM_ESP + Port_t *espp; +#endif + + if (unit >= SIO_MAXUNITS) + return ENXIO; if (unit >= sio_numunits) sio_numunits = unit + 1; /* @@ -1133,13 +1255,12 @@ */ bzero(com, sizeof *com); com->unit = unit; - com->ioportres = port; - com->bst = rman_get_bustag(port); - com->bsh = rman_get_bushandle(port); + com->bst = bst; + com->bsh = bsh; com->cfcr_image = CFCR_8BITS; com->dtr_wait = 3 * hz; com->loses_outints = COM_LOSESOUTINTS(flags) != 0; - com->no_irq = bus_get_resource(dev, SYS_RES_IRQ, 0, NULL, NULL) != 0; + com->no_irq = no_irq; com->tx_fifo_size = 1; com->obufs[0].l_head = com->obuf1; com->obufs[1].l_head = com->obuf2; @@ -1175,12 +1296,6 @@ com->it_in.c_ispeed = com->it_in.c_ospeed = TTYDEF_SPEED; if (siosetwater(com, com->it_in.c_ispeed) != 0) { enable_intr(); - /* - * Leave i/o resources allocated if this is a `cn'-level - * console, so that other devices can't snarf them. - */ - if (iobase != siocniobase) - bus_release_resource(dev, SYS_RES_IOPORT, rid, port); return (ENOMEM); } enable_intr(); @@ -1287,17 +1402,11 @@ #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(flags)) { - device_t masterdev; - com->multiport = TRUE; printf(" (multiport"); if (unit == COM_MPMASTER(flags)) printf(" master"); printf(")"); - masterdev = devclass_get_device(sio_devclass, - COM_MPMASTER(flags)); - com->no_irq = (masterdev == NULL || bus_get_resource(masterdev, - SYS_RES_IRQ, 0, NULL, NULL) != 0); } #endif /* COM_MULTIPORT */ if (unit == comconsole) @@ -1306,6 +1415,8 @@ printf(" with a bogus IIR_TXRDY register"); printf("\n"); + com_addr(unit) = com; + if (!sio_registered) { register_swi(SWI_TTY, siopoll); sio_registered = TRUE; @@ -1326,24 +1437,6 @@ com->pps.ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR; pps_init(&com->pps); - rid = 0; - com->irqres = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0ul, ~0ul, 1, - RF_ACTIVE); - if (com->irqres) { - ret = BUS_SETUP_INTR(device_get_parent(dev), dev, com->irqres, - INTR_TYPE_TTY | INTR_TYPE_FAST, - siointr, com, &com->cookie); - if (ret) { - ret = BUS_SETUP_INTR(device_get_parent(dev), dev, - com->irqres, INTR_TYPE_TTY, - siointr, com, &com->cookie); - if (ret == 0) - device_printf(dev, "unable to activate interrupt in fast mode - using normal mode"); - } - if (ret) - device_printf(dev, "could not activate interrupt\n"); - } - return (0); } @@ -1830,6 +1923,27 @@ } while (possibly_more_intrs); COM_UNLOCK(); #endif /* COM_MULTIPORT */ +} + +static void +sio_pci_intr(arg) + void *arg; +{ + struct multicom *d = arg; + struct com_s *com; + bool_t possibly_more_intrs; + + disable_intr(); + do { + possibly_more_intrs = FALSE; + for (com=d->com; comcom+d->nchan; ++com) { + if ((inb(com->int_id_port) & IIR_IMASK) != IIR_NOPEND) { + siointr1(com); + possibly_more_intrs = TRUE; + } + } + } while (possibly_more_intrs); + enable_intr(); } static void >Release-Note: >Audit-Trail: >Unformatted: To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-bugs" in the body of the message