From 2aa4b18cd1eaf39b83dacb13149fd9b9234ab9f0 Mon Sep 17 00:00:00 2001 From: jhay Date: Wed, 30 Jan 2002 17:22:04 +0000 Subject: Add support for different serial clock frequencies and not just the standard one of 1.8432MHz. This will be used by the puc (PCI "universal" communication card) device driver. Reviewed by: bde --- sys/dev/sio/sio.c | 152 +++++++++++++++++++++++++++-------------------- sys/dev/sio/sio_isa.c | 4 +- sys/dev/sio/sio_pccard.c | 4 +- sys/dev/sio/sio_pci.c | 4 +- sys/dev/sio/sioreg.h | 4 +- sys/dev/sio/siovar.h | 4 +- 6 files changed, 100 insertions(+), 72 deletions(-) (limited to 'sys') diff --git a/sys/dev/sio/sio.c b/sys/dev/sio/sio.c index 1518fbf..cdd25a5 100644 --- a/sys/dev/sio/sio.c +++ b/sys/dev/sio/sio.c @@ -255,6 +255,8 @@ struct com_s { u_int delta_error_counts[CE_NTYPES]; u_long error_counts[CE_NTYPES]; + u_long rclk; + struct resource *irqres; struct resource *ioportres; void *cookie; @@ -273,6 +275,7 @@ static int espattach __P((struct com_s *com, Port_t esp_port)); #endif static timeout_t siobusycheck; +static u_int siodivisor __P((u_long rclk, speed_t speed)); static timeout_t siodtrwakeup; static void comhardclose __P((struct com_s *com)); static void sioinput __P((struct com_s *com)); @@ -324,6 +327,8 @@ static struct cdevsw sio_cdevsw = { int comconsole = -1; static volatile speed_t comdefaultrate = CONSPEED; +static u_long comdefaultrclk = DEFAULT_RCLK; +SYSCTL_ULONG(_machdep, OID_AUTO, conrclk, CTLFLAG_RW, &comdefaultrclk, 0, ""); #ifdef __alpha__ static volatile speed_t gdbdefaultrate = CONSPEED; #endif @@ -342,29 +347,6 @@ static struct callout_handle sio_timeout_handle = CALLOUT_HANDLE_INITIALIZER(&sio_timeout_handle); static int sio_numunits; -static struct speedtab comspeedtab[] = { - { 0, 0 }, - { 50, COMBRD(50) }, - { 75, COMBRD(75) }, - { 110, COMBRD(110) }, - { 134, COMBRD(134) }, - { 150, COMBRD(150) }, - { 200, COMBRD(200) }, - { 300, COMBRD(300) }, - { 600, COMBRD(600) }, - { 1200, COMBRD(1200) }, - { 1800, COMBRD(1800) }, - { 2400, COMBRD(2400) }, - { 4800, COMBRD(4800) }, - { 9600, COMBRD(9600) }, - { 19200, COMBRD(19200) }, - { 28800, COMBRD(28800) }, - { 38400, COMBRD(38400) }, - { 57600, COMBRD(57600) }, - { 115200, COMBRD(115200) }, - { -1, -1 } -}; - #ifdef COM_ESP /* XXX configure this properly. */ /* XXX quite broken for new-bus. */ @@ -483,9 +465,10 @@ siodetach(dev) } int -sioprobe(dev, xrid, noprobe) +sioprobe(dev, xrid, rclk, noprobe) device_t dev; int xrid; + u_long rclk; int noprobe; { #if 0 @@ -493,6 +476,7 @@ sioprobe(dev, xrid, noprobe) device_t xdev; #endif struct com_s *com; + u_int divisor; bool_t failures[10]; int fn; device_t idev; @@ -518,6 +502,9 @@ sioprobe(dev, xrid, noprobe) device_set_softc(dev, com); com->bst = rman_get_bustag(port); com->bsh = rman_get_bushandle(port); + if (rclk == 0) + rclk = DEFAULT_RCLK; + com->rclk = rclk; while (sio_inited != 2) if (atomic_cmpset_int(&sio_inited, 0, 1)) { @@ -630,8 +617,9 @@ sioprobe(dev, xrid, noprobe) DELAY((16 + 1) * 1000000 / (comdefaultrate / 10)); else { sio_setreg(com, com_cfcr, CFCR_DLAB | CFCR_8BITS); - sio_setreg(com, com_dlbl, COMBRD(SIO_TEST_SPEED) & 0xff); - sio_setreg(com, com_dlbh, (u_int) COMBRD(SIO_TEST_SPEED) >> 8); + divisor = siodivisor(rclk, SIO_TEST_SPEED); + sio_setreg(com, com_dlbl, divisor & 0xff); + sio_setreg(com, com_dlbh, divisor >> 8); sio_setreg(com, com_cfcr, CFCR_8BITS); DELAY((16 + 1) * 1000000 / (SIO_TEST_SPEED / 10)); } @@ -866,9 +854,10 @@ espattach(com, esp_port) #endif /* COM_ESP */ int -sioattach(dev, xrid) +sioattach(dev, xrid, rclk) device_t dev; int xrid; + u_long rclk; { struct com_s *com; #ifdef COM_ESP @@ -927,6 +916,10 @@ sioattach(dev, xrid) com->modem_status_port = iobase + com_msr; com->intr_ctl_port = iobase + com_ier; + if (rclk == 0) + rclk = DEFAULT_RCLK; + com->rclk = rclk; + /* * We don't use all the flags from since they * are only relevant for logins. It's important to have echo off @@ -1495,6 +1488,32 @@ siobusycheck(chan) splx(s); } +static u_int +siodivisor(rclk, speed) + u_long rclk; + speed_t speed; +{ + long actual_speed; + u_int divisor; + int error; + + if (speed == 0 || speed > (ULONG_MAX - 1) / 8) + return (0); + divisor = (rclk / (8UL * speed) + 1) / 2; + if (divisor == 0 || divisor >= 65536) + return (0); + actual_speed = rclk / (16UL * divisor); + + /* 10 times error in percent: */ + error = ((actual_speed - (long)speed) * 2000 / (long)speed + 1) / 2; + + /* 3.0% maximum error tolerance: */ + if (error < -30 || error > 30) + return (0); + + return (divisor); +} + static void siodtrwakeup(chan) void *chan; @@ -2109,26 +2128,33 @@ comparam(tp, t) u_int cfcr; int cflag; struct com_s *com; - int divisor; + u_int divisor; u_char dlbh; u_char dlbl; int s; int unit; + unit = DEV_TO_UNIT(tp->t_dev); + com = com_addr(unit); + if (com == NULL) + return (ENODEV); + /* do historical conversions */ if (t->c_ispeed == 0) t->c_ispeed = t->c_ospeed; /* check requested parameters */ - divisor = ttspeedtab(t->c_ospeed, comspeedtab); - if (divisor < 0 || (divisor > 0 && t->c_ispeed != t->c_ospeed)) - return (EINVAL); + if (t->c_ospeed == 0) + divisor = 0; + else { + if (t->c_ispeed != t->c_ospeed) + return (EINVAL); + divisor = siodivisor(com->rclk, t->c_ispeed); + if (divisor == 0) + return (EINVAL); + } /* parameters are OK, convert them to the com struct and the device */ - unit = DEV_TO_UNIT(tp->t_dev); - com = com_addr(unit); - if (com == NULL) - return (ENODEV); s = spltty(); if (divisor == 0) (void)commctl(com, TIOCM_DTR, DMBIC); /* hang up line */ @@ -2203,7 +2229,7 @@ comparam(tp, t) dlbl = divisor & 0xFF; if (sio_getreg(com, com_dlbl) != dlbl) sio_setreg(com, com_dlbl, dlbl); - dlbh = (u_int) divisor >> 8; + dlbh = divisor >> 8; if (sio_getreg(com, com_dlbh) != dlbh) sio_setreg(com, com_dlbh, dlbh); } @@ -2669,7 +2695,7 @@ struct siocnstate { }; #ifndef __alpha__ -static speed_t siocngetspeed __P((Port_t, struct speedtab *)); +static speed_t siocngetspeed __P((Port_t, u_long rclk)); #endif static void siocnclose __P((struct siocnstate *sp, Port_t iobase)); static void siocnopen __P((struct siocnstate *sp, Port_t iobase, int speed)); @@ -2728,11 +2754,11 @@ siocntxwait(iobase) */ static speed_t -siocngetspeed(iobase, table) - Port_t iobase; - struct speedtab *table; +siocngetspeed(iobase, rclk) + Port_t iobase; + u_long rclk; { - int code; + u_int divisor; u_char dlbh; u_char dlbl; u_char cfcr; @@ -2745,13 +2771,12 @@ siocngetspeed(iobase, table) outb(iobase + com_cfcr, cfcr); - code = dlbh << 8 | dlbl; - - for (; table->sp_speed != -1; table++) - if (table->sp_code == code) - return (table->sp_speed); + divisor = dlbh << 8 | dlbl; - return (0); /* didn't match anything sane */ + /* XXX there should be more sanity checking. */ + if (divisor == 0) + return (CONSPEED); + return (rclk / (16UL * divisor)); } #endif @@ -2762,7 +2787,7 @@ siocnopen(sp, iobase, speed) Port_t iobase; int speed; { - int divisor; + u_int divisor; u_char dlbh; u_char dlbl; @@ -2784,11 +2809,11 @@ siocnopen(sp, iobase, speed) * data input register. This also reduces the effects of the * UMC8669F bug. */ - divisor = ttspeedtab(speed, comspeedtab); + divisor = siodivisor(comdefaultrclk, speed); dlbl = divisor & 0xFF; if (sp->dlbl != dlbl) outb(iobase + com_dlbl, dlbl); - dlbh = (u_int) divisor >> 8; + dlbh = divisor >> 8; if (sp->dlbh != dlbh) outb(iobase + com_dlbh, dlbh); outb(iobase + com_cfcr, CFCR_8BITS); @@ -2831,6 +2856,7 @@ siocnprobe(cp) { speed_t boot_speed; u_char cfcr; + u_int divisor; int s, unit; struct siocnstate sp; @@ -2868,7 +2894,8 @@ siocnprobe(cp) iobase = port; s = spltty(); if (boothowto & RB_SERIAL) { - boot_speed = siocngetspeed(iobase, comspeedtab); + boot_speed = + siocngetspeed(iobase, comdefaultrclk); if (boot_speed) comdefaultrate = boot_speed; } @@ -2884,10 +2911,9 @@ siocnprobe(cp) */ cfcr = inb(iobase + com_cfcr); outb(iobase + com_cfcr, CFCR_DLAB | cfcr); - outb(iobase + com_dlbl, - COMBRD(comdefaultrate) & 0xff); - outb(iobase + com_dlbh, - (u_int) COMBRD(comdefaultrate) >> 8); + divisor = siodivisor(comdefaultrclk, comdefaultrate); + outb(iobase + com_dlbl, divisor & 0xff); + outb(iobase + com_dlbh, divisor >> 8); outb(iobase + com_cfcr, cfcr); siocnopen(&sp, iobase, comdefaultrate); @@ -2962,6 +2988,7 @@ siocnattach(port, speed) { int s; u_char cfcr; + u_int divisor; struct siocnstate sp; siocniobase = port; @@ -2982,10 +3009,9 @@ siocnattach(port, speed) */ cfcr = inb(siocniobase + com_cfcr); outb(siocniobase + com_cfcr, CFCR_DLAB | cfcr); - outb(siocniobase + com_dlbl, - COMBRD(comdefaultrate) & 0xff); - outb(siocniobase + com_dlbh, - (u_int) COMBRD(comdefaultrate) >> 8); + divisor = siodivisor(comdefaultrclk, comdefaultrate); + outb(siocniobase + com_dlbl, divisor & 0xff); + outb(siocniobase + com_dlbh, divisor >> 8); outb(siocniobase + com_cfcr, cfcr); siocnopen(&sp, siocniobase, comdefaultrate); @@ -3002,6 +3028,7 @@ siogdbattach(port, speed) { int s; u_char cfcr; + u_int divisor; struct siocnstate sp; int unit = 1; /* XXX !!! */ @@ -3029,10 +3056,9 @@ siogdbattach(port, speed) */ cfcr = inb(siogdbiobase + com_cfcr); outb(siogdbiobase + com_cfcr, CFCR_DLAB | cfcr); - outb(siogdbiobase + com_dlbl, - COMBRD(gdbdefaultrate) & 0xff); - outb(siogdbiobase + com_dlbh, - (u_int) COMBRD(gdbdefaultrate) >> 8); + divisor = siodivisor(comdefaultrclk, gdbdefaultrate); + outb(siogdbiobase + com_dlbl, divisor & 0xff); + outb(siogdbiobase + com_dlbh, divisor >> 8); outb(siogdbiobase + com_cfcr, cfcr); siocnopen(&sp, siogdbiobase, gdbdefaultrate); diff --git a/sys/dev/sio/sio_isa.c b/sys/dev/sio/sio_isa.c index fe4d6f9..6e8fc60 100644 --- a/sys/dev/sio/sio_isa.c +++ b/sys/dev/sio/sio_isa.c @@ -149,14 +149,14 @@ sio_isa_probe(dev) /* Check isapnp ids */ if (ISA_PNP_PROBE(device_get_parent(dev), dev, sio_ids) == ENXIO) return (ENXIO); - return (sioprobe(dev, 0, 0)); + return (sioprobe(dev, 0, 0UL, 0)); } static int sio_isa_attach(dev) device_t dev; { - return (sioattach(dev, 0)); + return (sioattach(dev, 0, 0UL)); } DRIVER_MODULE(sio, isa, sio_isa_driver, sio_devclass, 0, 0); diff --git a/sys/dev/sio/sio_pccard.c b/sys/dev/sio/sio_pccard.c index 4db2e9d..c606312 100644 --- a/sys/dev/sio/sio_pccard.c +++ b/sys/dev/sio/sio_pccard.c @@ -98,14 +98,14 @@ sio_pccard_probe(dev) #endif /* Do not probe IRQ - pccard doesn't turn on the interrupt line */ /* until bus_setup_intr */ - return (sioprobe(dev, 0, 1)); + return (sioprobe(dev, 0, 0UL, 1)); } static int sio_pccard_attach(dev) device_t dev; { - return (sioattach(dev, 0)); + return (sioattach(dev, 0, 0UL)); } static int diff --git a/sys/dev/sio/sio_pci.c b/sys/dev/sio/sio_pci.c index a369146..3cf80f5 100644 --- a/sys/dev/sio/sio_pci.c +++ b/sys/dev/sio/sio_pci.c @@ -92,7 +92,7 @@ sio_pci_attach(dev) if (id->desc == NULL) return (ENXIO); sio_pci_kludge_unit(dev); - return (sioattach(dev, id->rid)); + return (sioattach(dev, id->rid, 0UL)); } /* @@ -138,7 +138,7 @@ sio_pci_probe(dev) if (id->desc == NULL) return (ENXIO); device_set_desc(dev, id->desc); - return (sioprobe(dev, id->rid, 0)); + return (sioprobe(dev, id->rid, 0UL, 0)); } DRIVER_MODULE(sio, pci, sio_pci_driver, sio_devclass, 0, 0); diff --git a/sys/dev/sio/sioreg.h b/sys/dev/sio/sioreg.h index 52d7658..a3fbc96 100644 --- a/sys/dev/sio/sioreg.h +++ b/sys/dev/sio/sioreg.h @@ -34,10 +34,12 @@ * $FreeBSD$ */ +/* Receiver clock frequency for "standard" pc serial ports. */ +#define DEFAULT_RCLK 1843200 +#ifdef PC98 /* 16 bit baud rate divisor (lower byte in dca_data, upper in dca_ier) */ #define COMBRD(x) (1843200 / (16*(x))) -#ifdef PC98 #define COMBRD_RSA(x) (14745600 / (16*(x))) #endif diff --git a/sys/dev/sio/siovar.h b/sys/dev/sio/siovar.h index 2c9913f..a6ba168 100644 --- a/sys/dev/sio/siovar.h +++ b/sys/dev/sio/siovar.h @@ -63,9 +63,9 @@ #define CLR_FLAG(dev, bit) device_set_flags(dev, device_get_flags(dev) & ~(bit)) #endif /* PC98 */ -int sioattach __P((device_t dev, int xrid)); +int sioattach __P((device_t dev, int xrid, u_long rclk)); int siodetach __P((device_t dev)); -int sioprobe __P((device_t dev, int xrid, int noprobe)); +int sioprobe __P((device_t dev, int xrid, u_long rclk, int noprobe)); extern devclass_t sio_devclass; extern char sio_driver_name[]; -- cgit v1.1