Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 10 Aug 2000 18:40:56 +0400 (MSD)
From:      vak@cronyx.ru
To:        FreeBSD-gnats-submit@freebsd.org
Subject:   kern/20523: [patch] -current, sio driver, support for PCI multiport cards
Message-ID:  <200008101440.SAA00350@crox.cronyx.ru>

next in thread | raw e-mail | index | archive | help

>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; com<d->com+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; com<d->com+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




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200008101440.SAA00350>