Subject: pc532 duarts
To: None <pc532@bungi.com, port-pc532@NetBSD.ORG>
From: Phil Budne <budd@cs.bu.edu>
List: port-pc532
Date: 02/10/1997 01:41:22
After reading the spec sheet for the 26c92 and seeing it had two
additional speed tables (each with two 16-entry "sides") and new
speeds from the reduiculous (880bps) to the sublime 115.2kbps, I just
sat down and wrote some code I'd been trying to figure out how to
write for about 7 years.
I haven't put it into the pc532 scn driver yet, I thought I'd toss it
out for people to play with until I find the time...
/*
* speed.c - attempt to fully utilize 2681/2692/26c92 duart rate capabilities
* Phil Budne <phil@ultimate.com>
* February 9, 1997
*
* Does not attempt to fuss with channels currently in use (would have
* to be delayed until no output active on EITHER channel).
*/
/* modes MR0A[0:2] (only 26c92 has MODE1 and MODE2) */
#define MODE0 0
#define MODE1 1
#define MODE2 4
#define MODEMASK 0x7
/* speed table groups ACR[7] */
#define GRP_A 0
#define GRP_B 0x80
#define MODE0A (MODE0|GRP_A)
#define MODE0B (MODE0|GRP_B)
#define MODE1A (MODE1|GRP_A)
#define MODE1B (MODE1|GRP_B)
#define MODE2A (MODE2|GRP_A)
#define MODE2B (MODE2|GRP_B)
#define ANYMODE -1
#define DEFMODE MODE0A /* use MODE4A if 26c92? */
/* speed code for Counter/Timer (all modes, groups) */
#define USE_CT 0xd
/*
* Rate table, ordered by speed, then mode NOTE: ordering of modes
* must be done carefully. 26C92 Extended mode1 looks pretty useless
* except for ultra-high rates. 26C92 Extended mode2 has lots of good
* stuff (but nothing useful below 4800).
*
* Could add entries for counter/timer (code 0xd) and desirable modes
* before entries for undesireable modes.
*/
struct tabent {
int speed;
int code;
int mode;
} table[] = {
50, 0x0, MODE0A,
75, 0x0, MODE0B,
110, 0x1, MODE0A,
110, 0x1, MODE0B,
110, 0x1, MODE1A,
110, 0x1, MODE1B,
134, 0x2, MODE0A, /* 134.5 */
134, 0x2, MODE0B, /* 134.5 */
134, 0x2, MODE1A, /* 134.5 */
134, 0x2, MODE1B, /* 134.5 */
150, 0x3, MODE0A,
150, 0x3, MODE0A,
200, 0x3, MODE0A,
300, 0x4, MODE0A,
300, 0x4, MODE0B,
300, 0x0, MODE1A,
450, 0x0, MODE1B,
600, 0x5, MODE0A,
600, 0x5, MODE0B,
880, 0x1, MODE2A,
880, 0x1, MODE2B,
900, 0x3, MODE1B,
1050, 0x7, MODE0A,
1050, 0x7, MODE1A,
1076, 0x2, MODE2A,
1076, 0x2, MODE2B,
1200, 0x6, MODE0A,
1200, 0x6, MODE0B,
1200, 0x3, MODE1A,
1800, 0xa, MODE0B,
1800, 0x4, MODE1A,
1800, 0x4, MODE1B,
2000, 0x7, MODE0B,
2000, 0x7, MODE1B,
2400, 0x8, MODE0A,
2400, 0x8, MODE0B,
3600, 0x5, MODE1A,
3600, 0x5, MODE1B,
4800, 0x9, MODE2A,
4800, 0x9, MODE2B,
4800, 0x9, MODE0A,
4800, 0x9, MODE0B,
7200, 0xa, MODE0A,
7200, 0x6, MODE1A,
7200, 0x6, MODE1B,
7200, 0x0, MODE2B,
9600, 0xb, MODE2A,
9600, 0xb, MODE2B,
9600, 0xb, MODE0A,
9600, 0xb, MODE0B,
14400, 0x8, MODE1A,
14400, 0x8, MODE1B,
14400, 0x3, MODE2B,
19200, 0x3, MODE2A,
19200, 0xc, MODE2B,
19200, 0xc, MODE0B,
28800, 0x4, MODE2A,
28800, 0x4, MODE2B,
28800, 0x9, MODE1A,
28800, 0x9, MODE1B,
38400, 0xc, MODE2A,
38400, 0xc, MODE0A,
57600, 0x5, MODE2A,
57600, 0x5, MODE2B,
57600, 0xb, MODE1A,
57600, 0xb, MODE1B,
115200, 0x6, MODE2A,
115200, 0x6, MODE2B,
115200, 0xc, MODE1B,
230400, 0xc, MODE1A
};
#define ENTRIES (sizeof(table)/sizeof(table[0]))
#if 0
/* manually constructed divisors table for minimum error */
const struct {
int speed;
int div;
} divisors[] = {
50, 2303,
75, 1536,
110, 1047, /* Should be 1047.27 */
134, 857, /* Should be 856.505576 */
150, 768,
200, 576,
300, 384,
600, 192,
1050, 110, /* Should be 109.7142857 */
1200, 96,
1800, 64,
2000, 57, /* should be 57.6 */
2400, 48,
4800, 24,
7200, 16,
9600, 12,
19200, 6,
38400, 3,
57600, 2
};
#define DIVISORS (sizeof(divisors)/sizeof(divisors[0]))
#endif
/* boolean for speed codes which are identical in both A/B groups
* in all modes
*/
static unsigned char bothgroups[16] = {
0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0
};
struct duart {
struct chan {
int open; /* XXX pointer to struct tty */
int ispeed;
int ospeed;
int icode;
int ocode;
} chan[2];
int c92;
int mode;
int counter;
} duart[4];
/*
* iterator function for speeds. returns sequence of possible
* speed codes for a given rate
*/
static int
iter(int *index, /* IN/OUT: index */
int wanted, /* IN: speed wanted */
int *counter, /* IN/OUT: counter/timer rate */
int *mode, /* IN/OUT: mode */
struct chan *other, /* IN: other channel */
int c92) /* IN: true for 26C92 */
{
for (; *index < ENTRIES; (*index)++) {
struct tabent *tp;
tp = table + *index;
if (tp->speed != wanted)
continue;
/* if not a 26C92 only look at MODE0 entries */
if (!c92 && (tp->mode & MODEMASK) != MODE0)
continue;
/*
* check mode;
* OK if this table entry for current mode, or mode not yet set,
* or other channel's rates are available in both A and B groups.
*/
if (tp->mode == *mode || *mode == ANYMODE ||
((tp->mode & MODEMASK) == (*mode & MODEMASK) &&
bothgroups[other->icode] && bothgroups[other->ocode])) {
/* for future table entries specifying use of counter/timer */
if (tp->code == USE_CT) {
if (*counter != wanted && *counter != 0)
continue; /* counter busy */
*counter = wanted;
}
*mode = tp->mode;
return tp->code;
} /* mode OK */
} /* for each table entry */
/* here after returning all applicable table entries */
if ((*index)++ == ENTRIES) {
/* Max C/T rate (even on 26C92?) is 57600 */
if (wanted <= 57600 && (*counter == wanted || *counter == 0)) {
*counter = wanted;
return USE_CT;
}
}
return -1; /* FAIL */
}
static void
setchip(int unit, struct duart *new)
{
struct duart *dp;
dp = &duart[unit/2];
if (dp->mode == new->mode &&
dp->counter == new->counter &&
dp->chan[0].icode == new->chan[0].icode &&
dp->chan[0].ocode == new->chan[0].ocode &&
dp->chan[1].icode == new->chan[1].icode &&
dp->chan[1].icode == new->chan[1].icode)
return; /* no changes */
printf("old: mode %x a: in %x out %x b: in %x out %x counter: %d\n",
dp->mode,
dp->chan[0].icode,
dp->chan[0].ocode,
dp->chan[1].icode,
dp->chan[1].icode,
dp->counter);
printf("new: mode %x a: in %x out %x b: in %x out %x counter: %d\n",
new->mode,
new->chan[0].icode,
new->chan[0].ocode,
new->chan[1].icode,
new->chan[1].icode,
new->counter);
/* save new values, program chip, or set flags so changes
* occur when tx done
*/
*dp = *new;
/* check to see if counter/timer in use.
* if not, zero "counter" so we know it's free
*/
if (dp->counter == 0 ||
dp->chan[0].icode == USE_CT ||
dp->chan[0].ocode == USE_CT ||
dp->chan[1].icode == USE_CT ||
dp->chan[1].ocode == USE_CT)
return;
dp->counter = 0;
printf("counter idle\n");
}
static int
config(int unit, int ispeed, int ospeed) {
struct duart *dp;
int chan; /* 0 or 1 */
int other; /* 1 or 0 */
int mode;
int counter;
int i, o; /* input, output iterator indexes */
int ic, oc; /* input, output codes */
struct chan *ocp;
printf("config %d: ispeed %d ospeed %d\n", unit, ispeed, ospeed);
dp = &duart[unit/2];
chan = unit & 1;
other = chan ^ 1;
ocp = &dp->chan[other];
if (ocp->open) {
mode = dp->mode;
counter = dp->counter;
i = 0;
while ((ic = iter(&i, ispeed, &counter, &mode, ocp, dp->c92)) != -1) {
o = 0;
if ((oc = iter(&o, ospeed, &counter, &mode, ocp, dp->c92)) != -1) {
struct duart new;
new = *dp;
new.chan[chan].ispeed = ispeed;
new.chan[chan].ospeed = ospeed;
new.chan[chan].icode = ic;
new.chan[chan].ocode = oc;
new.counter = counter;
if (mode == ANYMODE) /* no mode selected?? */
mode = DEFMODE;
new.mode = mode;
setchip(unit, &new);
return 1;
}
}
}
else {
int oo, oi; /* other input, output iterators */
int oic, ooc; /* other input, output codes */
mode = ANYMODE;
counter = 0;
oi = 0;
while ((oic = iter(&oi, ocp->ispeed, &counter,
&mode, ocp, dp->c92)) != -1) {
oo = 0;
while ((ooc = iter(&oo, ocp->ospeed, &counter,
&mode, ocp, dp->c92)) != -1) {
i = 0;
while ((ic = iter(&i, ispeed, &counter,
&mode, ocp, dp->c92)) != -1) {
o = 0;
if ((oc = iter(&oo, ispeed, &counter,
&mode, ocp, dp->c92)) != -1) {
struct duart new;
new = *dp;
new.chan[chan].ispeed = ispeed;
new.chan[chan].ospeed = ospeed;
new.chan[chan].icode = ic;
new.chan[chan].ocode = oc;
new.chan[other].icode = oic;
new.chan[other].ocode = ooc;
new.counter = counter;
if (mode == ANYMODE) /* no mode selected?? */
mode = DEFMODE;
new.mode = mode;
setchip(unit, &new);
return 1;
}
}
}
}
}
puts("FAILED");
}
/**************** test routines-- only use units 0/1 */
int default_rate;
void
init(int def, int c92)
{
default_rate = def;
duart[0].mode = ANYMODE;
duart[0].counter = 0;
duart[0].c92 = c92;
duart[0].chan[0].ispeed = default_rate;
duart[0].chan[0].ospeed = default_rate;
duart[0].chan[1].ispeed = default_rate;
duart[0].chan[1].ospeed = default_rate;
duart[0].chan[0].icode = 0xff;
duart[0].chan[0].ocode = 0xff;
duart[0].chan[1].icode = 0xff;
duart[0].chan[1].ocode = 0xff;
}
int
chopen(int chan)
{
printf("chopen(%d)\n", chan);
if (config(chan, default_rate, default_rate)) {
duart[0].chan[chan].open = 1;
return 1;
}
return 0;
}
void
chclose(int chan)
{
printf("chclose(%d)\n", chan);
duart[0].chan[chan].open = 0;
config(chan, default_rate, default_rate); /* ?? */
}
void
simptest(int def, int c92, int s1, int s2)
{
printf("==== simptest def %d %s%d/%d\n",
def, (c92 ? "26c92 " : ""), s1, s2);
init(def,c92);
chopen(0);
config(0, s1, s1);
chopen(1);
config(1, s2, s2);
}
void
simptestb(int def, int c92, int s1, int s2)
{
printf("==== simptestb def %d %s%d/%d\n",
def, (c92 ? "26c92 " : ""), s1, s2);
init(def,c92);
chopen(0);
chopen(1);
config(0, s1, s1);
config(1, s2, s2);
}
void
simptest2(int def, int c92, int s1, int s2)
{
simptest(def, c92, s1, s2);
simptest(def, c92, s2, s1);
simptestb(def, c92, s1, s2);
simptestb(def, c92, s2, s1);
}
void
simptest3(int def, int s1, int s2)
{
simptest2(def, 0, s1, s2);
simptest2(def, 1, s1, s2);
}
main()
{
simptest3(9600, 19200, 38400);
simptest3(9600, 19200, 57600);
simptest3(9600, 19200, 115200);
}