lkml.org 
[lkml]   [2010]   [Nov]   [22]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH] ce4100: Add errata fixes for UART on CE4100
    Date
    From: Dirk Brandewie <dirk.brandewie@gmail.com>

    This patch enables the UART on the CE4100. The UART has a couple of
    issues that need to be worked around. First the UART is mostly PC
    compatible except that it is clocked eight times faster than a
    standard PC so the default configuration provided in
    arch/x86/include/asm/serial.h needs to be overridden. Second the TX
    interrupt may not be set correctly all the time. Lastly accessing the
    UART via I/O space for early_prink() hangs the chip when the IOAPIC is
    enabled.

    A custom mem_serial_in() is provided to work around the TX interrupt
    issue. The configuration issues are dealt with in the call back
    registered with the 8250 driver via serial8250_set_isa_configurator()

    Signed-off-by: Dirk Brandewie <dirk.brandewie@gmail.com>
    ---
    arch/x86/platform/ce4100/ce4100.c | 92 +++++++++++++++++++++++++++++++++++-
    1 files changed, 89 insertions(+), 3 deletions(-)

    diff --git a/arch/x86/platform/ce4100/ce4100.c b/arch/x86/platform/ce4100/ce4100.c
    index b8f1c48..d2c0d51 100644
    --- a/arch/x86/platform/ce4100/ce4100.c
    +++ b/arch/x86/platform/ce4100/ce4100.c
    @@ -12,25 +12,111 @@
    #include <linux/kernel.h>
    #include <linux/irq.h>
    #include <linux/module.h>
    +#include <linux/serial_reg.h>
    +#include <linux/serial_8250.h>

    #include <asm/setup.h>
    -
    +#include <asm/io.h>

    static int ce4100_i8042_detect(void)
    {
    return 0;
    }

    -static void __init sdv_arch_setup(void)
    +static void __init sdv_find_smp_config(void)
    {
    +}
    +
    +#ifdef CONFIG_SERIAL_8250
    +

    +static unsigned int mem_serial_in(struct uart_port *p, int offset)
    +{
    + offset = offset << p->regshift;
    + return readl(p->membase + offset);
    }

    +/*
    + * The UART Tx interrupts are not set under some conditions and therefore serial
    + * transmission hangs. This is a silicon issue and has not been root caused. The
    + * workaround for this silicon issue checks UART_LSR_THRE bit and UART_LSR_TEMT
    + * bit of LSR register in interrupt handler to see whether at least one of these
    + * two bits is set, if so then process the transmit request. If this workaround
    + * is not applied, then the serial transmission may hang. This workaround is for
    + * errata number 9 in Errata - B step.
    +*/

    -static void __init sdv_find_smp_config(void)
    +static unsigned int ce4100_mem_serial_in(struct uart_port *p, int offset)
    +{
    + unsigned int ret, ier, lsr;
    +
    + if (offset == UART_IIR) {
    + offset = offset << p->regshift;
    + ret = readl(p->membase + offset);
    + if (ret & UART_IIR_NO_INT) {
    + /* see if the TX interrupt should have really set */
    + ier = mem_serial_in(p, UART_IER);
    + /* see if the UART's XMIT interrupt is enabled */
    + if (ier & UART_IER_THRI) {
    + lsr = mem_serial_in(p, UART_LSR);
    + /* now check to see if the UART should be
    + generating an interrupt (but isn't) */
    + if (lsr & (UART_LSR_THRE | UART_LSR_TEMT))
    + ret &= ~UART_IIR_NO_INT;
    + }
    + }
    + } else
    + ret = mem_serial_in(p, offset);
    + return ret;
    +}
    +
    +static void ce4100_mem_serial_out(struct uart_port *p, int offset, int value)
    +{
    + offset = offset << p->regshift;
    + writel(value, p->membase + offset);
    +}
    +
    +static void ce4100_serial_fixup(int port, struct uart_port *up,
    + unsigned short *capabilites)
    +{
    +#ifdef CONFIG_EARLY_PRINTK
    + /*
    + * Over ride the legacy port configuration that comes from
    + * asm/serial.h. Using the ioport driver then switching to the
    + * PCI memmaped driver hangs the IOAPIC
    + */
    + if (up->iotype != UPIO_MEM32) {
    + up->uartclk = 14745600;
    + up->mapbase = 0xdffe0200;
    + set_fixmap_nocache(FIX_EARLYCON_MEM_BASE,
    + up->mapbase & PAGE_MASK);
    + up->membase =
    + (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE);
    + up->membase += up->mapbase & ~PAGE_MASK;
    + up->iotype = UPIO_MEM32;
    + up->regshift = 2;
    + }
    +#endif
    + up->iobase = 0;
    + up->serial_in = ce4100_mem_serial_in;
    + up->serial_out = ce4100_mem_serial_out;
    +
    + *capabilites |= (1 << 12);
    +}
    +
    +static __init void sdv_serial_fixup(void)
    {
    + serial8250_set_isa_configurator(ce4100_serial_fixup);
    }

    +#else
    +static inline void sdv_serial_fixup(void);
    +#endif
    +
    +static void __init sdv_arch_setup(void)
    +{
    + sdv_serial_fixup();
    +}

    /*
    * CE4100 specific x86_init function overrides and early setup
    --
    1.7.2.3


    \
     
     \ /
      Last update: 2010-11-22 15:31    [W:0.027 / U:60.612 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site