All of lore.kernel.org
 help / color / mirror / Atom feed
* UART lite driver?
@ 2003-02-24 19:39 Kerl, John
  2003-02-24 19:50 ` Peter 'p2' De Schrijver
  0 siblings, 1 reply; 2+ messages in thread
From: Kerl, John @ 2003-02-24 19:39 UTC (permalink / raw)
  To: linuxppc embedded


Hello,

Does anyone have a UART lite driver for Xilinx Virtex-II Pro?
I've got a recent Denx tree (about 2 weeks ago) which does include
support for 16550 UART, but unfortunately I don't have a license
for that peripheral.

Thanks.

** Sent via the linuxppc-embedded mail list. See http://lists.linuxppc.org/

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: UART lite driver?
  2003-02-24 19:39 UART lite driver? Kerl, John
@ 2003-02-24 19:50 ` Peter 'p2' De Schrijver
  0 siblings, 0 replies; 2+ messages in thread
From: Peter 'p2' De Schrijver @ 2003-02-24 19:50 UTC (permalink / raw)
  To: Kerl, John; +Cc: linuxppc embedded

[-- Attachment #1: Type: text/plain, Size: 520 bytes --]

Hi John,

>
> Hello,
>
> Does anyone have a UART lite driver for Xilinx Virtex-II Pro?
> I've got a recent Denx tree (about 2 weeks ago) which does include
> support for 16550 UART, but unfortunately I don't have a license
> for that peripheral.
>

I recently made a driver for the UART lite. I didn't test it fully yet,
but basic functionality seems ok. I didn't find the time to make a
proper patch for it yet, so you will have to integrate it yourself into
the kernel tree.

Comments welcome & happy hacking,

Peter.

[-- Attachment #2: xilinx_uartlite.c --]
[-- Type: text/x-csrc, Size: 31921 bytes --]

/*
 * xilinx_uartlite.c: Driver for the Xilinx UART lite
 *
 * Copyright 2003 Mind NV
 *
 * http://www.mind.be/
 *
 * Author : Peter De Schrijver (p2@mind.be)
 *
 * Based on drivers/char/serial_amba.c
 *
 * This software may be used and distributed according to the terms of
 * the GNU General Public License (GPL) version 2, incorporated herein by
 * reference. Drivers based on or derived from this code fall under the GPL
 * and must retain the authorship, copyright and this license notice. This
 * file is not a complete program and may only be used when the entire
 * operating system is licensed under the GPL.
 *
 */
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/circ_buf.h>
#include <linux/serial.h>
#include <linux/console.h>
#include <linux/sysrq.h>

#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>

#define SERIAL_UARTLITE_NAME	"ttyUL"
#define SERIAL_UARTLITE_MAJOR	204
#define SERIAL_UARTLITE_MINOR	18
#define SERIAL_UARTLITE_NR		2

#define CALLOUT_UARTLITE_NAME	"cuaUL"
#define CALLOUT_UARTLITE_MAJOR	205
#define CALLOUT_UARTLITE_MINOR	18
#define CALLOUT_UARTLITE_NR		SERIAL_UARTLITE_NR

#define HIGH_BITS_OFFSET    ((sizeof(long)-sizeof(int))*8)

#define UARTLITE_XMIT_SIZE		1024
#define WAKEUP_CHARS        256
#define UARTLITE_ISR_PASS_LIMIT 256

#define EVT_WRITE_WAKEUP    0

#define UART_RFIFO	0
#define UART_WFIFO	4

#define UART_SR		8
#define UART_RX_FIFO_VALID_DATA (1<<0)
#define UART_TX_FIFO_EMPTY (1<<2)
#define UART_OVERRUN_ERROR (1<<5)
#define UART_FRAME_ERROR (1<<6)
#define UART_PAR_ERROR (1<<7)
#define UART_ERROR (UART_OVERRUN_ERROR | UART_FRAME_ERROR | UART_PAR_ERROR)
#define UART_BUSY UART_TX_FIFO_EMPTY

#define UART_CR		0xc
#define UART_ENABLE_INTR	(1<<4)

#define UART_GET_CR(p) (p->cr)
#define UART_GET_SR(p) ((*(volatile unsigned int *)(p->uart_base+UART_SR)))
#define UART_PUT_CR(p,x) (*(volatile unsigned int *)(p->uart_base+UART_CR)=p->cr=x)
#define UART_PUT_CHAR(p,x) ((*(volatile unsigned int *)(p->uart_base+UART_WFIFO))=x)
#define UART_GET_CHAR(p) (*(volatile unsigned int *)(p->uart_base+UART_RFIFO))

#define UART_RX_DATA(x) (x & UART_RX_FIFO_VALID_DATA)

static struct tty_driver uartlite_driver, uartlitecallout_driver;
static int uartlite_refcount;
static struct tty_struct *uartlite_table[SERIAL_UARTLITE_NR];
static struct termios *uartlite_termios[SERIAL_UARTLITE_NR];
static struct termios *uartlite_termios_locked[SERIAL_UARTLITE_NR];

#if defined(CONFIG_SERIAL_UARTLITE_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif

static u_char *tmp_buf;
static DECLARE_MUTEX(tmp_buf_sem);

struct uartlite_icount {
	__u32	rx;
	__u32	tx;
	__u32	frame;
	__u32	overrun;
	__u32	parity;
	__u32	brk;
	__u32	buf_overrun;
};

struct uartlite_port {
	unsigned int 	uart_base;
	unsigned int    irq;
	unsigned int	uartclk;
	unsigned int 	fifosize;
	unsigned int 	tiocm_support;
	unsigned int 	cr;
};

struct uartlite_state {
	struct uartlite_icount	icount;
	unsigned int	line;
	unsigned int	close_delay;
	unsigned int	closing_wait;
	unsigned int	flags;
	struct termios	normal_termios;
	struct termios	callout_termios;

	int	count;
	struct uartlite_info *info;
};

struct uartlite_info {
	struct uartlite_port	*port;
	struct uartlite_state	*state;
	struct tty_struct		*tty;
	unsigned char			x_char;
	unsigned char			old_status;
	unsigned char			read_status_mask;
	unsigned char			ignore_status_mask;
	unsigned int 			xmit_busy;
	struct circ_buf			xmit;
	unsigned int			flags;
#ifdef SUPPORT_SYSRQ
	unsigned long			sysrq;
#endif

	unsigned int			event;
	unsigned int 			timeout;
	int 					blocked_open;
	pid_t					session;
	pid_t					pgrp;

	struct tasklet_struct	tlet;

	wait_queue_head_t   open_wait;
	wait_queue_head_t   close_wait;
	wait_queue_head_t   delta_msr_wait;

};

#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE
static struct condole uartlite_cons;
#endif
static void uartlite_wait_until_sent(struct tty_struct *tty, int timeout);

static struct uartlite_port uartlite_ports[SERIAL_UARTLITE_NR] = {
	{
		uart_base: 	0xf0000100,
		irq:		0,
	},
	{
		uart_base: 	0,
		irq:		0,
	}
};

static struct uartlite_state uartlite_state[SERIAL_UARTLITE_NR];

static void uartlite_enable_rx_interrupt(struct uartlite_info *info) {

	unsigned int cr;

	cr=UART_GET_CR(info->port);
	cr|=UART_ENABLE_INTR;
	UART_PUT_CR(info->port,cr);

}


static void uartlite_disable_rx_interrupt(struct uartlite_info *info) {

	unsigned int cr;

	cr=UART_GET_CR(info->port);
	cr|=UART_ENABLE_INTR;
	UART_PUT_CR(info->port,cr);

}

static void uartlite_stop(struct tty_struct *tty)
{
	struct uartlite_info *info=tty->driver_data;

	UART_PUT_CR(info->port,0);
	info->xmit_busy=0;

}

static void uartlite_start(struct tty_struct *tty)
{
	struct uartlite_info *info=tty->driver_data;
	info->xmit_busy=0;
}

/*
 * This routine is used by the interrupt handler to schedule
 * processing in the software interrupt portion of the driver.
 */
static void uartlite_event(struct uartlite_info *info, int event)
{
    info->event |= 1 << event;
    tasklet_schedule(&info->tlet);
}

static void
#ifdef SUPPORT_SYSRQ
uartlite_rx_chars(struct uartlite_info *info, struct pt_regs *regs)
#else
uartlite_rx_chars(struct uartlite_info *info)
#endif
{
	struct tty_struct *tty = info->tty;
	unsigned int status, ch, rsr, flg, ignored = 0;
	struct uartlite_port *port = info->port;
	struct uartlite_icount *icount=&info->state->icount;

	status=UART_GET_SR(port);
	while(UART_RX_DATA(status)) {
		ch=UART_GET_CHAR(port);

		if (tty->flip.count >= TTY_FLIPBUF_SIZE)
			goto ignore_char;

		icount->rx++;

		flg = TTY_NORMAL;

		if(status & UART_ERROR)
			goto handle_error;

#ifdef SUPPORT_SYSRQ
        if (info->sysrq) {
			if (ch && time_before(jiffies, info->sysrq)) {
				handle_sysrq(ch, regs, NULL, NULL);
				info->sysrq = 0;
				goto ignore_char;
			}
			info->sysrq = 0;
		}
#endif
		error_return:
			*tty->flip.flag_buf_ptr++ = flg;
			*tty->flip.char_buf_ptr++ = ch;
			tty->flip.count++;
		ignore_char:
			status=UART_GET_SR(port);
	}
out:
	tty_flip_buffer_push(tty);
	return ;

handle_error:
	if(rsr & UART_PAR_ERROR)
		icount->parity++;
	else if(rsr & UART_FRAME_ERROR)
		icount->frame++;

	if(rsr & UART_OVERRUN_ERROR)
		icount->overrun++;

	if (rsr & info->ignore_status_mask) {
		if (++ignored > 100)
			goto out;
		goto ignore_char;
	}

	rsr &= info->read_status_mask;

	if(rsr & UART_PAR_ERROR)
		flg=TTY_PARITY;
	else if(rsr & UART_FRAME_ERROR)
		flg=TTY_FRAME;

	if(rsr & UART_OVERRUN_ERROR) {
		*tty->flip.flag_buf_ptr++ = flg;
		*tty->flip.char_buf_ptr++ = ch;
		tty->flip.count++;
		if (tty->flip.count >= TTY_FLIPBUF_SIZE)
			goto ignore_char;
		ch=0;
		flg=TTY_OVERRUN;
	}
#ifdef SUPPORT_SYSRQ
	info->sysrq = 0;
#endif
	goto error_return;
}

static void uartlite_tx_chars(struct uartlite_info *info)
{
	struct uartlite_port *port=info->port;
	int count;

	if(info->x_char) {
		UART_PUT_CHAR(port, info->x_char);
		info->state->icount.tx++;
		info->x_char = 0;
		return ;
	}
	if (info->xmit.head == info->xmit.tail
		|| info->tty->stopped
		|| info->tty->hw_stopped) {
		info->xmit_busy=0;
		return ;
	}

	count=port->fifosize;

	do {
		UART_PUT_CHAR(port, info->xmit.buf[info->xmit.tail]);
		info->xmit.tail = (info->xmit.tail + 1) & (UARTLITE_XMIT_SIZE - 1);
		info->state->icount.tx++;
		if (info->xmit.head == info->xmit.tail)
			break;
	} while (--count > 0);

	if (CIRC_CNT(info->xmit.head,
				 info->xmit.tail,
				 UARTLITE_XMIT_SIZE) < WAKEUP_CHARS)
			uartlite_event(info, EVT_WRITE_WAKEUP);

}


static void uartlite_int(int irq, void *dev_id, struct pt_regs *regs)
{
	struct uartlite_info *info=dev_id;
	unsigned int status, pass_counter=0;

	status=UART_GET_SR(info->port);
	do {
		if(status & UART_RX_FIFO_VALID_DATA)
#ifdef SUPPORT_SYSRQ
			uartlite_rx_chars(info, regs);
#else
			uartlite_rx_chars(info);
#endif
		if(status & UART_TX_FIFO_EMPTY)
			uartlite_tx_chars(info);
		if(pass_counter++ > UARTLITE_ISR_PASS_LIMIT)
			break;

		status=UART_GET_SR(info->port);
	} while(status & (UART_TX_FIFO_EMPTY | UART_RX_FIFO_VALID_DATA));
}

static void uartlite_tasklet_action(unsigned long data)
{
	struct uartlite_info *info=(struct uartlite_info *)data;
	struct tty_struct *tty;

	tty=info->tty;

	if(!tty || !test_and_clear_bit(EVT_WRITE_WAKEUP, &info->event))
		return ;

	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
		tty->ldisc.write_wakeup)
		(tty->ldisc.write_wakeup)(tty);
	wake_up_interruptible(&tty->write_wait);
}

static int uartlite_startup(struct uartlite_info *info)
{
	unsigned long flags;
	unsigned long page;
	int retval=0;

	page = get_zeroed_page(GFP_KERNEL);
	if(!page)
		return -ENOMEM;

	save_flags(flags); cli();

	if (info->flags & ASYNC_INITIALIZED) {
		free_page(page);
		goto errout;
	}

	if (info->xmit.buf)
		free_page(page);
	else
		info->xmit.buf = (unsigned char *) page;

	/*
	 * Allocate IRQ
	 */
	retval = request_irq(info->port->irq, uartlite_int, 0, "uartlite", info);
	if (retval) {
		if (capable(CAP_SYS_ADMIN)) {
			if (info->tty)
				set_bit(TTY_IO_ERROR, &info->tty->flags);
			retval=0;
		}
		goto errout;
	}

	uartlite_enable_rx_interrupt(info);

	if(info->tty)
		clear_bit(TTY_IO_ERROR, &info->tty->flags);
    info->xmit.head = info->xmit.tail = 0;

	info->flags |= ASYNC_INITIALIZED;
    restore_flags(flags);
	return 0;

errout:
	restore_flags(flags);
	return retval;
}

static void uartlite_shutdown(struct uartlite_info *info)
{
	unsigned long flags;

    if (!(info->flags & ASYNC_INITIALIZED))
		return;

	save_flags(flags); cli(); /* Disable interrupts */

	free_irq(info->port->irq, info);

    if (info->xmit.buf) {
		unsigned long pg = (unsigned long) info->xmit.buf;
		info->xmit.buf = NULL;
		free_page(pg);
	}

    /*
	 *  disable all interrupts, disable the port
	 */

	UART_PUT_CR(info->port, 0);

	/* kill off our tasklet */
	tasklet_kill(&info->tlet);
	if(info->tty)
		set_bit(TTY_IO_ERROR, &info->tty->flags);

    info->flags &= ~ASYNC_INITIALIZED;
	restore_flags(flags);
}

static void uartlite_put_char(struct tty_struct *tty, u_char ch)
{
	struct uartlite_info *info= tty->driver_data;
	unsigned long flags;

	if (!tty || !info->xmit.buf)
		return ;

	save_flags(flags); cli();
	if (CIRC_SPACE(info->xmit.head, info->xmit.tail, UARTLITE_XMIT_SIZE) != 0) {
		info->xmit.buf[info->xmit.head] = ch;
		info->xmit.head = (info->xmit.head + 1) & (UARTLITE_XMIT_SIZE - 1);
	}
	restore_flags(flags);
}

static void uartlite_flush_chars(struct tty_struct *tty)
{

	struct uartlite_info *info=tty->driver_data;

    if (info->xmit.head == info->xmit.tail
        || tty->stopped
        || tty->hw_stopped
        || !info->xmit.buf)
        return;
}

static int uartlite_write(struct tty_struct *tty, int from_user,
						  const u_char * buf, int count)
{

	struct uartlite_info *info=tty->driver_data;
	unsigned long flags;
	int c,ret=0;

	if(!tty || !info->xmit.buf || !tmp_buf)
		return 0;

	save_flags(flags);
	if (from_user) {
		down(&tmp_buf_sem);
		while(1) {
			int c1;
			c=CIRC_SPACE_TO_END(info->xmit.head,
								info->xmit.tail,
								UARTLITE_XMIT_SIZE);
			if(count < c)
				c=count;
			if(c <= 0)
				break;

			c-=copy_from_user(tmp_buf, buf, c);
			if(!c) {
				if(!ret)
					ret=-EFAULT;
				break;
			}
			cli();
			c1=CIRC_SPACE_TO_END(info->xmit.head,
								 info->xmit.tail,
								 UARTLITE_XMIT_SIZE);
			if (c1 < c)
				c=c1;
			memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c);
			info->xmit.head = (info->xmit.head + c) &
							  (UARTLITE_XMIT_SIZE -1);
			restore_flags(flags);
			buf+=c;
			count-=c;
			ret+=c;
		}
		up(&tmp_buf_sem);
	} else {
		cli();
		while(1) {
			c = CIRC_SPACE_TO_END(info->xmit.head,
								  info->xmit.tail,
								  UARTLITE_XMIT_SIZE);
			if(count < c)
				c=count;
			if(c<=0)
				break;
			memcpy(info->xmit.buf + info->xmit.head, buf, c);
			info->xmit.head = (info->xmit.head + c) &
							(UARTLITE_XMIT_SIZE - 1);
			buf += c;
			count -= c;
			ret += c;
		}
		restore_flags(flags);
	}
	if (info->xmit.head != info->xmit.tail
			&& !tty->stopped
			&& !tty->hw_stopped
			&& !info->xmit_busy) {
			save_flags(flags);
			cli();
	        UART_PUT_CHAR(info->port, info->xmit.buf[info->xmit.tail]);
	        info->xmit.tail = (info->xmit.tail + 1) & (UARTLITE_XMIT_SIZE - 1);
	        info->state->icount.tx++;
			info->xmit_busy=1;
			restore_flags(flags);
	}

	return ret;
}

static int uartlite_write_room(struct tty_struct *tty) {

	struct uartlite_info *info=tty->driver_data;

	return CIRC_SPACE(info->xmit.head, info->xmit.tail, UARTLITE_XMIT_SIZE);

}

static int uartlite_chars_in_buffer(struct tty_struct *tty) {

	struct uartlite_info *info=tty->driver_data;

	return CIRC_CNT(info->xmit.head, info->xmit.tail, UARTLITE_XMIT_SIZE);

}

static void uartlite_flush_buffer(struct tty_struct *tty) {

	struct uartlite_info *info=tty->driver_data;
	unsigned long flags;

	save_flags(flags); cli();
	info->xmit.head = info->xmit.tail = 0;
	restore_flags(flags);
	wake_up_interruptible(&tty->write_wait);
    if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
		 tty->ldisc.write_wakeup)
		 (tty->ldisc.write_wakeup)(tty);
}

static void uartlite_send_xchar(struct tty_struct *tty, char ch) {

	struct uartlite_info *info = tty->driver_data;
	unsigned long flags;

	info->x_char = ch;
	if(ch && !info->xmit_busy) {
		save_flags(flags); cli();
		cli();
	    UART_PUT_CHAR(info->port, ch);
		info->xmit_busy=1;
		restore_flags(flags);
	}
}

static void uartlite_throttle(struct tty_struct *tty) {

    if (I_IXOFF(tty))
		uartlite_send_xchar(tty, STOP_CHAR(tty));
}

static void uartlite_unthrottle(struct tty_struct *tty) {

	struct uartlite_info *info = tty->driver_data;

	if (I_IXOFF(tty)) {
		if (info->x_char)
			info->x_char = 0;
		else
			uartlite_send_xchar(tty, STOP_CHAR(tty));
	}
}

static int get_serial_info(struct uartlite_info *info,
						   struct serial_struct *retinfo) {

	struct uartlite_state *state=info->state;
	struct uartlite_port *port=info->port;
	struct serial_struct tmp;

	memset(&tmp, 0, sizeof(tmp));
    tmp.type       = 0;
    tmp.line       = state->line;
    tmp.port       = port->uart_base;
    if (HIGH_BITS_OFFSET)
        tmp.port_high = port->uart_base >> HIGH_BITS_OFFSET;
    tmp.irq        = port->irq;
    tmp.flags      = 0;
    tmp.xmit_fifo_size = port->fifosize;
    tmp.close_delay    = state->close_delay;
    tmp.closing_wait   = state->closing_wait;

    if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
        return -EFAULT;
    return 0;
}

static int set_serial_info(struct uartlite_info *info,
			               struct serial_struct *newinfo) {

    struct serial_struct new_serial;
	struct uartlite_state *state, old_state;
	struct uartlite_port *port;
    unsigned long new_port;
    unsigned int i, change_irq, change_port;
    int retval = 0;

	if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
		return -EFAULT;

    state = info->state;
    old_state = *state;
    port = info->port;

	new_port = new_serial.port;
    if (HIGH_BITS_OFFSET)
		new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET;

	change_irq  = new_serial.irq != port->irq;
	change_port = new_port != port->uart_base;

	if (!capable(CAP_SYS_ADMIN)) {
		if (change_irq || change_port ||
			(new_serial.close_delay != state->close_delay) ||
			(new_serial.xmit_fifo_size != port->fifosize) ||
			((new_serial.flags & ~ASYNC_USR_MASK) !=
			 (state->flags & ~ASYNC_USR_MASK)))
			return -EPERM;
		state->flags = ((state->flags & ~ASYNC_USR_MASK) |
					(new_serial.flags & ASYNC_USR_MASK));
		info->flags = ((info->flags & ~ASYNC_USR_MASK) |
					(new_serial.flags & ASYNC_USR_MASK));
		goto check_and_exit;
	}

    if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0))
		return -EINVAL;

    if (new_serial.type && change_port) {
        for (i = 0; i < SERIAL_UARTLITE_NR; i++)
            if ((port != uartlite_ports + i) &&
                uartlite_ports[i].uart_base != new_port)
                return -EADDRINUSE;
    }

	if ((change_port || change_irq) && (state->count > 1))
		return -EBUSY;

    state->flags = ((state->flags & ~ASYNC_FLAGS) |
		            (new_serial.flags & ASYNC_FLAGS));
    info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) |
	               (info->flags & ASYNC_INTERNAL_FLAGS));
	state->close_delay = new_serial.close_delay * HZ / 100;
    state->closing_wait = new_serial.closing_wait * HZ / 100;
    info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
    port->fifosize = new_serial.xmit_fifo_size;

	if (change_port || change_irq) {
		uartlite_shutdown(info);
        port->irq = new_serial.irq;
        port->uart_base = new_port;
    }

check_and_exit:
    if (!port->uart_base)
        return 0;
	if ((info->flags & ASYNC_INITIALIZED)) {
		if ((old_state.flags & ASYNC_SPD_MASK) !=
			(state->flags & ASYNC_SPD_MASK)) {
				return 0;
		}
	}
	else
        retval = uartlite_startup(info);
	return retval;
}

static int get_lsr_info(struct uartlite_info *info, unsigned int *value) {

	unsigned int result, status;
    unsigned long flags;

	save_flags(flags); cli();
	status=UART_GET_SR(info->port);
	restore_flags(flags);
	result=status & UART_BUSY ? TIOCSER_TEMT : 0;

	if (info->x_char ||
		((CIRC_CNT(info->xmit.head, info->xmit.tail,
				 UARTLITE_XMIT_SIZE) > 0) &&
		 !info->tty->stopped && !info->tty->hw_stopped))
		result &= TIOCSER_TEMT;

    return put_user(result, value);
}

static int get_modem_info(struct uartlite_info *info, unsigned int *value) {

	return put_user(0, value);

}

static int set_modem_info(struct uartlite_info *info, unsigned int cmd,
				              unsigned int *value) {

	return 0;
}

static void uartlite_break_ctl(struct tty_struct *tty, int break_state) {

	return ;

}

static int uartlite_ioctl(struct tty_struct *tty, struct file *file,
						  unsigned int cmd, unsigned long arg)
{
	struct uartlite_info *info = tty->driver_data;
	struct uartlite_icount cnow;
    struct serial_icounter_struct icount;
	unsigned long flags;


    if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
        (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
        (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
        if (tty->flags & (1 << TTY_IO_ERROR))
            return -EIO;
    }

    switch (cmd) {
		case TIOCMGET:
			return get_modem_info(info, (unsigned int *)arg);
        case TIOCMBIS:
        case TIOCMBIC:
        case TIOCMSET:
            return set_modem_info(info, cmd, (unsigned int *)arg);
        case TIOCGSERIAL:
            return get_serial_info(info,
	                           (struct serial_struct *)arg);
		case TIOCSSERIAL:
			return set_serial_info(info, (struct serial_struct *)arg);
		case TIOCSERGETLSR: /* Get line status register */
			return get_lsr_info(info, (unsigned int *)arg);
		case TIOCMIWAIT:
			return 0;

		case TIOCGICOUNT:
			save_flags(flags); cli();
			cnow = info->state->icount;
			restore_flags(flags);
			icount.cts=icount.dsr=icount.rng=icount.dcd=icount.brk=0;
			icount.rx  = cnow.rx;
			icount.tx  = cnow.tx;
			icount.frame = cnow.frame;
			icount.overrun = cnow.overrun;
			icount.parity = cnow.parity;
			icount.buf_overrun = cnow.buf_overrun;

			return copy_to_user((void *)arg, &icount, sizeof(icount))
						? -EFAULT : 0;

		default:
			return -ENOIOCTLCMD;
	}
	return 0;
}

static void uartlite_set_termios(struct tty_struct *tty, struct termios *old_termios) {

	return ;

}

static void uartlite_close(struct tty_struct *tty, struct file *filp) {

	struct uartlite_info *info=tty->driver_data;
	struct uartlite_state *state;
	unsigned long flags;

	if (!info)
		return;

	state=info->state;

	save_flags(flags); cli();

	if (tty_hung_up_p(filp)) {
		MOD_DEC_USE_COUNT;
		restore_flags(flags);
		return ;
	}

	if ((tty->count == 1) && (state->count != 1)) {
        printk("uartlite_close: bad serial port count; tty->count is 1, "
			   "state->count is %d\n", state->count);
		state->count=1;
	}

	if (--state->count < 0) {
		printk("rs_close: bad serial port count for %s%d: %d\n",
				tty->driver.name, info->state->line, state->count);
		state->count = 0;
	}

	info->flags |= ASYNC_CLOSING;
	restore_flags(flags);

    if (info->flags & ASYNC_NORMAL_ACTIVE)
        info->state->normal_termios = *tty->termios;
    if (info->flags & ASYNC_CALLOUT_ACTIVE)
        info->state->callout_termios = *tty->termios;

	tty->closing = 1;
	if (info->state->closing_wait != ASYNC_CLOSING_WAIT_NONE)
		tty_wait_until_sent(tty, info->state->closing_wait);

    if (info->flags & ASYNC_INITIALIZED) {
        uartlite_disable_rx_interrupt(info);
        uartlite_wait_until_sent(tty, info->timeout);
    }

	uartlite_shutdown(info);
    if (tty->driver.flush_buffer)
        tty->driver.flush_buffer(tty);
    if (tty->ldisc.flush_buffer)
       tty->ldisc.flush_buffer(tty);
    tty->closing = 0;
    info->event = 0;
    info->tty = NULL;
    if (info->blocked_open) {
        if (info->state->close_delay) {
            set_current_state(TASK_INTERRUPTIBLE);
			schedule_timeout(info->state->close_delay);
		}
		wake_up_interruptible(&info->open_wait);
	}
    info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
					             ASYNC_CLOSING);
    wake_up_interruptible(&info->close_wait);
    MOD_DEC_USE_COUNT;
}

static void uartlite_wait_until_sent(struct tty_struct *tty, int timeout) {

	struct uartlite_info *info=(struct uartlite_info *)tty->driver_data;
    unsigned long char_time, expire;
    unsigned int status;

    if (info->port->fifosize == 0)
        return;

	char_time = (info->timeout - HZ/50) / info->port->fifosize;
	char_time = char_time / 5;
	if (char_time == 0)
		char_time=1;
	if (timeout && timeout < char_time)
        char_time = timeout;

	if (!timeout || timeout > 2 * info->timeout)
		timeout = 2 * info->timeout;

	expire = jiffies + timeout;

	while(UART_GET_SR(info->port) & UART_BUSY) {
		set_current_state(TASK_INTERRUPTIBLE);
		schedule_timeout(char_time);
		if (signal_pending(current))
			break;
		if (timeout && time_after(jiffies, expire))
			break;
		status = UART_GET_SR(info->port);
	}
    set_current_state(TASK_RUNNING);
}

static void uartlite_hangup(struct tty_struct *tty)
{
	struct uartlite_info *info = tty->driver_data;
	struct uartlite_state *state = info->state;

	uartlite_flush_buffer(tty);
	if (info->flags & ASYNC_CLOSING)
		return;
	uartlite_shutdown(info);
	info->event = 0;
	state->count = 0;
	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
	info->tty = NULL;
	wake_up_interruptible(&info->open_wait);
}

static int block_til_ready(struct tty_struct *tty, struct file *filp,
			   struct uartlite_info *info)
{
	DECLARE_WAITQUEUE(wait, current);
	struct uartlite_state *state = info->state;
	unsigned long flags;
	int do_clocal = 0, extra_count = 0, retval;

	/*
	 * If the device is in the middle of being closed, then block
	 * until it's done, and then try again.
	 */
	if (tty_hung_up_p(filp) ||
	    (info->flags & ASYNC_CLOSING)) {
		if (info->flags & ASYNC_CLOSING)
			interruptible_sleep_on(&info->close_wait);
		return (info->flags & ASYNC_HUP_NOTIFY) ?
			-EAGAIN : -ERESTARTSYS;
	}

	/*
	 * If this is a callout device, then just make sure the normal
	 * device isn't being used.
	 */
	if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
		if (info->flags & ASYNC_NORMAL_ACTIVE)
			return -EBUSY;
		if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
		    (info->flags & ASYNC_SESSION_LOCKOUT) &&
		    (info->session != current->session))
			return -EBUSY;
		if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
		    (info->flags & ASYNC_PGRP_LOCKOUT) &&
		    (info->pgrp != current->pgrp))
			return -EBUSY;
		info->flags |= ASYNC_CALLOUT_ACTIVE;
		return 0;
	}

	/*
	 * If non-blocking mode is set, or the port is not enabled,
	 * then make the check up front and then exit.
	 */
	if ((filp->f_flags & O_NONBLOCK) ||
	    (tty->flags & (1 << TTY_IO_ERROR))) {
		if (info->flags & ASYNC_CALLOUT_ACTIVE)
			return -EBUSY;
		info->flags |= ASYNC_NORMAL_ACTIVE;
		return 0;
	}

	if (info->flags & ASYNC_CALLOUT_ACTIVE) {
		if (state->normal_termios.c_cflag & CLOCAL)
			do_clocal = 1;
	} else {
		if (tty->termios->c_cflag & CLOCAL)
			do_clocal = 1;
	}

	/*
	 * Block waiting for the carrier detect and the line to become
	 * free (i.e., not in use by the callout).  While we are in
	 * this loop, state->count is dropped by one, so that
	 * rs_close() knows when to free things.  We restore it upon
	 * exit, either normal or abnormal.
	 */
	retval = 0;
	add_wait_queue(&info->open_wait, &wait);
	save_flags(flags); cli();
	if (!tty_hung_up_p(filp)) {
		extra_count = 1;
		state->count--;
	}
	restore_flags(flags);
	info->blocked_open++;
	while (1) {
		save_flags(flags); cli();
		restore_flags(flags);
		set_current_state(TASK_INTERRUPTIBLE);
		if (tty_hung_up_p(filp) ||
		    !(info->flags & ASYNC_INITIALIZED)) {
			if (info->flags & ASYNC_HUP_NOTIFY)
				retval = -EAGAIN;
			else
				retval = -ERESTARTSYS;
			break;
		}
		if (signal_pending(current)) {
			retval = -ERESTARTSYS;
			break;
		}
		schedule();
	}
	set_current_state(TASK_RUNNING);
	remove_wait_queue(&info->open_wait, &wait);
	if (extra_count)
		state->count++;
	info->blocked_open--;
	if (retval)
		return retval;
	info->flags |= ASYNC_NORMAL_ACTIVE;
	return 0;
}

static struct uartlite_info *uartlite_get(int line)
{
	struct uartlite_info *info;
	struct uartlite_state *state = uartlite_state + line;

	state->count++;
	if (state->info)
		return state->info;
	info = kmalloc(sizeof(struct uartlite_info), GFP_KERNEL);
	if (info) {
		memset(info, 0, sizeof(struct uartlite_info));
		init_waitqueue_head(&info->open_wait);
		init_waitqueue_head(&info->close_wait);
		init_waitqueue_head(&info->delta_msr_wait);
		info->flags = state->flags;
		info->state = state;
		info->port  = uartlite_ports + line;
		tasklet_init(&info->tlet, uartlite_tasklet_action,
			     (unsigned long)info);
	}
	if (state->info) {
		kfree(info);
		return state->info;
	}
	state->info = info;
	return info;
}

static int uartlite_open(struct tty_struct *tty, struct file *filp)
{
	struct uartlite_info *info;
	int retval, line = MINOR(tty->device) - tty->driver.minor_start;

	// is this a line that we've got?
	MOD_INC_USE_COUNT;
	if (line >= SERIAL_UARTLITE_NR) {
		MOD_DEC_USE_COUNT;
		return -ENODEV;
	}

	info = uartlite_get(line);
	if (!info)
		return -ENOMEM;

	tty->driver_data = info;
	info->tty = tty;
	info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;

	/*
	 * Make sure we have the temporary buffer allocated
	 */
	if (!tmp_buf) {
		unsigned long page = get_zeroed_page(GFP_KERNEL);
		if (tmp_buf)
			free_page(page);
		else if (!page) {
			MOD_DEC_USE_COUNT;
			return -ENOMEM;
		}
		tmp_buf = (u_char *)page;
	}

	/*
	 * If the port is in the middle of closing, bail out now.
	 */
	if (tty_hung_up_p(filp) ||
	    (info->flags & ASYNC_CLOSING)) {
		if (info->flags & ASYNC_CLOSING)
			interruptible_sleep_on(&info->close_wait);
		MOD_DEC_USE_COUNT;
		return -EAGAIN;
	}

	/*
	 * Start up the serial port
	 */
	retval = uartlite_startup(info);
	if (retval) {
		MOD_DEC_USE_COUNT;
		return retval;
	}

	retval = block_til_ready(tty, filp, info);
	if (retval) {
		MOD_DEC_USE_COUNT;
		return retval;
	}

	if ((info->state->count == 1) &&
	    (info->flags & ASYNC_SPLIT_TERMIOS)) {
		if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
			*tty->termios = info->state->normal_termios;
		else
			*tty->termios = info->state->callout_termios;
	}
#ifdef CONFIG_SERIAL_AMBA_CONSOLE
	if (uartlite_cons.cflag && uartlite_cons.index == line) {
		tty->termios->c_cflag = uartlite_cons.cflag;
		uartlite_cons.cflag = 0;
	}
#endif

	info->session = current->session;
	info->pgrp = current->pgrp;
	return 0;
}

int __init uartlite_init(void)
{
	int i;

	uartlite_driver.magic = TTY_DRIVER_MAGIC;
	uartlite_driver.driver_name = "serial_uartlite";
	uartlite_driver.name = SERIAL_UARTLITE_NAME;
	uartlite_driver.major = SERIAL_UARTLITE_MAJOR;
	uartlite_driver.minor_start = SERIAL_UARTLITE_MINOR;
	uartlite_driver.num = SERIAL_UARTLITE_NR;
	uartlite_driver.type = TTY_DRIVER_TYPE_SERIAL;
	uartlite_driver.subtype = SERIAL_TYPE_NORMAL;
	uartlite_driver.init_termios = tty_std_termios;
	uartlite_driver.init_termios.c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL;
	uartlite_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
	uartlite_driver.refcount = &uartlite_refcount;
	uartlite_driver.table = uartlite_table;
	uartlite_driver.termios = uartlite_termios;
	uartlite_driver.termios_locked = uartlite_termios_locked;

	uartlite_driver.open = uartlite_open;
	uartlite_driver.close = uartlite_close;
	uartlite_driver.write = uartlite_write;
	uartlite_driver.put_char = uartlite_put_char;
	uartlite_driver.flush_chars = uartlite_flush_chars;
	uartlite_driver.write_room = uartlite_write_room;
	uartlite_driver.chars_in_buffer = uartlite_chars_in_buffer;
	uartlite_driver.flush_buffer	= uartlite_flush_buffer;
	uartlite_driver.ioctl = uartlite_ioctl;
	uartlite_driver.throttle = uartlite_throttle;
	uartlite_driver.unthrottle = uartlite_unthrottle;
	uartlite_driver.send_xchar = uartlite_send_xchar;
	uartlite_driver.set_termios = uartlite_set_termios;
	uartlite_driver.stop = uartlite_stop;
	uartlite_driver.start = uartlite_start;
	uartlite_driver.hangup = uartlite_hangup;
	uartlite_driver.break_ctl = uartlite_break_ctl;
	uartlite_driver.wait_until_sent = uartlite_wait_until_sent;
	uartlite_driver.read_proc = NULL;

	/*
	 * The callout device is just like the normal device except for
	 * the major number and the subtype code.
	 */
	uartlitecallout_driver = uartlite_driver;
	uartlitecallout_driver.name = CALLOUT_UARTLITE_NAME;
	uartlitecallout_driver.major = CALLOUT_UARTLITE_MAJOR;
	uartlitecallout_driver.subtype = SERIAL_TYPE_CALLOUT;
	uartlitecallout_driver.read_proc = NULL;
	uartlitecallout_driver.proc_entry = NULL;

	if (tty_register_driver(&uartlite_driver))
		panic("Couldn't register uartlite serial driver\n");
	if (tty_register_driver(&uartlitecallout_driver))
		panic("Couldn't register uartlite callout driver\n");

	for (i = 0; i < SERIAL_UARTLITE_NR; i++) {
		struct uartlite_state *state = uartlite_state + i;
		state->line		= i;
		state->close_delay	= 5 * HZ / 10;
		state->closing_wait	= 30 * HZ;
		state->callout_termios	= uartlitecallout_driver.init_termios;
		state->normal_termios	= uartlite_driver.init_termios;
	}

	return 0;
}

__initcall(uartlite_init);

static void uartlite_console_write(struct console *co, const char *s, u_int count) {

	struct uartlite_port *port=&uartlite_ports[co->index];
	unsigned int status, old_cr;
	int i;

	old_cr=UART_GET_CR(port);
	UART_PUT_CR(port,0);

	for (i = 0; i < count; i++) {
		do {
			status=UART_GET_SR(port);
		} while(status & 0x8);
		UART_PUT_CHAR(port,s[i]);
		if (s[i] == '\n') {
			do {
				status=UART_GET_SR(port);
			} while(status & 0x8);
			UART_PUT_CHAR(port,'\r');
		}
	}

	do {
		status=UART_GET_SR(port);
	} while(!(status & 0x4));
	UART_PUT_CR(port,old_cr);

}

static kdev_t uartlite_console_device(struct console *c) {

	return MKDEV(SERIAL_UARTLITE_MAJOR, SERIAL_UARTLITE_MINOR + c->index);

}

static int __init uartlite_console_setup(struct console *co, char *options) {

	struct uartlite_port *port=&uartlite_ports[co->index];

	UART_PUT_CR(port,0);

	if (co->index >= SERIAL_UARTLITE_NR)
		co->index=0;

	return 0;

}

static struct console xilinxuart_cons =
{
	.name = "ttyUL",
	.write = uartlite_console_write,
	.device = uartlite_console_device,
	.setup = uartlite_console_setup,
	.flags = CON_PRINTBUFFER,
	.index = -1,
};

void __init xilinxuart_console_init(void)
{
	register_console(&xilinxuart_cons);
}

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2003-02-24 19:50 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2003-02-24 19:39 UART lite driver? Kerl, John
2003-02-24 19:50 ` Peter 'p2' De Schrijver

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.