lkml.org 
[lkml]   [2010]   [Aug]   [10]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 05/68] mrst_max3110: add UART driver for Max3110 on Moorestown
    Date
    From: Feng Tang <feng.tang@intel.com>

    This driver enable the max3110 device, it can be used as
    a system console. the IRQ needs be enabled if user want a
    better performance. MRST max3110 works in 3.684MHz clock,
    which supports 230400 as its maximum rate.

    Signed-off-by: Feng Tang <feng.tang@intel.com>
    Signed-off-by: Alan Cox <alan@linux.intel.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
    ---
    drivers/serial/Kconfig | 17 +
    drivers/serial/Makefile | 1 +
    drivers/serial/mrst_max3110.c | 844 +++++++++++++++++++++++++++++++++++++++++
    drivers/serial/mrst_max3110.h | 59 +++
    4 files changed, 921 insertions(+), 0 deletions(-)
    create mode 100644 drivers/serial/mrst_max3110.c
    create mode 100644 drivers/serial/mrst_max3110.h

    diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
    index e437ce8..964634e 100644
    --- a/drivers/serial/Kconfig
    +++ b/drivers/serial/Kconfig
    @@ -690,6 +690,23 @@ config SERIAL_SA1100_CONSOLE
    your boot loader (lilo or loadlin) about how to pass options to the
    kernel at boot time.)

    +config SERIAL_MRST_MAX3110
    + tristate "SPI UART driver for Max3110"
    + depends on SPI_DW_PCI
    + select SERIAL_CORE
    + select SERIAL_CORE_CONSOLE
    + help
    + This is the UART protocol driver for the MAX3110 device on
    + the Intel Moorestown platform. On other systems use the max3100
    + driver.
    +
    +config MRST_MAX3110_IRQ
    + boolean "Enable GPIO IRQ for Max3110 over Moorestown"
    + default n
    + depends on SERIAL_MRST_MAX3110 && GPIO_LANGWELL
    + help
    + This has to be enabled after Moorestown GPIO driver is loaded
    +
    config SERIAL_BFIN
    tristate "Blackfin serial port support"
    depends on BLACKFIN
    diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
    index 208a855..a5edb49 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_MRST_MAX3110) += mrst_max3110.o
    diff --git a/drivers/serial/mrst_max3110.c b/drivers/serial/mrst_max3110.c
    new file mode 100644
    index 0000000..273e7cb
    --- /dev/null
    +++ b/drivers/serial/mrst_max3110.c
    @@ -0,0 +1,844 @@
    +/*
    + * max3110.c - spi uart protocol driver for Maxim 3110 on Moorestown
    + *
    + * Copyright (C) Intel 2008 Feng Tang <feng.tang@intel.com>
    + *
    + * This program is free software; you can redistribute it and/or modify it
    + * under the terms and conditions of the GNU General Public License,
    + * version 2, as published by the Free Software Foundation.
    + *
    + * This program is distributed in the hope 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.,
    + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
    + */
    +
    +/*
    + * Note:
    + * 1. From Max3110 spec, the Rx FIFO has 8 words, while the Tx FIFO only has
    + * 1 word. If SPI master controller doesn't support sclk frequency change,
    + * then the char need be sent out one by one with some delay
    + *
    + * 2. Currently only RX availabe interrrupt is used, no need for waiting TXE
    + * interrupt for a low speed UART device
    + */
    +
    +#include <linux/module.h>
    +#include <linux/ioport.h>
    +#include <linux/init.h>
    +#include <linux/console.h>
    +#include <linux/sysrq.h>
    +#include <linux/platform_device.h>
    +#include <linux/tty.h>
    +#include <linux/tty_flip.h>
    +#include <linux/serial_core.h>
    +#include <linux/serial_reg.h>
    +
    +#include <linux/kthread.h>
    +#include <linux/delay.h>
    +#include <asm/atomic.h>
    +#include <linux/spi/spi.h>
    +#include <linux/spi/dw_spi.h>
    +
    +#include "mrst_max3110.h"
    +
    +#define PR_FMT "mrst_max3110: "
    +
    +struct uart_max3110 {
    + struct uart_port port;
    + struct spi_device *spi;
    + char *name;
    +
    + wait_queue_head_t wq;
    + struct task_struct *main_thread;
    + struct task_struct *read_thread;
    + int mthread_up;
    + spinlock_t lock;
    +
    + u32 baud;
    + u16 cur_conf;
    + u8 clock;
    + u8 parity, word_7bits;
    +
    + atomic_t uart_tx_need;
    +
    + /* console related */
    + struct circ_buf con_xmit;
    + atomic_t con_tx_need;
    +
    + /* irq related */
    + u16 irq;
    + atomic_t irq_pending;
    +};
    +
    +/* global data structure, may need be removed */
    +struct uart_max3110 *pmax;
    +static inline void receive_char(struct uart_max3110 *max, u8 ch);
    +static void receive_chars(struct uart_max3110 *max,
    + unsigned char *str, int len);
    +static int max3110_read_multi(struct uart_max3110 *max, int len, u8 *buf);
    +static void max3110_console_receive(struct uart_max3110 *max);
    +
    +int max3110_write_then_read(struct uart_max3110 *max,
    + const u8 *txbuf, u8 *rxbuf, unsigned len, int always_fast)
    +{
    + struct spi_device *spi = max->spi;
    + struct spi_message message;
    + struct spi_transfer x;
    + int ret;
    +
    + if (!txbuf || !rxbuf)
    + return -EINVAL;
    +
    + spi_message_init(&message);
    + memset(&x, 0, sizeof x);
    + x.len = len;
    + x.tx_buf = txbuf;
    + x.rx_buf = rxbuf;
    + spi_message_add_tail(&x, &message);
    +
    + if (always_fast)
    + x.speed_hz = 3125000;
    + else if (max->baud)
    + x.speed_hz = max->baud;
    +
    + /* Do the i/o */
    + ret = spi_sync(spi, &message);
    + return ret;
    +}
    +
    +/* Write a u16 to the device, and return one u16 read back */
    +int max3110_out(struct uart_max3110 *max, const u16 out)
    +{
    + u16 tmp;
    + int ret;
    +
    + ret = max3110_write_then_read(max, (u8 *)&out, (u8 *)&tmp, 2, 1);
    + if (ret)
    + return ret;
    +
    + /* If some valid data is read back */
    + if (tmp & MAX3110_READ_DATA_AVAILABLE)
    + receive_char(max, (tmp & 0xff));
    +
    + return ret;
    +}
    +
    +#define MAX_READ_LEN 20
    +/*
    + * This is usually used to read data from SPIC RX FIFO, which doesn't
    + * need any delay like flushing character out. It returns how many
    + * valide bytes are read back
    + */
    +static int max3110_read_multi(struct uart_max3110 *max, int len, u8 *buf)
    +{
    + u16 out[MAX_READ_LEN], in[MAX_READ_LEN];
    + u8 *pbuf, valid_str[MAX_READ_LEN];
    + int i, j, bytelen;
    +
    + if (len > MAX_READ_LEN) {
    + pr_err(PR_FMT "read len %d is too large\n", len);
    + return 0;
    + }
    +
    + bytelen = len * 2;
    + memset(out, 0, bytelen);
    + memset(in, 0, bytelen);
    +
    + if (max3110_write_then_read(max, (u8 *)out, (u8 *)in, bytelen, 1))
    + return 0;
    +
    + /* If caller don't provide a buffer, then handle received char */
    + pbuf = buf ? buf : valid_str;
    +
    + for (i = 0, j = 0; i < len; i++) {
    + if (in[i] & MAX3110_READ_DATA_AVAILABLE)
    + pbuf[j++] = (u8)(in[i] & 0xff);
    + }
    +
    + if (j && (pbuf == valid_str))
    + receive_chars(max, valid_str, j);
    +
    + return j;
    +}
    +
    +static void serial_m3110_con_putchar(struct uart_port *port, int ch)
    +{
    + struct uart_max3110 *max =
    + container_of(port, struct uart_max3110, port);
    + struct circ_buf *xmit = &max->con_xmit;
    +
    + if (uart_circ_chars_free(xmit)) {
    + xmit->buf[xmit->head] = (char)ch;
    + xmit->head = (xmit->head + 1) & (PAGE_SIZE - 1);
    + }
    +
    + if (!atomic_read(&max->con_tx_need)) {
    + atomic_set(&max->con_tx_need, 1);
    + wake_up_process(max->main_thread);
    + }
    +}
    +
    +/*
    + * Print a string to the serial port trying not to disturb
    + * any possible real use of the port...
    + *
    + * The console_lock must be held when we get here.
    + */
    +static void serial_m3110_con_write(struct console *co,
    + const char *s, unsigned int count)
    +{
    + if (!pmax)
    + return;
    +
    + uart_console_write(&pmax->port, s, count, serial_m3110_con_putchar);
    +}
    +
    +static int __init
    +serial_m3110_con_setup(struct console *co, char *options)
    +{
    + struct uart_max3110 *max = pmax;
    + int baud = 115200;
    + int bits = 8;
    + int parity = 'n';
    + int flow = 'n';
    +
    + pr_info(PR_FMT "setting up console\n");
    +
    + if (!max) {
    + pr_err(PR_FMT "pmax is NULL, return");
    + return -ENODEV;
    + }
    +
    + if (options)
    + uart_parse_options(options, &baud, &parity, &bits, &flow);
    +
    + return uart_set_options(&max->port, co, baud, parity, bits, flow);
    +}
    +
    +static struct tty_driver *serial_m3110_con_device(struct console *co,
    + int *index)
    +{
    + struct uart_driver *p = co->data;
    + *index = co->index;
    + return p->tty_driver;
    +}
    +
    +static struct uart_driver serial_m3110_reg;
    +static struct console serial_m3110_console = {
    + .name = "ttyS",
    + .write = serial_m3110_con_write,
    + .device = serial_m3110_con_device,
    + .setup = serial_m3110_con_setup,
    + .flags = CON_PRINTBUFFER,
    + .index = -1,
    + .data = &serial_m3110_reg,
    +};
    +
    +#define MRST_CONSOLE (&serial_m3110_console)
    +
    +static unsigned int serial_m3110_tx_empty(struct uart_port *port)
    +{
    + return 1;
    +}
    +
    +static void serial_m3110_stop_tx(struct uart_port *port)
    +{
    + return;
    +}
    +
    +/* stop_rx will be called in spin_lock env */
    +static void serial_m3110_stop_rx(struct uart_port *port)
    +{
    + return;
    +}
    +
    +#define WORDS_PER_XFER 128
    +static inline void send_circ_buf(struct uart_max3110 *max,
    + struct circ_buf *xmit)
    +{
    + int len, left = 0;
    + u16 obuf[WORDS_PER_XFER], ibuf[WORDS_PER_XFER];
    + u8 valid_str[WORDS_PER_XFER];
    + int i, j;
    +
    + while (!uart_circ_empty(xmit)) {
    + left = uart_circ_chars_pending(xmit);
    + while (left) {
    + len = (left >= WORDS_PER_XFER) ? WORDS_PER_XFER : left;
    +
    + memset(obuf, 0, len * 2);
    + memset(ibuf, 0, len * 2);
    + for (i = 0; i < len; i++) {
    + obuf[i] = (u8)xmit->buf[xmit->tail] | WD_TAG;
    + xmit->tail = (xmit->tail + 1) &
    + (UART_XMIT_SIZE - 1);
    + }
    + max3110_write_then_read(max, (u8 *)obuf,
    + (u8 *)ibuf, len * 2, 0);
    +
    + for (i = 0, j = 0; i < len; i++) {
    + if (ibuf[i] & MAX3110_READ_DATA_AVAILABLE)
    + valid_str[j++] = (u8)(ibuf[i] & 0xff);
    + }
    +
    + if (j)
    + receive_chars(max, valid_str, j);
    +
    + max->port.icount.tx += len;
    + left -= len;
    + }
    + }
    +}
    +
    +static void transmit_char(struct uart_max3110 *max)
    +{
    + struct uart_port *port = &max->port;
    + struct circ_buf *xmit = &port->state->xmit;
    +
    + if (uart_circ_empty(xmit) || uart_tx_stopped(port))
    + return;
    +
    + send_circ_buf(max, xmit);
    +
    + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
    + uart_write_wakeup(port);
    +
    + if (uart_circ_empty(xmit))
    + serial_m3110_stop_tx(port);
    +}
    +
    +/* This will be called by uart_write() and tty_write, can't
    + * go to sleep */
    +static void serial_m3110_start_tx(struct uart_port *port)
    +{
    + struct uart_max3110 *max =
    + container_of(port, struct uart_max3110, port);
    +
    + if (!atomic_read(&max->uart_tx_need)) {
    + atomic_set(&max->uart_tx_need, 1);
    + wake_up_process(max->main_thread);
    + }
    +}
    +
    +static void receive_chars(struct uart_max3110 *max, unsigned char *str, int len)
    +{
    + struct uart_port *port = &max->port;
    + struct tty_struct *tty;
    + int usable;
    +
    + /* If uart is not opened, just return */
    + if (!port->state)
    + return;
    +
    + tty = port->state->port.tty;
    + if (!tty)
    + return; /* receive some char before the tty is opened */
    +
    + while (len) {
    + usable = tty_buffer_request_room(tty, len);
    + if (usable) {
    + tty_insert_flip_string(tty, str, usable);
    + str += usable;
    + port->icount.rx += usable;
    + tty_flip_buffer_push(tty);
    + }
    + len -= usable;
    + }
    +}
    +
    +static inline void receive_char(struct uart_max3110 *max, u8 ch)
    +{
    + receive_chars(max, &ch, 1);
    +}
    +
    +static void max3110_console_receive(struct uart_max3110 *max)
    +{
    + int loop = 1, num, total = 0;
    + u8 recv_buf[512], *pbuf;
    +
    + pbuf = recv_buf;
    + do {
    + num = max3110_read_multi(max, 8, pbuf);
    +
    + if (num) {
    + loop = 10;
    + pbuf += num;
    + total += num;
    +
    + if (total >= 500) {
    + receive_chars(max, recv_buf, total);
    + pbuf = recv_buf;
    + total = 0;
    + }
    + }
    + } while (--loop);
    +
    + if (total)
    + receive_chars(max, recv_buf, total);
    +}
    +
    +static int max3110_main_thread(void *_max)
    +{
    + struct uart_max3110 *max = _max;
    + wait_queue_head_t *wq = &max->wq;
    + int ret = 0;
    + struct circ_buf *xmit = &max->con_xmit;
    +
    + init_waitqueue_head(wq);
    + pr_info(PR_FMT "start main thread\n");
    +
    + do {
    + wait_event_interruptible(*wq, (atomic_read(&max->irq_pending) ||
    + atomic_read(&max->con_tx_need) ||
    + atomic_read(&max->uart_tx_need)) ||
    + kthread_should_stop());
    + max->mthread_up = 1;
    +
    +#ifdef CONFIG_MRST_MAX3110_IRQ
    + if (atomic_read(&max->irq_pending)) {
    + max3110_console_receive(max);
    + atomic_set(&max->irq_pending, 0);
    + }
    +#endif
    +
    + /* first handle console output */
    + if (atomic_read(&max->con_tx_need)) {
    + send_circ_buf(max, xmit);
    + atomic_set(&max->con_tx_need, 0);
    + }
    +
    + /* handle uart output */
    + if (atomic_read(&max->uart_tx_need)) {
    + transmit_char(max);
    + atomic_set(&max->uart_tx_need, 0);
    + }
    + max->mthread_up = 0;
    + } while (!kthread_should_stop());
    +
    + return ret;
    +}
    +
    +#ifdef CONFIG_MRST_MAX3110_IRQ
    +static irqreturn_t serial_m3110_irq(int irq, void *dev_id)
    +{
    + struct uart_max3110 *max = dev_id;
    +
    + /* max3110's irq is a falling edge, not level triggered,
    + * so no need to disable the irq */
    + if (!atomic_read(&max->irq_pending)) {
    + atomic_inc(&max->irq_pending);
    + wake_up_process(max->main_thread);
    + }
    + return IRQ_HANDLED;
    +}
    +#else
    +/* if don't use RX IRQ, then need a thread to polling read */
    +static int max3110_read_thread(void *_max)
    +{
    + struct uart_max3110 *max = _max;
    +
    + pr_info(PR_FMT "start read thread\n");
    + do {
    + if (!max->mthread_up)
    + max3110_console_receive(max);
    +
    + set_current_state(TASK_INTERRUPTIBLE);
    + schedule_timeout(HZ / 20);
    + } while (!kthread_should_stop());
    +
    + return 0;
    +}
    +#endif
    +
    +static int serial_m3110_startup(struct uart_port *port)
    +{
    + struct uart_max3110 *max =
    + container_of(port, struct uart_max3110, port);
    + u16 config = 0;
    + int ret = 0;
    +
    + if (port->line != 0)
    + pr_err(PR_FMT "uart port startup failed\n");
    +
    + /* firstly disable all IRQ and config it to 115200, 8n1 */
    + config = WC_TAG | WC_FIFO_ENABLE
    + | WC_1_STOPBITS
    + | WC_8BIT_WORD
    + | WC_BAUD_DR2;
    + ret = max3110_out(max, config);
    +
    + /* as we use thread to handle tx/rx, need set low latency */
    + port->state->port.tty->low_latency = 1;
    +
    +#ifdef CONFIG_MRST_MAX3110_IRQ
    + ret = request_irq(max->irq, serial_m3110_irq,
    + IRQ_TYPE_EDGE_FALLING, "max3110", max);
    + if (ret)
    + return ret;
    +
    + /* enable RX IRQ only */
    + config |= WC_RXA_IRQ_ENABLE;
    + max3110_out(max, config);
    +#else
    + /* if IRQ is disabled, start a read thread for input data */
    + max->read_thread =
    + kthread_run(max3110_read_thread, max, "max3110_read");
    +#endif
    +
    + max->cur_conf = config;
    + return 0;
    +}
    +
    +static void serial_m3110_shutdown(struct uart_port *port)
    +{
    + struct uart_max3110 *max =
    + container_of(port, struct uart_max3110, port);
    + u16 config;
    +
    + if (max->read_thread) {
    + kthread_stop(max->read_thread);
    + max->read_thread = NULL;
    + }
    +
    +#ifdef CONFIG_MRST_MAX3110_IRQ
    + free_irq(max->irq, max);
    +#endif
    +
    + /* Disable interrupts from this port */
    + config = WC_TAG | WC_SW_SHDI;
    + max3110_out(max, config);
    +}
    +
    +static void serial_m3110_release_port(struct uart_port *port)
    +{
    +}
    +
    +static int serial_m3110_request_port(struct uart_port *port)
    +{
    + return 0;
    +}
    +
    +static void serial_m3110_config_port(struct uart_port *port, int flags)
    +{
    + /* give it fake type */
    + port->type = PORT_PXA;
    +}
    +
    +static int
    +serial_m3110_verify_port(struct uart_port *port, struct serial_struct *ser)
    +{
    + /* we don't want the core code to modify any port params */
    + return -EINVAL;
    +}
    +
    +
    +static const char *serial_m3110_type(struct uart_port *port)
    +{
    + struct uart_max3110 *max =
    + container_of(port, struct uart_max3110, port);
    + return max->name;
    +}
    +
    +static void
    +serial_m3110_set_termios(struct uart_port *port, struct ktermios *termios,
    + struct ktermios *old)
    +{
    + struct uart_max3110 *max =
    + container_of(port, struct uart_max3110, port);
    + unsigned char cval;
    + unsigned int baud, parity = 0;
    + int clk_div = -1;
    + u16 new_conf = max->cur_conf;
    +
    + switch (termios->c_cflag & CSIZE) {
    + case CS7:
    + cval = UART_LCR_WLEN7;
    + new_conf |= WC_7BIT_WORD;
    + break;
    + default:
    + case CS8:
    + cval = UART_LCR_WLEN8;
    + new_conf |= WC_8BIT_WORD;
    + break;
    + }
    +
    + baud = uart_get_baud_rate(port, termios, old, 0, 230400);
    +
    + /* first calc the div for 1.8MHZ clock case */
    + switch (baud) {
    + case 300:
    + clk_div = WC_BAUD_DR384;
    + break;
    + case 600:
    + clk_div = WC_BAUD_DR192;
    + break;
    + case 1200:
    + clk_div = WC_BAUD_DR96;
    + break;
    + case 2400:
    + clk_div = WC_BAUD_DR48;
    + break;
    + case 4800:
    + clk_div = WC_BAUD_DR24;
    + break;
    + case 9600:
    + clk_div = WC_BAUD_DR12;
    + break;
    + case 19200:
    + clk_div = WC_BAUD_DR6;
    + break;
    + case 38400:
    + clk_div = WC_BAUD_DR3;
    + break;
    + case 57600:
    + clk_div = WC_BAUD_DR2;
    + break;
    + case 115200:
    + clk_div = WC_BAUD_DR1;
    + break;
    + case 230400:
    + if (max->clock & MAX3110_HIGH_CLK)
    + break;
    + default:
    + /* pick the previous baud rate */
    + baud = max->baud;
    + clk_div = max->cur_conf & WC_BAUD_DIV_MASK;
    + tty_termios_encode_baud_rate(termios, baud, baud);
    + }
    +
    + if (max->clock & MAX3110_HIGH_CLK) {
    + clk_div += 1;
    + /* high clk version max3110 doesn't support B300 */
    + if (baud == 300)
    + baud = 600;
    + if (baud == 230400)
    + clk_div = WC_BAUD_DR1;
    + tty_termios_encode_baud_rate(termios, baud, baud);
    + }
    +
    + new_conf = (new_conf & ~WC_BAUD_DIV_MASK) | clk_div;
    + if (termios->c_cflag & CSTOPB)
    + new_conf |= WC_2_STOPBITS;
    + else
    + new_conf &= ~WC_2_STOPBITS;
    +
    + if (termios->c_cflag & PARENB) {
    + new_conf |= WC_PARITY_ENABLE;
    + parity |= UART_LCR_PARITY;
    + } else
    + new_conf &= ~WC_PARITY_ENABLE;
    +
    + if (!(termios->c_cflag & PARODD))
    + parity |= UART_LCR_EPAR;
    + max->parity = parity;
    +
    + uart_update_timeout(port, termios->c_cflag, baud);
    +
    + new_conf |= WC_TAG;
    + if (new_conf != max->cur_conf) {
    + max3110_out(max, new_conf);
    + max->cur_conf = new_conf;
    + max->baud = baud;
    + }
    +}
    +
    +/* don't handle hw handshaking */
    +static unsigned int serial_m3110_get_mctrl(struct uart_port *port)
    +{
    + return TIOCM_DSR | TIOCM_CAR | TIOCM_DSR;
    +}
    +
    +static void serial_m3110_set_mctrl(struct uart_port *port, unsigned int mctrl)
    +{
    +}
    +
    +static void serial_m3110_break_ctl(struct uart_port *port, int break_state)
    +{
    +}
    +
    +static void serial_m3110_pm(struct uart_port *port, unsigned int state,
    + unsigned int oldstate)
    +{
    +}
    +
    +static void serial_m3110_enable_ms(struct uart_port *port)
    +{
    +}
    +
    +struct uart_ops serial_m3110_ops = {
    + .tx_empty = serial_m3110_tx_empty,
    + .set_mctrl = serial_m3110_set_mctrl,
    + .get_mctrl = serial_m3110_get_mctrl,
    + .stop_tx = serial_m3110_stop_tx,
    + .start_tx = serial_m3110_start_tx,
    + .stop_rx = serial_m3110_stop_rx,
    + .enable_ms = serial_m3110_enable_ms,
    + .break_ctl = serial_m3110_break_ctl,
    + .startup = serial_m3110_startup,
    + .shutdown = serial_m3110_shutdown,
    + .set_termios = serial_m3110_set_termios, /* must have */
    + .pm = serial_m3110_pm,
    + .type = serial_m3110_type,
    + .release_port = serial_m3110_release_port,
    + .request_port = serial_m3110_request_port,
    + .config_port = serial_m3110_config_port,
    + .verify_port = serial_m3110_verify_port,
    +};
    +
    +static struct uart_driver serial_m3110_reg = {
    + .owner = THIS_MODULE,
    + .driver_name = "MRST serial",
    + .dev_name = "ttyS",
    + .major = TTY_MAJOR,
    + .minor = 64,
    + .nr = 1,
    + .cons = MRST_CONSOLE,
    +};
    +
    +static int serial_m3110_suspend(struct spi_device *spi, pm_message_t state)
    +{
    + return 0;
    +}
    +
    +static int serial_m3110_resume(struct spi_device *spi)
    +{
    + return 0;
    +}
    +
    +static struct dw_spi_chip spi0_uart = {
    + .poll_mode = 1,
    + .enable_dma = 0,
    + .type = SPI_FRF_SPI,
    +};
    +
    +static int serial_m3110_probe(struct spi_device *spi)
    +{
    + struct uart_max3110 *max;
    + int ret;
    + unsigned char *buffer;
    +
    + max = kzalloc(sizeof(*max), GFP_KERNEL);
    + if (!max)
    + return -ENOMEM;
    +
    + /* set spi info */
    + spi->mode = SPI_MODE_0;
    + spi->bits_per_word = 16;
    + max->clock = MAX3110_HIGH_CLK;
    + spi->controller_data = &spi0_uart;
    +
    + spi_setup(spi);
    +
    + max->port.type = PORT_PXA; /* need apply for a max3110 type */
    + max->port.fifosize = 2; /* only have 16b buffer */
    + max->port.ops = &serial_m3110_ops;
    + max->port.line = 0;
    + max->port.dev = &spi->dev;
    + max->port.uartclk = 115200;
    +
    + max->spi = spi;
    + max->name = spi->modalias; /* use spi name as the name */
    + max->irq = (u16)spi->irq;
    +
    + spin_lock_init(&max->lock);
    +
    + max->word_7bits = 0;
    + max->parity = 0;
    + max->baud = 0;
    +
    + max->cur_conf = 0;
    + atomic_set(&max->irq_pending, 0);
    +
    + buffer = (unsigned char *)__get_free_page(GFP_KERNEL);
    + if (!buffer) {
    + ret = -ENOMEM;
    + goto err_get_page;
    + }
    + max->con_xmit.buf = (unsigned char *)buffer;
    + max->con_xmit.head = max->con_xmit.tail = 0;
    +
    + max->main_thread = kthread_run(max3110_main_thread,
    + max, "max3110_main");
    + if (IS_ERR(max->main_thread)) {
    + ret = PTR_ERR(max->main_thread);
    + goto err_kthread;
    + }
    +
    + pmax = max;
    + /* give membase a psudo value to pass serial_core's check */
    + max->port.membase = (void *)0xff110000;
    + uart_add_one_port(&serial_m3110_reg, &max->port);
    +
    + return 0;
    +
    +err_kthread:
    + free_page((unsigned long)buffer);
    +err_get_page:
    + pmax = NULL;
    + kfree(max);
    + return ret;
    +}
    +
    +static int max3110_remove(struct spi_device *dev)
    +{
    + struct uart_max3110 *max = pmax;
    +
    + if (!pmax)
    + return 0;
    +
    + pmax = NULL;
    + uart_remove_one_port(&serial_m3110_reg, &max->port);
    +
    + free_page((unsigned long)max->con_xmit.buf);
    +
    + if (max->main_thread)
    + kthread_stop(max->main_thread);
    +
    + kfree(max);
    + return 0;
    +}
    +
    +static struct spi_driver uart_max3110_driver = {
    + .driver = {
    + .name = "spi_max3111",
    + .bus = &spi_bus_type,
    + .owner = THIS_MODULE,
    + },
    + .probe = serial_m3110_probe,
    + .remove = __devexit_p(max3110_remove),
    + .suspend = serial_m3110_suspend,
    + .resume = serial_m3110_resume,
    +};
    +
    +
    +int __init serial_m3110_init(void)
    +{
    + int ret = 0;
    +
    + ret = uart_register_driver(&serial_m3110_reg);
    + if (ret)
    + return ret;
    +
    + ret = spi_register_driver(&uart_max3110_driver);
    + if (ret)
    + uart_unregister_driver(&serial_m3110_reg);
    +
    + return ret;
    +}
    +
    +void __exit serial_m3110_exit(void)
    +{
    + spi_unregister_driver(&uart_max3110_driver);
    + uart_unregister_driver(&serial_m3110_reg);
    +}
    +
    +module_init(serial_m3110_init);
    +module_exit(serial_m3110_exit);
    +
    +MODULE_LICENSE("GPL");
    +MODULE_ALIAS("max3110-uart");
    diff --git a/drivers/serial/mrst_max3110.h b/drivers/serial/mrst_max3110.h
    new file mode 100644
    index 0000000..363478a
    --- /dev/null
    +++ b/drivers/serial/mrst_max3110.h
    @@ -0,0 +1,59 @@
    +#ifndef _MRST_MAX3110_H
    +#define _MRST_MAX3110_H
    +
    +#define MAX3110_HIGH_CLK 0x1 /* 3.6864 MHZ */
    +#define MAX3110_LOW_CLK 0x0 /* 1.8432 MHZ */
    +
    +/* status bits for all 4 MAX3110 operate modes */
    +#define MAX3110_READ_DATA_AVAILABLE (1 << 15)
    +#define MAX3110_WRITE_BUF_EMPTY (1 << 14)
    +
    +#define WC_TAG (3 << 14)
    +#define RC_TAG (1 << 14)
    +#define WD_TAG (2 << 14)
    +#define RD_TAG (0 << 14)
    +
    +/* bits def for write configuration */
    +#define WC_FIFO_ENABLE_MASK (1 << 13)
    +#define WC_FIFO_ENABLE (0 << 13)
    +
    +#define WC_SW_SHDI (1 << 12)
    +
    +#define WC_IRQ_MASK (0xF << 8)
    +#define WC_TXE_IRQ_ENABLE (1 << 11) /* TX empty irq */
    +#define WC_RXA_IRQ_ENABLE (1 << 10) /* RX availabe irq */
    +#define WC_PAR_HIGH_IRQ_ENABLE (1 << 9)
    +#define WC_REC_ACT_IRQ_ENABLE (1 << 8)
    +
    +#define WC_IRDA_ENABLE (1 << 7)
    +
    +#define WC_STOPBITS_MASK (1 << 6)
    +#define WC_2_STOPBITS (1 << 6)
    +#define WC_1_STOPBITS (0 << 6)
    +
    +#define WC_PARITY_ENABLE_MASK (1 << 5)
    +#define WC_PARITY_ENABLE (1 << 5)
    +
    +#define WC_WORDLEN_MASK (1 << 4)
    +#define WC_7BIT_WORD (1 << 4)
    +#define WC_8BIT_WORD (0 << 4)
    +
    +#define WC_BAUD_DIV_MASK (0xF)
    +#define WC_BAUD_DR1 (0x0)
    +#define WC_BAUD_DR2 (0x1)
    +#define WC_BAUD_DR4 (0x2)
    +#define WC_BAUD_DR8 (0x3)
    +#define WC_BAUD_DR16 (0x4)
    +#define WC_BAUD_DR32 (0x5)
    +#define WC_BAUD_DR64 (0x6)
    +#define WC_BAUD_DR128 (0x7)
    +#define WC_BAUD_DR3 (0x8)
    +#define WC_BAUD_DR6 (0x9)
    +#define WC_BAUD_DR12 (0xA)
    +#define WC_BAUD_DR24 (0xB)
    +#define WC_BAUD_DR48 (0xC)
    +#define WC_BAUD_DR96 (0xD)
    +#define WC_BAUD_DR192 (0xE)
    +#define WC_BAUD_DR384 (0xF)
    +
    +#endif
    --
    1.7.2


    \
     
     \ /
      Last update: 2010-08-11 00:17    [W:3.674 / U:0.152 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site