summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoradrian <adrian@FreeBSD.org>2013-03-30 04:31:29 +0000
committeradrian <adrian@FreeBSD.org>2013-03-30 04:31:29 +0000
commit48f29e1c7d2cb8af093ab1dca16f23ad10575a2c (patch)
treedc7c3fdf3980342993bada011bf940ba566b79cd
parentb3c007b431158819d980e913b81010252d005e10 (diff)
downloadFreeBSD-src-48f29e1c7d2cb8af093ab1dca16f23ad10575a2c.zip
FreeBSD-src-48f29e1c7d2cb8af093ab1dca16f23ad10575a2c.tar.gz
AR933x CPU device improvements:
* Add baud rate and divisor programming code. See below for more information. * Flesh out ar933x_init() to disable interrupts and program the initial console setup. * Remove #if 0'ed code from ar933x_term(). * Explain what these functions do. Now, the baud rate and divisor code comes from Linux, as a submission to the OpenWRT project and Linux kernel from Gabor Juhos <juhosg@openwrt.org>. The original ticket for this code is https://dev.openwrt.org/ticket/12031 . I've contacted Gabor and asked for his permission to also licence the patch in question (which covers this code) to BSD lience and he's agreed. Hence why I'm including it here in FreeBSD. Tested: * AP121 (AR9330)
-rw-r--r--sys/mips/atheros/uart_dev_ar933x.c165
1 files changed, 95 insertions, 70 deletions
diff --git a/sys/mips/atheros/uart_dev_ar933x.c b/sys/mips/atheros/uart_dev_ar933x.c
index ad1736b..a6fbc83 100644
--- a/sys/mips/atheros/uart_dev_ar933x.c
+++ b/sys/mips/atheros/uart_dev_ar933x.c
@@ -147,45 +147,94 @@ ar933x_flush(struct uart_bas *bas, int what)
}
#endif
-#if 0
+/*
+ * Calculate the baud from the given chip configuration parameters.
+ */
+static unsigned long
+ar933x_uart_get_baud(unsigned int clk, unsigned int scale,
+ unsigned int step)
+{
+ uint64_t t;
+ uint32_t div;
+
+ div = (2 << 16) * (scale + 1);
+ t = clk;
+ t *= step;
+ t += (div / 2);
+ t = t / div;
+
+ return (t);
+}
+
+/*
+ * Calculate the scale/step with the lowest possible deviation from
+ * the target baudrate.
+ */
+static void
+ar933x_uart_get_scale_step(struct uart_bas *bas, unsigned int baud,
+ unsigned int *scale, unsigned int *step)
+{
+ unsigned int tscale;
+ uint32_t clk;
+ long min_diff;
+
+ clk = bas->rclk;
+ *scale = 0;
+ *step = 0;
+
+ min_diff = baud;
+ for (tscale = 0; tscale < AR933X_UART_MAX_SCALE; tscale++) {
+ uint64_t tstep;
+ int diff;
+
+ tstep = baud * (tscale + 1);
+ tstep *= (2 << 16);
+ tstep = tstep / clk;
+
+ if (tstep > AR933X_UART_MAX_STEP)
+ break;
+
+ diff = abs(ar933x_uart_get_baud(clk, tscale, tstep) - baud);
+ if (diff < min_diff) {
+ min_diff = diff;
+ *scale = tscale;
+ *step = tstep;
+ }
+ }
+}
+
static int
ar933x_param(struct uart_bas *bas, int baudrate, int databits, int stopbits,
int parity)
{
- int divisor;
- uint8_t lcr;
-
- lcr = 0;
- if (databits >= 8)
- lcr |= LCR_8BITS;
- else if (databits == 7)
- lcr |= LCR_7BITS;
- else if (databits == 6)
- lcr |= LCR_6BITS;
- else
- lcr |= LCR_5BITS;
- if (stopbits > 1)
- lcr |= LCR_STOPB;
- lcr |= parity << 3;
-
- /* Set baudrate. */
+ /* UART always 8 bits */
+
+ /* UART always 1 stop bit */
+
+ /* UART parity is controllable by bits 0:1, ignore for now */
+
+ /* Set baudrate if required. */
if (baudrate > 0) {
- divisor = ar933x_divisor(bas->rclk, baudrate);
- if (divisor == 0)
- return (EINVAL);
- uart_setreg(bas, REG_LCR, lcr | LCR_DLAB);
- uart_barrier(bas);
- uart_setreg(bas, REG_DLL, divisor & 0xff);
- uart_setreg(bas, REG_DLH, (divisor >> 8) & 0xff);
- uart_barrier(bas);
+ uint32_t clock_scale, clock_step;
+
+ /* Find the best fit for the given baud rate */
+ ar933x_uart_get_scale_step(bas, baudrate, &clock_scale,
+ &clock_step);
+
+ /*
+ * Program the clock register in its entirety - no need
+ * for Read-Modify-Write.
+ */
+ ar933x_setreg(bas, AR933X_UART_CLOCK_REG,
+ ((clock_scale & AR933X_UART_CLOCK_SCALE_M)
+ << AR933X_UART_CLOCK_SCALE_S) |
+ (clock_step & AR933X_UART_CLOCK_STEP_M));
}
- /* Set LCR and clear DLAB. */
- uart_setreg(bas, REG_LCR, lcr);
uart_barrier(bas);
return (0);
}
-#endif
+
/*
* Low-level UART interface.
@@ -209,23 +258,8 @@ static struct uart_ops uart_ar933x_ops = {
static int
ar933x_probe(struct uart_bas *bas)
{
-#if 0
- u_char val;
- /* Check known 0 bits that don't depend on DLAB. */
- val = uart_getreg(bas, REG_IIR);
- if (val & 0x30)
- return (ENXIO);
- /*
- * Bit 6 of the MCR (= 0x40) appears to be 1 for the Sun1699
- * chip, but otherwise doesn't seem to have a function. In
- * other words, uart(4) works regardless. Ignore that bit so
- * the probe succeeds.
- */
- val = uart_getreg(bas, REG_MCR);
- if (val & 0xa0)
- return (ENXIO);
-#endif
+ /* We always know this will be here */
return (0);
}
@@ -233,43 +267,34 @@ static void
ar933x_init(struct uart_bas *bas, int baudrate, int databits, int stopbits,
int parity)
{
-#if 0
- u_char ier;
+ uint32_t reg;
- if (bas->rclk == 0)
- bas->rclk = DEFAULT_RCLK;
+ /* Setup default parameters */
ar933x_param(bas, baudrate, databits, stopbits, parity);
- /* Disable all interrupt sources. */
- /*
- * We use 0xe0 instead of 0xf0 as the mask because the XScale PXA
- * UARTs split the receive time-out interrupt bit out separately as
- * 0x10. This gets handled by ier_mask and ier_rxbits below.
- */
- ier = uart_getreg(bas, REG_IER) & 0xe0;
- uart_setreg(bas, REG_IER, ier);
- uart_barrier(bas);
+ /* XXX Force enable UART in case it was disabled */
- /* Disable the FIFO (if present). */
- uart_setreg(bas, REG_FCR, 0);
- uart_barrier(bas);
+ /* Disable all interrupts */
+ ar933x_setreg(bas, AR933X_UART_INT_EN_REG, 0x00000000);
+
+ /* Disable the host interrupt */
+ reg = ar933x_getreg(bas, AR933X_UART_CS_REG);
+ reg &= ~AR933X_UART_CS_HOST_INT_EN;
+ ar933x_setreg(bas, AR933X_UART_CS_REG, reg);
- /* Set RTS & DTR. */
- uart_setreg(bas, REG_MCR, MCR_IE | MCR_RTS | MCR_DTR);
uart_barrier(bas);
- ar933x_clrint(bas);
-#endif
+ /* XXX Set RTS/DTR? */
}
+/*
+ * Detach from console.
+ */
static void
ar933x_term(struct uart_bas *bas)
{
-#if 0
- /* Clear RTS & DTR. */
- uart_setreg(bas, REG_MCR, MCR_IE);
- uart_barrier(bas);
-#endif
+
+ /* XXX TODO */
}
static void
OpenPOWER on IntegriCloud