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