Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/puc/pucdata.c
Show All 33 Lines | |||||
* match/attach the cards). | * match/attach the cards). | ||||
*/ | */ | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/bus.h> | #include <sys/bus.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/limits.h> | |||||
#include <sys/ttycom.h> | |||||
#include <machine/resource.h> | #include <machine/resource.h> | ||||
#include <machine/bus.h> | #include <machine/bus.h> | ||||
#include <sys/rman.h> | #include <sys/rman.h> | ||||
#include <dev/ic/ns16550.h> | #include <dev/ic/ns16550.h> | ||||
#include <dev/pci/pcireg.h> | #include <dev/pci/pcireg.h> | ||||
▲ Show 20 Lines • Show All 1,699 Lines • ▼ Show 20 Lines | else | ||||
*res = DEFAULT_RCLK; | *res = DEFAULT_RCLK; | ||||
return (0); | return (0); | ||||
default: | default: | ||||
break; | break; | ||||
} | } | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
struct puc_util_fract { | |||||
uint64_t n; | |||||
uint64_t d; | |||||
}; | |||||
/* | |||||
* Returns the greatest common denomiator of a and b | |||||
* Thanks Wikipedia! | |||||
*/ | |||||
static uint64_t | |||||
puc_util_gcd(uint64_t a, uint64_t b) | |||||
{ | |||||
uint64_t d = 0; | |||||
while (((a | b) & 1) == 0) { | |||||
a >>= 1; | |||||
b >>= 1; | |||||
d++; | |||||
} | |||||
while (a != b) { | |||||
if ((a & 1) == 0) | |||||
a >>= 1; | |||||
else if ((b & 1) == 0) | |||||
b >>= 1; | |||||
else if (a > b) | |||||
a = (a - b) >> 1; | |||||
else | |||||
b = (b - a) >> 1; | |||||
} | |||||
return (1<<d) * a; | |||||
} | |||||
/* | |||||
* Reduces a fraction | |||||
*/ | |||||
static void | |||||
puc_util_fract_reduce(struct puc_util_fract *f) | |||||
{ | |||||
uint64_t d = puc_util_gcd(f->n, f->d); | |||||
f->n /= d; | |||||
f->d /= d; | |||||
} | |||||
/* | |||||
* Divides two fractions | |||||
* r may equal x or y. | |||||
*/ | |||||
static void | |||||
puc_util_fract_div(struct puc_util_fract *x, struct puc_util_fract *y, | |||||
struct puc_util_fract *r) | |||||
{ | |||||
uint64_t rn, rd; | |||||
rn = x->n * y->d; | |||||
rd = x->d * y->n; | |||||
r->n = rn; | |||||
r->d = rd; | |||||
puc_util_fract_reduce(r); | |||||
} | |||||
struct puc_oxford_1695x_result { | |||||
uint16_t cpr; | |||||
uint16_t div; | |||||
uint8_t tcr; | |||||
}; | |||||
static int | static int | ||||
puc_oxford_1695x_solve(struct puc_util_fract *baud, | |||||
struct puc_oxford_1695x_result *res) | |||||
{ | |||||
struct puc_util_fract base = {62500000, 16}; | |||||
struct puc_util_fract prescale; | |||||
struct puc_util_fract result; | |||||
int tcr; | |||||
int div; | |||||
int cpr; | |||||
int bcpr = INT_MAX; | |||||
int bdiv = INT_MAX; | |||||
int btcr; | |||||
int act; | |||||
int64_t err; | |||||
int64_t berr = INT64_MAX; | |||||
/* First, try for "perfect" just using div */ | |||||
puc_util_fract_div(&base, baud, &result); | |||||
/* We got it... done. */ | |||||
if (result.d == 1) { | |||||
if (result.n < 65536 && result.n > 0) { | |||||
res->tcr = 16; | |||||
res->cpr = 8; | |||||
res->div = result.n; | |||||
return 0; | |||||
} | |||||
} | |||||
/* | |||||
* Now we try all combinations of tcr/cpr, picking the best one. | |||||
* We prefer high tcr, and we may still get a perfect match. | |||||
* This results in a table similar to the one in the datasheet, | |||||
* but with a preference for a high tcr and low prescaler. | |||||
* The high tcr may make it more resistant to timing errors, | |||||
* and the low prescaler shouldn't matter. | |||||
*/ | |||||
for (tcr = 16; tcr >= 4; tcr--) { | |||||
base.d = tcr; | |||||
for (cpr = 8; cpr < 512; cpr++) { | |||||
prescale.n = cpr; | |||||
prescale.d = 8; | |||||
puc_util_fract_div(&base, &prescale, &prescale); | |||||
puc_util_fract_div(&prescale, baud, &result); | |||||
/* We got a perfect match. */ | |||||
if (result.d == 1) { | |||||
if (result.n < 65536 && result.n > 0) { | |||||
res->tcr = tcr; | |||||
res->cpr = cpr; | |||||
res->div = result.n; | |||||
return 0; | |||||
} | |||||
} | |||||
div = ((result.n << 1) / result.d + 1) >> 1; | |||||
if (div > 65535) | |||||
continue; | |||||
if (div < 1) | |||||
continue; | |||||
act = (((base.n << 1) / div + 1) / base.d) >> 1; | |||||
err = (((baud->n << 1) / baud->d + 1) >> 1) - act; | |||||
if (err < 0) | |||||
err = -err; | |||||
if (err < berr) { | |||||
bcpr = cpr; | |||||
bdiv = div; | |||||
berr = err; | |||||
btcr = tcr; | |||||
} | |||||
} | |||||
} | |||||
res->tcr = btcr; | |||||
res->cpr = bcpr; | |||||
res->div = bdiv; | |||||
/* | |||||
* Check for errors... | |||||
*/ | |||||
if (res->tcr < 4 || res->tcr > 16 || | |||||
res->cpr < 8 || res->cpr > 511 || | |||||
res->div < 1) { | |||||
res->tcr = 16; | |||||
res->cpr = 8; | |||||
res->div = 65535; | |||||
return EINVAL; | |||||
} | |||||
return 0; | |||||
} | |||||
static int | |||||
puc_config_oxford_pcie(struct puc_softc *sc, enum puc_cfg_cmd cmd, int port, | puc_config_oxford_pcie(struct puc_softc *sc, enum puc_cfg_cmd cmd, int port, | ||||
intptr_t *res) | intptr_t *res) | ||||
{ | { | ||||
const struct puc_cfg *cfg = sc->sc_cfg; | const struct puc_cfg *cfg = sc->sc_cfg; | ||||
struct puc_oxford_1695x_result bres; | |||||
struct puc_util_fract baud; | |||||
struct baud_fraction *bf; | |||||
int idx; | int idx; | ||||
struct puc_bar *bar; | struct puc_bar *bar; | ||||
uint8_t value; | uint8_t value; | ||||
uint16_t prescale; | |||||
int rval; | |||||
switch (cmd) { | switch (cmd) { | ||||
case PUC_CFG_SETUP: | case PUC_CFG_SETUP: | ||||
device_printf(sc->sc_dev, "%d UARTs detected\n", | device_printf(sc->sc_dev, "%d UARTs detected\n", | ||||
sc->sc_nports); | sc->sc_nports); | ||||
/* Set UARTs to enhanced mode */ | /* Set UARTs to enhanced mode */ | ||||
bar = puc_get_bar(sc, cfg->rid); | bar = puc_get_bar(sc, cfg->rid); | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | case PUC_CFG_GET_NPORTS: | ||||
sc->sc_cfg_data = value; | sc->sc_cfg_data = value; | ||||
*res = sc->sc_cfg_data; | *res = sc->sc_cfg_data; | ||||
return (0); | return (0); | ||||
case PUC_CFG_GET_OFS: | case PUC_CFG_GET_OFS: | ||||
*res = 0x1000 + (port << 9); | *res = 0x1000 + (port << 9); | ||||
return (0); | return (0); | ||||
case PUC_CFG_GET_TYPE: | case PUC_CFG_GET_TYPE: | ||||
*res = PUC_TYPE_SERIAL; | *res = PUC_TYPE_SERIAL; | ||||
return (0); | |||||
case PUC_CFG_TARGETBAUD: | |||||
bf = (struct baud_fraction *)res; | |||||
bar = puc_get_bar(sc, cfg->rid); | |||||
if (bar == NULL) | |||||
return (ENXIO); | |||||
baud.n = bf->bf_numerator; | |||||
baud.d = bf->bf_denominator; | |||||
rval = puc_oxford_1695x_solve(&baud, &bres); | |||||
if (rval) | |||||
return rval; | |||||
value = bus_read_1(bar->b_res, 0x1000 + (port << 9) + 0x04); | |||||
if (bres.cpr == 8) { | |||||
value &= ~0x80; | |||||
bus_write_1(bar->b_res, 0x1000 + (port << 9) + 0x04, | |||||
value); | |||||
} | |||||
else { | |||||
value |= 0x80; | |||||
bus_write_1(bar->b_res, 0x1000 + (port << 9) + 0x04, | |||||
value); | |||||
bus_write_1(bar->b_res, 0x1000 + (port << 9) + 0xC1, | |||||
bres.cpr & 0xff); | |||||
/* Don't change unused bits */ | |||||
value = bus_read_1(bar->b_res, 0x1000 + (port << 9) + 0xC3); | |||||
value &= 0xfe; | |||||
value |= (bres.cpr >> 8) & 1; | |||||
bus_write_1(bar->b_res, 0x1000 + (port << 9) + 0xC3, | |||||
value); | |||||
} | |||||
value = bus_read_1(bar->b_res, 0x1000 + (port << 9) + 0xC2); | |||||
value &= 0xf0; | |||||
if (bres.tcr == 16) | |||||
bus_write_1(bar->b_res, 0x1000 + (port << 9) + 0xC2, value); | |||||
else | |||||
bus_write_1(bar->b_res, 0x1000 + (port << 9) + 0xC2, | |||||
value | (bres.tcr & 0x0f)); | |||||
sc->sc_port[port].p_rclk = 62500000; // Base rclk | |||||
sc->sc_port[port].p_rclk <<= 4; // Assumed tcr | |||||
sc->sc_port[port].p_rclk /= bres.tcr; // Real tcr | |||||
sc->sc_port[port].p_rclk <<= 3; // Prescale units 1/8 | |||||
sc->sc_port[port].p_rclk /= bres.cpr; | |||||
return (0); | |||||
case PUC_CFG_GET_CLOCK: | |||||
*res = 62500000; // Base clock is 62.5MHz | |||||
/* | |||||
* TCR sets the data-bit oversampling... UART driver | |||||
* assumes this is 16, so we scale the clock up if less | |||||
* than 16. | |||||
*/ | |||||
*res <<= 4; | |||||
bar = puc_get_bar(sc, cfg->rid); | |||||
if (bar == NULL) | |||||
return (ENXIO); | |||||
value = bus_read_1(bar->b_res, 0x1000 + (port << 9) + 0xC2); | |||||
value &= 0x0f; | |||||
if (value < 4) | |||||
*res >>= 4; | |||||
else | |||||
*res /= value; | |||||
/* | |||||
* Now check MCR bit 7... if it's set, the prescaler | |||||
* is enabled. | |||||
*/ | |||||
value = bus_read_1(bar->b_res, 0x1000 + (port << 9) + 0x04); | |||||
if ((value & 0x80) == 0) | |||||
return 0; | |||||
/* | |||||
* Read the prescaler configuration | |||||
*/ | |||||
value = bus_read_1(bar->b_res, 0x1000 + (port << 9) + 0xC3); | |||||
value &= 1; | |||||
prescale = value<<8; | |||||
value = bus_read_1(bar->b_res, 0x1000 + (port << 9) + 0xC1); | |||||
prescale |= value; | |||||
if (prescale < 8) | |||||
return ENXIO; | |||||
*res <<= 3; | |||||
*res /= prescale; | |||||
return (0); | return (0); | ||||
default: | default: | ||||
break; | break; | ||||
} | } | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
static int | static int | ||||
▲ Show 20 Lines • Show All 41 Lines • Show Last 20 Lines |