[PATCH][RFC] arm: Add basic support for VIA/WonderMedia SoC's
Alexey Charkov
alchark at gmail.com
Mon Jul 12 13:41:56 EDT 2010
Good night Marek, and thanks for looking at the patch!
I will drop other pieces of the patch to reduce traffic and only
retain those with your comments below:
2010/7/12 Marek Vasut <marek.vasut at gmail.com>:
> Dne Po 12. července 2010 18:50:25 Alexey Charkov napsal(a):
>> diff --git a/arch/arm/mach-vt8500/include/mach/io.h
>> b/arch/arm/mach-vt8500/include/mach/io.h new file mode 100644
>> index 0000000..dc2181a
>> --- /dev/null
>> +++ b/arch/arm/mach-vt8500/include/mach/io.h
>> @@ -0,0 +1,28 @@
>> +/*
>> + * arch/arm/mach-vt8500/include/mach/io.h
>> + *
>> + * Copyright (C) 2003 ARM Limited
>
> Really? You just copied it ?
>
Yes, this is straight from mach-versatile.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
>> USA + */
>> +#ifndef __ASM_ARM_ARCH_IO_H
>> +#define __ASM_ARM_ARCH_IO_H
>> +
>> +#define IO_SPACE_LIMIT 0xffffffff
>> +
>> +#define __io(a) __typesafe_io(a)
>> +#define __mem_pci(a) (a)
>> +
>> +#endif
>> diff --git a/arch/arm/mach-vt8500/include/mach/memory.h
>> b/arch/arm/mach-vt8500/include/mach/memory.h new file mode 100644
>> index 0000000..175f914
>> --- /dev/null
>> +++ b/arch/arm/mach-vt8500/include/mach/memory.h
>> @@ -0,0 +1,28 @@
>> +/*
>> + * arch/arm/mach-vt8500/include/mach/memory.h
>> + *
>> + * Copyright (C) 2003 ARM Limited
>
> DTTO ?
>
Yep, again mach-versatile
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
>> USA + */
>> +#ifndef __ASM_ARCH_MEMORY_H
>> +#define __ASM_ARCH_MEMORY_H
>> +
>> +/*
>> + * Physical DRAM offset.
>> + */
>> +#define PHYS_OFFSET UL(0x00000000)
>> +
>> +#endif
>> diff --git a/arch/arm/mach-vt8500/include/mach/timex.h
>> b/arch/arm/mach-vt8500/include/mach/timex.h new file mode 100644
>> index 0000000..ff209d8
>> --- /dev/null
>> +++ b/arch/arm/mach-vt8500/include/mach/timex.h
>> @@ -0,0 +1,21 @@
>> +/*
>> + * arch/arm/mach-vt8500/include/mach/timex.h
>> + *
>> + * Copyright (C) 2010 Alexey Charkov <alchark at gmail.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
>> USA + */
>> +
>
> #ifndef MACH_TIMEX
> #define MACH_TIMEX
>
>> +#define CLOCK_TICK_RATE (3000000)
>
> #endif
>
> Maybe ?
>
Looks reasonable, shall I add those checks? Frankly, I have not found
out where it is used, yet... Neither Versatile nor Realview have
ifndefs here.
>> diff --git a/arch/arm/mach-vt8500/include/mach/uncompress.h
>> b/arch/arm/mach-vt8500/include/mach/uncompress.h new file mode 100644
>> index 0000000..3f14bf1
>> --- /dev/null
>> +++ b/arch/arm/mach-vt8500/include/mach/uncompress.h
>> @@ -0,0 +1,46 @@
>> +/* arch/arm/mach-vt8500/include/mach/uncompress.h
>> + *
>> + * Copyright (C) 2010 Alexey Charkov <alchark at gmail.com>
>> + *
>> + * Based on arch/arm/mach-dove/include/mach/uncompress.h
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + *
>> + */
>> +
>> +#include <mach/vt8500.h>
>> +
>> +#define UART_THR ((volatile unsigned char *)(VT8500_UART0_BASE + 0x0))
>> +#define UART_LSR ((volatile unsigned char *)(VT8500_UART0_BASE + 0x24))
>
> Can't you use some better accessors here ?
This one just follows mach-dove. Shall I change these to inline
function helpers?
>> +
>> +#define LSR_THRE 0x1f
>> +
>> +static void putc(const char c)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < 0x1000; i++) {
>> + /* Transmit fifo not full? */
>> + if (*UART_LSR & LSR_THRE)
>> + break;
>> + }
>> +
>> + *UART_THR = c;
>> +}
>> +
>> +static void flush(void)
>> +{
>> +}
>> +
>> +/*
>> + * nothing to do
>> + */
>> +#define arch_decomp_setup()
>> +#define arch_decomp_wdog()
>> diff --git a/arch/arm/mach-vt8500/timer.c b/arch/arm/mach-vt8500/timer.c
>> new file mode 100644
>> index 0000000..817edc8
>> --- /dev/null
>> +++ b/arch/arm/mach-vt8500/timer.c
>> @@ -0,0 +1,185 @@
>> +/*
>> + * arch/arm/mach-vt8500/timer.c
>> + *
>> + * Copyright (C) 2010 Alexey Charkov <alchark at gmail.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
>> USA + */
>> +
>> +#include <linux/irq.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/clocksource.h>
>> +#include <linux/clockchips.h>
>> +#include <linux/cnt32_to_63.h>
>> +
>> +#include <asm/mach/time.h>
>> +
>> +#include <mach/hardware.h>
>> +#include <mach/irqs.h>
>> +#include <mach/vt8500.h>
>> +
>> +#define VT8500_TIMER_OFFSET 0x0100
>> +#define TIMER_MATCH_VAL 0x0000
>> +#define TIMER_COUNT_VAL 0x0010
>> +#define TIMER_STATUS_VAL 0x0014
>> +#define TIMER_IER_VAL 0x001c /* interrupt enable */
>> +#define TIMER_CTRL_VAL 0x0020
>> +#define TIMER_AS_VAL 0x0024 /* access status */
>> +#define TIMER_COUNT_R_ACTIVE (1 << 5) /* not ready for read */
>> +#define TIMER_COUNT_W_ACTIVE (1 << 4) /* not ready for write */
>> +#define TIMER_MATCH_W_ACTIVE (1 << 0) /* not ready for write */
>> +#define VT8500_TIMER_HZ 3000000
>> +void __iomem* regbase = (void __iomem *)(IO_ADDRESS(VT8500_PMC_BASE)
>> + + VT8500_TIMER_OFFSET);
>> +
>> +unsigned long long sched_clock(void)
>> +{
>> + unsigned long long v;
>> +
>> + writel(0x3, regbase + TIMER_CTRL_VAL);
>> + while (readl(regbase + TIMER_AS_VAL) & TIMER_COUNT_R_ACTIVE)
>> + /* wait and poll */;
>> +
>> + v = cnt32_to_63(readl(regbase + TIMER_COUNT_VAL));
>> +
>> + /* the <<1 gets rid of the cnt_32_to_63 top bit saving on a bic insn*/
>> + v *= 1000<<1; /* 3MHz timer tick implies 333ns resolution */
>> + do_div(v, 3<<1);
>
> Did you run checkpatch here ... or on this patch ?
>
Yes, I did, and fixed all positives except for the volatile pointers
in accessors of uncompress.h that you've commented on above. As I have
no idea why those keywords were added in the first place, I decided to
retain them.
>> +
>> + return v;
>> +}
>> +
>> +static int vt8500_timer_set_next_event(unsigned long cycles,
>> + struct clock_event_device *evt)
>> +{
>> + unsigned long now, alarm;
>> + int late;
>> +
>> + writel(3, regbase + TIMER_CTRL_VAL);
>> + while (readl(regbase + TIMER_AS_VAL) & TIMER_COUNT_R_ACTIVE)
>> + /* wait and poll */;
>> + now = readl(regbase + TIMER_COUNT_VAL);
>> + alarm = now + cycles;
>> + while (readl(regbase + TIMER_AS_VAL) & TIMER_MATCH_W_ACTIVE)
>> + /* wait and poll */;
>> + writel(alarm, regbase + TIMER_MATCH_VAL);
>> +
>> + writel(3, regbase + TIMER_CTRL_VAL);
>> + while (readl(regbase + TIMER_AS_VAL) & TIMER_COUNT_R_ACTIVE)
>> + /* wait and poll */;
>> + now = readl(regbase + TIMER_COUNT_VAL);
>> + late = now - alarm;
>> + if (late >= (-2) && late < VT8500_TIMER_HZ*5)
>> + return -ETIME;
>> +
>> + writel(1, regbase + TIMER_IER_VAL);
>> +
>> + return 0;
>> +}
>> +
>> +static void vt8500_timer_set_mode(enum clock_event_mode mode,
>> + struct clock_event_device *evt)
>> +{
>> + switch (mode) {
>> + case CLOCK_EVT_MODE_RESUME:
>> + case CLOCK_EVT_MODE_PERIODIC:
>> + break;
>> + case CLOCK_EVT_MODE_ONESHOT:
>> + writel(readl(regbase + TIMER_CTRL_VAL) | 1,
>> + regbase + TIMER_CTRL_VAL);
>> + writel(0, regbase + TIMER_IER_VAL);
>> + break;
>> + case CLOCK_EVT_MODE_UNUSED:
>> + case CLOCK_EVT_MODE_SHUTDOWN:
>> + writel(readl(regbase + TIMER_CTRL_VAL) & ~1,
>> + regbase + TIMER_CTRL_VAL);
>> + break;
>> + }
>> +}
>> +
>> +struct clock_event_device clockevent = {
>> + .name = "vt8500_timer",
>> + .features = CLOCK_EVT_FEAT_ONESHOT,
>> + .rating = 200,
>> + .set_next_event = vt8500_timer_set_next_event,
>> + .set_mode = vt8500_timer_set_mode,
>> +};
>> +
>> +static irqreturn_t vt8500_timer_interrupt(int irq, void *dev_id)
>> +{
>> + struct clock_event_device *evt = dev_id;
>> + evt->event_handler(evt);
>> + writel(0xf, regbase + TIMER_STATUS_VAL);
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> +static cycle_t vt8500_timer_read(struct clocksource *cs)
>> +{
>> + writel(3, regbase + TIMER_CTRL_VAL);
>> + while (readl((regbase + TIMER_AS_VAL)) & TIMER_COUNT_R_ACTIVE)
>> + /* wait and poll */;
>> + return readl(regbase + TIMER_COUNT_VAL);
>> +}
>> +
>> +struct clocksource clocksource = {
>> + .name = "vt8500_timer",
>> + .rating = 200,
>> + .read = vt8500_timer_read,
>> + .mask = CLOCKSOURCE_MASK(32),
>> + .flags = CLOCK_SOURCE_IS_CONTINUOUS,
>> +};
>> +struct irqaction irq = {
>> + .name = "vt8500_timer",
>> + .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
>> + .handler = vt8500_timer_interrupt,
>> + .dev_id = &clockevent,
>> +};
>> +
>> +static void __init vt8500_timer_init(void)
>> +{
>> + int res;
>> +
>> + struct clock_event_device *ce = &clockevent;
>> + struct clocksource *cs = &clocksource;
>> + writel(1, regbase + TIMER_CTRL_VAL);
>> + writel(0xf, regbase + TIMER_STATUS_VAL);
>> + writel(~0, regbase + TIMER_MATCH_VAL);
>> +
>> + clockevents_calc_mult_shift(ce, VT8500_TIMER_HZ, 4);
>> + clocksource_calc_mult_shift(cs, VT8500_TIMER_HZ, 4);
>> +
>> + /* copy-pasted from mach-msm; no idea */
>> + ce->max_delta_ns =
>> + clockevent_delta2ns(0xf0000000, ce);
>> + ce->min_delta_ns = clockevent_delta2ns(4, ce);
>> + ce->cpumask = cpumask_of(0);
>> +
>> + res = clocksource_register(cs);
>> + if (res)
>> + printk(KERN_ERR "vt8500_timer_init: clocksource_register "
>> + "failed for %s\n", cs->name);
>> +
>> + res = setup_irq(IRQ_PMCOS0, &irq);
>> +
>> + if (res)
>> + printk(KERN_ERR "vt8500_timer_init: setup_irq "
>> + "failed for %s\n", cs->name);
>> + clockevents_register_device(ce);
>> +}
>> +
>> +struct sys_timer vt8500_timer = {
>> + .init = vt8500_timer_init
>> +};
>> diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types
>> index 8f10d24..f5745d1 100644
>> --- a/arch/arm/tools/mach-types
>> +++ b/arch/arm/tools/mach-types
>
> This should be dropped but you probably know that :)
>
Ok, no problem :)
>> diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
>> index 8b23165..5993659 100644
>> --- a/drivers/serial/Kconfig
>> +++ b/drivers/serial/Kconfig
>> @@ -1351,6 +1351,16 @@ config SERIAL_MSM_CONSOLE
>> depends on SERIAL_MSM=y
>> select SERIAL_CORE_CONSOLE
>>
>> +config SERIAL_VT8500
>> + bool "VIA VT8500 on-chip serial port support"
>> + depends on ARM && ARCH_VT8500
>> + select SERIAL_CORE
>> +
>> +config SERIAL_VT8500_CONSOLE
>> + bool "VIA VT8500 serial console support"
>> + depends on SERIAL_VT8500=y
>> + select SERIAL_CORE_CONSOLE
>> +
>> config SERIAL_NETX
>> tristate "NetX serial port support"
>> depends on ARM && ARCH_NETX
>> diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
>> index 208a855..0e8f7b0 100644
>> --- a/drivers/serial/Makefile
>> +++ b/drivers/serial/Makefile
>> @@ -84,3 +84,4 @@ obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o
>> obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o
>> obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o
>> obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o
>> +obj-$(CONFIG_SERIAL_VT8500) += vt8500_serial.o
>
> Possibly move the serial driver into another patch ... split this into patch
> series :)
>
I'll probably try to. There are some traces of serial in the platform
files as well, though.
>> diff --git a/drivers/serial/vt8500_serial.c
>> b/drivers/serial/vt8500_serial.c new file mode 100644
>> index 0000000..21e0462
>> --- /dev/null
>> +++ b/drivers/serial/vt8500_serial.c
>> @@ -0,0 +1,612 @@
>> +/*
>> + * drivers/serial/vt8500_serial.c
>> + *
>> + * Copyright (C) 2010 Alexey Charkov <alchark at gmail.com>
>> + *
>> + * Based on msm_serial.c, which is:
>> + * Copyright (C) 2007 Google, Inc.
>> + * Author: Robert Love <rlove at google.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#if defined(CONFIG_SERIAL_VT8500_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
>> +# define SUPPORT_SYSRQ
>> +#endif
>> +
>> +#include <linux/hrtimer.h>
>> +#include <linux/module.h>
>> +#include <linux/io.h>
>> +#include <linux/ioport.h>
>> +#include <linux/irq.h>
>> +#include <linux/init.h>
>> +#include <linux/console.h>
>> +#include <linux/tty.h>
>> +#include <linux/tty_flip.h>
>> +#include <linux/serial_core.h>
>> +#include <linux/serial.h>
>> +#include <linux/clk.h>
>> +#include <linux/platform_device.h>
>> +
>> +#include <mach/vt8500.h>
>> +
>> +struct vt8500_port {
>> + struct uart_port uart;
>> + char name[16];
>> + struct clk *clk;
>> + unsigned int ier;
>> +};
>> +
>> +#define UART_TO_VT8500(uart_port) ((struct vt8500_port *) uart_port)
>> +
>> +static inline void vt8500_write(struct uart_port *port, unsigned int val,
>> + unsigned int off)
>> +{
>> + __raw_writel(val, port->membase + off);
>> +}
>> +
>> +static inline unsigned int vt8500_read(struct uart_port *port, unsigned
>> int off) +{
>> + return __raw_readl(port->membase + off);
>> +}
>> +
>> +static void vt8500_stop_tx(struct uart_port *port)
>> +{
>> + struct vt8500_port *vt8500_port = UART_TO_VT8500(port);
>> +
>> + vt8500_port->ier &= ~1;
>> + vt8500_write(port, vt8500_port->ier, VT8500_URIER);
>> +}
>> +
>> +static void vt8500_start_tx(struct uart_port *port)
>> +{
>> + struct vt8500_port *vt8500_port = UART_TO_VT8500(port);
>> +
>> + vt8500_port->ier |= 1;
>> + vt8500_write(port, vt8500_port->ier, VT8500_URIER);
>> +}
>> +
>> +static void vt8500_stop_rx(struct uart_port *port)
>> +{
>> + struct vt8500_port *vt8500_port = UART_TO_VT8500(port);
>> +
>> + vt8500_port->ier &= ~2;
>> + vt8500_write(port, vt8500_port->ier, VT8500_URIER);
>> +}
>> +
>> +static void vt8500_enable_ms(struct uart_port *port)
>> +{
>> + struct vt8500_port *vt8500_port = UART_TO_VT8500(port);
>> +
>> + vt8500_port->ier |= (1 << 10);
>> + vt8500_write(port, vt8500_port->ier, VT8500_URIER);
>> +}
>
> Won't "inline" help here performance-wise?
>
Quite possibly. Doesn't GCC auto-inline it with -O2/-Os, though?
Should I add "inline" explicitly?
>> +
>> +static void handle_rx(struct uart_port *port)
>> +{
>> + struct tty_struct *tty = port->state->port.tty;
>> +
>> + /*
>> + * Handle overrun
>> + */
>> + if ((vt8500_read(port, VT8500_URISR) & (1 << 7))) {
>> + port->icount.overrun++;
>> + tty_insert_flip_char(tty, 0, TTY_OVERRUN);
>> + }
>> +
>> + /* and now the main RX loop */
>> + while (vt8500_read(port, VT8500_URUSR) & (1 << 3)) {
>> + unsigned int c;
>> + char flag = TTY_NORMAL;
>> +
>> + c = vt8500_read(port, VT8500_URRDR);
>> +
>> + /* Mask conditions we're ignorning. */
>> + c &= ~port->read_status_mask;
>> +
>> + if (c & (1 << 9)) {
>> + port->icount.frame++;
>> + flag = TTY_FRAME;
>> + } else if (c & (1 << 8)) {
>> + port->icount.parity++;
>> + flag = TTY_PARITY;
>> + } else
>> + port->icount.rx++;
>> +
>> + if (!uart_handle_sysrq_char(port, c))
>> + tty_insert_flip_char(tty, c, flag);
>> + }
>> +
>> + tty_flip_buffer_push(tty);
>> +}
>> +
>> +static void handle_tx(struct uart_port *port)
>> +{
>> + struct circ_buf *xmit = &port->state->xmit;
>> + struct vt8500_port *vt8500_port = UART_TO_VT8500(port);
>> + int sent_tx;
>> +
>> + if (port->x_char) {
>> + vt8500_write(port, port->x_char, VT8500_URTDR);
>> + port->icount.tx++;
>> + port->x_char = 0;
>> + }
>> +
>> + while (!(vt8500_read(port, VT8500_URUSR) & 2)) {
>> + if (uart_circ_empty(xmit)) {
>> + /* disable tx interrupts */
>> + vt8500_port->ier &= ~1;
>> + vt8500_write(port, vt8500_port->ier, VT8500_URIER);
>> + break;
>> + }
>> +
>> + vt8500_write(port, xmit->buf[xmit->tail], VT8500_URTDR);
>> +
>> + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
>> + port->icount.tx++;
>> + sent_tx = 1;
>> + }
>> +
>> + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
>> + uart_write_wakeup(port);
>> +}
>> +
>> +static void handle_delta_cts(struct uart_port *port)
>> +{
>> + port->icount.cts++;
>> + wake_up_interruptible(&port->state->port.delta_msr_wait);
>> +}
>> +
>> +static irqreturn_t vt8500_irq(int irq, void *dev_id)
>> +{
>> + struct uart_port *port = dev_id;
>> + struct vt8500_port *vt8500_port = UART_TO_VT8500(port);
>> + unsigned int isr;
>> +
>> + spin_lock(&port->lock);
>> + isr = vt8500_read(port, VT8500_URISR);
>> + vt8500_write(port, 0, VT8500_URIER); /* disable interrupt */
>> +
>> + if (isr & 2)
>> + handle_rx(port);
>> + if (isr & 1)
>> + handle_tx(port);
>> + if (isr & (1 << 10))
>> + handle_delta_cts(port);
>> +
>> + /* restore interrupt */
>> + vt8500_write(port, vt8500_port->ier, VT8500_URIER);
>> + spin_unlock(&port->lock);
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> +static unsigned int vt8500_tx_empty(struct uart_port *port)
>> +{
>> + return (vt8500_read(port, VT8500_URUSR) & 2) ? 0 : TIOCSER_TEMT;
>> +}
>> +
>> +static unsigned int vt8500_get_mctrl(struct uart_port *port)
>> +{
>> + return TIOCM_CTS | TIOCM_RTS;
>> +}
>> +
>> +static void vt8500_set_mctrl(struct uart_port *port, unsigned int mctrl)
>> +{
>> + unsigned int lcr;
>> +
>> + lcr = vt8500_read(port, VT8500_URLCR);
>> +
>> + if (!(mctrl & TIOCM_RTS)) {
>> + lcr &= ~(1 << 6);
>> + vt8500_write(port, lcr, VT8500_URLCR);
>> + } else {
>> + lcr |= (1 << 6);
>> + vt8500_write(port, lcr, VT8500_URLCR);
>> + }
>> +}
>> +
>> +static void vt8500_break_ctl(struct uart_port *port, int break_ctl)
>> +{
>> + if (break_ctl)
>> + vt8500_write(port, vt8500_read(port, VT8500_URLCR) | (1 << 9),
>> + VT8500_URLCR);
>> +}
>> +
>> +static int vt8500_set_baud_rate(struct uart_port *port, unsigned int baud)
>> +{
>> + unsigned int div = vt8500_read(port, VT8500_URDIV) & ~(0x3ff);
>> +
>> + if (unlikely((baud < 900) || (baud > 921600)))
>> + div |= 7;
>> + else
>> + div |= (921600 / baud) - 1;
>> +
>> + while (vt8500_read(port, VT8500_URUSR) & (1 << 5))
>> + ;
>> + vt8500_write(port, div, VT8500_URDIV);
>> +
>> + return baud;
>> +}
>> +
>> +static int vt8500_startup(struct uart_port *port)
>> +{
>> + struct vt8500_port *vt8500_port = UART_TO_VT8500(port);
>> + int ret;
>> +
>> + snprintf(vt8500_port->name, sizeof(vt8500_port->name),
>> + "vt8500_serial%d", port->line);
>> +
>> + ret = request_irq(port->irq, vt8500_irq, IRQF_TRIGGER_HIGH,
>> + vt8500_port->name, port);
>> + if (unlikely(ret))
>> + return ret;
>> +
>> + vt8500_write(port, 0x03, VT8500_URLCR); /* enable TX & RX */
>> +
>> + /* turn on RX and CTS interrupts */
>> + vt8500_port->ier = (2) | (1 << 10);
>> + vt8500_write(port, vt8500_port->ier, VT8500_URIER);
>> +
>> + return 0;
>> +}
>> +
>> +static void vt8500_shutdown(struct uart_port *port)
>> +{
>> + struct vt8500_port *vt8500_port = UART_TO_VT8500(port);
>> +
>> + vt8500_port->ier = 0;
>> + vt8500_write(port, 0, VT8500_URIER); /* disable interrupts */
>> +
>> + free_irq(port->irq, port);
>> +}
>> +
>> +static void vt8500_set_termios(struct uart_port *port, struct ktermios
>> *termios, + struct ktermios *old)
>> +{
>> + unsigned long flags;
>> + unsigned int baud, lcr;
>> +
>> + spin_lock_irqsave(&port->lock, flags);
>> +
>> + /* calculate and set baud rate */
>> + baud = uart_get_baud_rate(port, termios, old, 900, 921600);
>> + baud = vt8500_set_baud_rate(port, baud);
>> + if (tty_termios_baud_rate(termios))
>> + tty_termios_encode_baud_rate(termios, baud, baud);
>> +
>> + /* calculate parity */
>> + lcr = vt8500_read(port, VT8500_URLCR);
>> + lcr &= ~((1 << 5) | (1 << 4));
>> + if (termios->c_cflag & PARENB) {
>> + lcr |= (1 << 4);
>> + if (termios->c_cflag & PARODD)
>> + lcr |= (1 << 5);
>> + }
>> +
>> + /* calculate bits per char */
>> + lcr &= ~(1 << 2);
>> + switch (termios->c_cflag & CSIZE) {
>> + case CS7:
>> + break;
>> + case CS8:
>> + default:
>> + lcr |= (1 << 2);
>> + break;
>> + }
>> +
>> + /* calculate stop bits */
>> + lcr &= ~(1 << 3);
>> + if (termios->c_cflag & CSTOPB)
>> + lcr |= (1 << 3);
>> +
>> + /* set parity, bits per char, and stop bit */
>> + vt8500_write(port, lcr, VT8500_URLCR);
>> +
>> + /* Configure status bits to ignore based on termio flags. */
>> + port->read_status_mask = 0;
>> + if (termios->c_iflag & INPCK)
>> + port->read_status_mask |= (1 << 9) | (1 << 8);
>> +
>> + uart_update_timeout(port, termios->c_cflag, baud);
>> +
>> + spin_unlock_irqrestore(&port->lock, flags);
>> +}
>> +
>> +static const char *vt8500_type(struct uart_port *port)
>> +{
>> + return "VT8500";
>> +}
>> +
>> +static void vt8500_release_port(struct uart_port *port)
>> +{
>> + struct platform_device *pdev = to_platform_device(port->dev);
>> + struct resource *resource;
>> + resource_size_t size;
>> +
>> + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + if (unlikely(!resource))
>> + return;
>> + size = resource->end - resource->start + 1;
>> +
>> + release_mem_region(port->mapbase, size);
>> + iounmap(port->membase);
>> + port->membase = NULL;
>> +}
>> +
>> +static int vt8500_request_port(struct uart_port *port)
>> +{
>> + struct platform_device *pdev = to_platform_device(port->dev);
>> + struct resource *resource;
>> + resource_size_t size;
>> +
>> + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + if (unlikely(!resource))
>> + return -ENXIO;
>> + size = resource->end - resource->start + 1;
>> +
>> + if (unlikely(!request_mem_region(port->mapbase, size, "vt8500_serial")))
>> + return -EBUSY;
>> +
>> + port->membase = ioremap(port->mapbase, size);
>> + if (!port->membase) {
>> + release_mem_region(port->mapbase, size);
>> + return -EBUSY;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static void vt8500_config_port(struct uart_port *port, int flags)
>> +{
>> + if (flags & UART_CONFIG_TYPE) {
>> + port->type = PORT_VT8500;
>> + vt8500_request_port(port);
>> + }
>> +}
>> +
>> +static int vt8500_verify_port(struct uart_port *port, struct serial_struct
>> *ser) +{
>> + if (unlikely(ser->type != PORT_UNKNOWN && ser->type != PORT_VT8500))
>> + return -EINVAL;
>> + if (unlikely(port->irq != ser->irq))
>> + return -EINVAL;
>> + return 0;
>> +}
>> +
>> +static struct uart_ops vt8500_uart_pops = {
>> + .tx_empty = vt8500_tx_empty,
>> + .set_mctrl = vt8500_set_mctrl,
>> + .get_mctrl = vt8500_get_mctrl,
>> + .stop_tx = vt8500_stop_tx,
>> + .start_tx = vt8500_start_tx,
>> + .stop_rx = vt8500_stop_rx,
>> + .enable_ms = vt8500_enable_ms,
>> + .break_ctl = vt8500_break_ctl,
>> + .startup = vt8500_startup,
>> + .shutdown = vt8500_shutdown,
>> + .set_termios = vt8500_set_termios,
>> + .type = vt8500_type,
>> + .release_port = vt8500_release_port,
>> + .request_port = vt8500_request_port,
>> + .config_port = vt8500_config_port,
>> + .verify_port = vt8500_verify_port,
>> +};
>> +
>> +static struct vt8500_port vt8500_uart_ports[] = {
>> + {
>> + .uart = {
>> + .iotype = UPIO_MEM,
>> + .ops = &vt8500_uart_pops,
>> + .flags = UPF_BOOT_AUTOCONF,
>> + .fifosize = 16,
>> + .line = 0,
>> + },
>> + },
>> + {
>> + .uart = {
>> + .iotype = UPIO_MEM,
>> + .ops = &vt8500_uart_pops,
>> + .flags = UPF_BOOT_AUTOCONF,
>> + .fifosize = 16,
>> + .line = 1,
>> + },
>> + },
>> + {
>> + .uart = {
>> + .iotype = UPIO_MEM,
>> + .ops = &vt8500_uart_pops,
>> + .flags = UPF_BOOT_AUTOCONF,
>> + .fifosize = 16,
>> + .line = 2,
>> + },
>> + },
>> + {
>> + .uart = {
>> + .iotype = UPIO_MEM,
>> + .ops = &vt8500_uart_pops,
>> + .flags = UPF_BOOT_AUTOCONF,
>> + .fifosize = 16,
>> + .line = 3,
>> + },
>> + },
>> +};
>> +
>> +#define UART_NR ARRAY_SIZE(vt8500_uart_ports)
>> +
>> +static inline struct uart_port *get_port_from_line(unsigned int line)
>> +{
>> + return &vt8500_uart_ports[line].uart;
>> +}
>> +
>> +#ifdef CONFIG_SERIAL_VT8500_CONSOLE
>> +
>> +static void vt8500_console_putchar(struct uart_port *port, int c)
>> +{
>> + while (vt8500_read(port, VT8500_URUSR) & 2) /* Transmitter busy */
>> + ;
>> + vt8500_write(port, c, VT8500_URTDR);
>> +}
>> +
>> +static void vt8500_console_write(struct console *co, const char *s,
>> + unsigned int count)
>> +{
>> + struct uart_port *port;
>> + struct vt8500_port *vt8500_port;
>> +
>> + BUG_ON(co->index < 0 || co->index >= UART_NR);
>> +
>> + port = get_port_from_line(co->index);
>> + vt8500_port = UART_TO_VT8500(port);
>> +
>> + spin_lock(&port->lock);
>> + uart_console_write(port, s, count, vt8500_console_putchar);
>> + spin_unlock(&port->lock);
>> +}
>> +
>> +static int __init vt8500_console_setup(struct console *co, char *options)
>> +{
>> + struct uart_port *port;
>> + int baud, flow, bits, parity;
>> + unsigned int tmp;
>> +
>> + if (unlikely(co->index >= UART_NR || co->index < 0))
>> + return -ENXIO;
>> +
>> + port = get_port_from_line(co->index);
>> +
>> + if (unlikely(!port->membase))
>> + return -ENXIO;
>> +
>> + port->cons = co;
>> +
>> + if (options)
>> + uart_parse_options(options, &baud, &parity, &bits, &flow);
>> +
>> + bits = 8;
>> + parity = 'n';
>> + flow = 'n';
>> + tmp = vt8500_read(port, VT8500_URLCR) & ~((1 << 5) | (1 << 4));
>> + vt8500_write(port, tmp, VT8500_URLCR); /* 8N1 */
>> +
>> + if (baud < 28800 || baud > 921600)
>> + baud = 115200;
>> + vt8500_set_baud_rate(port, baud);
>> +
>> + printk(KERN_INFO "vt8500_serial: console setup on port #%d\n",
>> + port->line);
>> +
>> + return uart_set_options(port, co, baud, parity, bits, flow);
>> +}
>> +
>> +static struct uart_driver vt8500_uart_driver;
>> +
>> +static struct console vt8500_console = {
>> + .name = "ttyS",
>> + .write = vt8500_console_write,
>> + .device = uart_console_device,
>> + .setup = vt8500_console_setup,
>> + .flags = CON_PRINTBUFFER,
>> + .index = -1,
>> + .data = &vt8500_uart_driver,
>> +};
>> +
>> +#define VT8500_CONSOLE (&vt8500_console)
>> +
>> +#else
>> +#define VT8500_CONSOLE NULL
>> +#endif
>> +
>> +static struct uart_driver vt8500_uart_driver = {
>> + .owner = THIS_MODULE,
>> + .driver_name = "vt8500_serial",
>> + .dev_name = "ttyS",
>> + .nr = UART_NR,
>> + .cons = VT8500_CONSOLE,
>> +};
>> +
>> +static int __init vt8500_serial_probe(struct platform_device *pdev)
>> +{
>> + struct vt8500_port *vt8500_port;
>> + struct resource *resource;
>> + struct uart_port *port;
>> + int irq;
>> +
>> + if (unlikely(pdev->id < 0 || pdev->id >= UART_NR))
>> + return -ENXIO;
>> +
>> + printk(KERN_INFO "vt8500_serial: detected port #%d\n", pdev->id);
>> +
>> + port = get_port_from_line(pdev->id);
>> + port->dev = &pdev->dev;
>> + vt8500_port = UART_TO_VT8500(port);
>> +
>> + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + if (unlikely(!resource))
>> + return -ENXIO;
>> + port->mapbase = resource->start;
>> +
>> + irq = platform_get_irq(pdev, 0);
>> + if (unlikely(irq < 0))
>> + return -ENXIO;
>> + port->irq = irq;
>> +
>> + platform_set_drvdata(pdev, port);
>> +
>> + return uart_add_one_port(&vt8500_uart_driver, port);
>> +}
>> +
>> +static int __devexit vt8500_serial_remove(struct platform_device *pdev)
>> +{
>> + return 0;
>> +}
>> +
>> +static struct platform_driver vt8500_platform_driver = {
>> + .remove = vt8500_serial_remove,
>> + .driver = {
>> + .name = "vt8500_serial",
>> + .owner = THIS_MODULE,
>> + },
>> +};
>> +
>> +static int __init vt8500_serial_init(void)
>> +{
>> + int ret;
>> +
>> + ret = uart_register_driver(&vt8500_uart_driver);
>> + if (unlikely(ret))
>> + return ret;
>> +
>> + ret = platform_driver_probe(&vt8500_platform_driver,
>> + vt8500_serial_probe);
>> +
>> + if (unlikely(ret))
>> + uart_unregister_driver(&vt8500_uart_driver);
>> +
>> + printk(KERN_INFO "vt8500_serial: driver initialized\n");
>> +
>> + return ret;
>> +}
>> +
>> +static void __exit vt8500_serial_exit(void)
>> +{
>> +#ifdef CONFIG_SERIAL_VT8500_CONSOLE
>> + unregister_console(&vt8500_console);
>> +#endif
>> + platform_driver_unregister(&vt8500_platform_driver);
>> + uart_unregister_driver(&vt8500_uart_driver);
>> +}
>> +
>> +module_init(vt8500_serial_init);
>> +module_exit(vt8500_serial_exit);
>> +
>> +MODULE_AUTHOR("Alexey Charkov <alchark at gmail.com>");
>> +MODULE_DESCRIPTION("Driver for vt8500 serial device");
>> +MODULE_LICENSE("GPL");
>> diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
>> index f10db6e..20ba8c3 100644
>> --- a/include/linux/serial_core.h
>> +++ b/include/linux/serial_core.h
>> @@ -186,6 +186,9 @@
>> #define PORT_ALTERA_JTAGUART 91
>> #define PORT_ALTERA_UART 92
>>
>> +/* VIA VT8500 SoC */
>> +#define PORT_VT8500 93
>> +
>> #ifdef __KERNEL__
>>
>> #include <linux/compiler.h>
>
> This patch looks very good otherwise, good night ! :)
Thanks :)
More information about the linux-arm-kernel
mailing list