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
This commit is contained in:
John Hay 2002-01-30 17:22:04 +00:00
parent aff5957cfd
commit 57019e6041
6 changed files with 100 additions and 72 deletions

View File

@ -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 <sys/ttydefaults.h> 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;
divisor = dlbh << 8 | dlbl;
for (; table->sp_speed != -1; table++)
if (table->sp_code == code)
return (table->sp_speed);
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);

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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[];