blob: cc157a19358a043cc6783d55244ce8e4f1dc9397 [file] [log] [blame]
/*
** mux.c:
** MUX console for the NOVA and K-Class systems.
**
** (c) Copyright 2002 Ryan Bradetich
** (c) Copyright 2002 Hewlett-Packard Company
**
** 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 Driver used Christoph Plattner's pdc_console.c as a driver
** template.
**
** This Driver currently only supports the console (port 0) on the MUX.
** Additional work will be needed on this driver to enable the full
** functionality of the MUX.
**
*/
static char *mux_drv_version = "0.1";
#include <linux/config.h>
#include <linux/version.h>
#undef SERIAL_PARANOIA_CHECK
#define CONFIG_SERIAL_NOPAUSE_IO
#define SERIAL_DO_RESTART
#include <linux/module.h>
#include <linux/serial.h>
#include <linux/serialP.h>
#include <linux/tty.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#ifdef CONFIG_MAGIC_SYSRQ
#include <linux/sysrq.h>
static unsigned long break_pressed;
#endif
#ifdef CONFIG_GSC
#include <asm/gsc.h>
#endif
static unsigned long hpa;
#define MUX_OFFSET 0x800
#define MUX_LINE_OFFSET 0x80
#define MUX_FIFO_SIZE 255
#define MUX_MIN_FREE_SIZE 32
#define MUX_FIFO_DRAIN_DELAY 1
#define MUX_POLL_DELAY (30 * HZ / 1000)
#define IO_COMMAND_REG_OFFSET 0x30
#define IO_STATUS_REG_OFFSET 0x34
#define IO_DATA_REG_OFFSET 0x3c
#define IO_DCOUNT_REG_OFFSET 0x40
#define IO_UCOUNT_REG_OFFSET 0x44
#define IO_FIFOS_REG_OFFSET 0x48
#define MUX_EOFIFO(status) ((status & 0xF000) == 0xF000)
#define MUX_STATUS(status) ((status & 0xF000) == 0x8000)
#define MUX_BREAK(status) ((status & 0xF000) == 0x2000)
static int mux_drv_refcount; /* = 0 */
static struct tty_driver mux_drv_driver;
static struct async_struct *mux_drv_info;
static struct timer_list mux_drv_timer;
#define NR_PORTS 1
static struct tty_struct *mux_drv_table[NR_PORTS];
static struct termios *mux_drv_termios[NR_PORTS];
static struct termios *mux_drv_termios_locked[NR_PORTS];
/**
* mux_read_fifo - Read chars from the mux fifo.
* @info: Ptr to the async structure.
*
* This reads all available data from the mux's fifo and pushes
* the data to the tty layer.
*/
static void
mux_read_fifo(struct async_struct *info)
{
int data;
struct tty_struct *tty = info->tty;
while(1) {
data = __raw_readl((unsigned long)info->iomem_base
+ IO_DATA_REG_OFFSET);
if (MUX_STATUS(data))
continue;
if (MUX_EOFIFO(data))
break;
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
continue;
*tty->flip.char_buf_ptr = data & 0xffu;
*tty->flip.flag_buf_ptr = 0;
#ifdef CONFIG_MAGIC_SYSRQ
if (MUX_BREAK(data) && !break_pressed) {
break_pressed = jiffies;
continue;
}
if(MUX_BREAK(data)) {
*tty->flip.flag_buf_ptr = TTY_BREAK;
}
if(break_pressed) {
if(time_before(jiffies, break_pressed + HZ * 5)) {
handle_sysrq(data & 0xffu, NULL, NULL, NULL);
break_pressed = 0;
continue;
}
break_pressed = 0;
}
#endif
tty->flip.flag_buf_ptr++;
tty->flip.char_buf_ptr++;
tty->flip.count++;
}
tty_flip_buffer_push(tty);
}
/**
* mux_drv_poll - Mux poll function.
* @unused: Unused variable
*
* This function periodically polls the Mux to check for new data.
*/
static void
mux_drv_poll(unsigned long unused)
{
struct async_struct *info = mux_drv_info;
if(info && info->tty && mux_drv_refcount) {
mux_read_fifo(info);
info->last_active = jiffies;
}
mod_timer(&mux_drv_timer, jiffies + MUX_POLL_DELAY);
}
/**
* mux_chars_in_buffer - Returns the number of chars present in the outbound fifo.
* @tty: Ptr to the tty structure.
*
* This function returns the number of chars sitting in the outbound fifo.
* [Note: This function is required for the normal_poll function in
* drivers/char/n_tty.c].
*/
static int
mux_chars_in_buffer(struct tty_struct *tty)
{
struct async_struct *info = (struct async_struct *)tty->driver_data;
return __raw_readl((unsigned long)info->iomem_base
+ IO_DCOUNT_REG_OFFSET);
}
/**
* mux_flush_buffer - Pause until the fifo is empty.
* @tty: Ptr to the tty structure.
*
* Since the mux fifo is self draining, this function just
* waits until the fifo has completely drained.
*/
static void
mux_flush_buffer(struct tty_struct *tty)
{
while(mux_chars_in_buffer(tty))
mdelay(MUX_FIFO_DRAIN_DELAY);
}
/**
* mux_write_room - How much room is left in the fifo.
* @tty: Ptr to the tty structure.
*
* This function returns how much room is in the fifo for
* writing.
*/
static int
mux_write_room(struct tty_struct *tty)
{
int room = mux_chars_in_buffer(tty);
if(room > MUX_FIFO_SIZE)
return 0;
return MUX_FIFO_SIZE - room;
}
/**
* mux_write - Write chars to the mux fifo.
* @tty: Ptr to the tty structure.
* @from_user: Is the buffer from user space?
* @buf: The buffer to write to the mux fifo.
* @count: The number of chars to write to the mux fifo.
*
* This function writes the data from buf to the mux fifo.
* [Note: we need the mux_flush_buffer() at the end of the
* function, otherwise the system will wait for LONG_MAX
* if the fifo is not empty when the TCSETSW ioctl is called.]
*/
static int
mux_write(struct tty_struct *tty, int from_user,
const unsigned char *buf, int count)
{
int size, len, ret = count;
char buffer[MUX_FIFO_SIZE], *buf_p;
unsigned long iomem_base =
(unsigned long)((struct async_struct *)tty->driver_data)->iomem_base;
while (count) {
size = mux_write_room(tty);
len = (size < count) ? size : count;
if (from_user) {
copy_from_user(buffer, buf, len);
buf_p = buffer;
} else {
buf_p = (char *)buf;
}
count -= len;
buf += len;
if(size < MUX_MIN_FREE_SIZE)
mux_flush_buffer(tty);
while(len--) {
__raw_writel(*buf_p++, iomem_base + IO_DATA_REG_OFFSET);
}
}
mux_flush_buffer(tty);
return ret;
}
/**
* mux_break - Turn break handling on or off.
* @tty: Ptr to the tty structure.
* @break_state: break value.
*
* This function must be defined because the send_break() in
* drivers/char/tty_io.c requires it. Currently the Serial Mux
* does nothing when this function is called.
*/
static void
mux_break(struct tty_struct *tty, int break_state)
{
}
/**
* get_serial_info - Return the serial structure to userspace.
* @info: Ptr to the async structure.
* @retinfo: Ptr to the users space buffer.
*
* Fill in this serial structure and return it to userspace.
*/
static int
get_serial_info(struct async_struct *info,
struct serial_struct *retinfo)
{
struct serial_struct tmp;
if (!retinfo)
return -EFAULT;
memset(&tmp, 0, sizeof(tmp));
tmp.line = info->line;
tmp.port = info->line;
tmp.flags = info->flags;
tmp.close_delay = info->close_delay;
return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0;
}
/**
* get_modem_info - Return the modem control and status signals to userspace.
* @info: Ptr to the async structure.
* @value: The return buffer.
*
* The Serial MUX driver always returns these values to userspace:
* Data Terminal Ready, Carrier Detect, Clear To Send,
* Request To Send.
*
*/
static int
get_modem_info(struct async_struct *info, unsigned int *value)
{
unsigned int result = TIOCM_DTR|TIOCM_CAR|TIOCM_CTS|TIOCM_RTS;
return copy_to_user(value, &result, sizeof(int)) ? -EFAULT : 0;
}
/**
* get_lsr_info - Return line status register info to userspace.
* @info: Ptr to the async structure.
* @value: The return buffer.
*
* The Serial MUX driver always returns empty transmitter to userspace.
*/
static int
get_lsr_info(struct async_struct *info, unsigned int *value)
{
unsigned int result = TIOCSER_TEMT;
return copy_to_user(value, &result, sizeof(int)) ? -EFAULT : 0;
}
/**
* mux_ioctl - Handle driver specific ioctl commands.
* @tty: Ptr to the tty structure.
* @file: Unused.
* @cmd: The ioctl number.
* @arg: The ioctl argument.
*
* This function handles ioctls specific to the Serial MUX driver,
* or ioctls that need driver specific information.
*
*/
static int
mux_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct async_struct *info = (struct async_struct *) tty->driver_data;
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 0;
case TIOCGSERIAL:
return get_serial_info(info, (struct serial_struct *) arg);
case TIOCSSERIAL:
return 0;
case TIOCSERCONFIG:
return 0;
case TIOCSERGETLSR:
return get_lsr_info(info, (unsigned int *) arg);
case TIOCSERGSTRUCT:
if (copy_to_user((struct async_struct *) arg,
info, sizeof (struct async_struct)))
return -EFAULT;
return 0;
case TIOCMIWAIT:
return 0;
case TIOCGICOUNT:
return 0;
case TIOCSERGWILD:
case TIOCSERSWILD:
/* "setserial -W" is called in Debian boot */
printk("TIOCSER?WILD ioctl obsolete, ignored.\n");
return 0;
default:
return -ENOIOCTLCMD;
}
return 0;
}
/**
* mux_close - Close the serial mux driver.
* @tty: Ptr to the tty structure.
* @filp: Unused.
*
* This routine is called when the serial port gets closed. First, we
* wait for the last remaining data to be sent. Then, we unlink its
* async structure from the interrupt chain if necessary, and we free
* that IRQ if nothing is left in the chain.
*/
static void
mux_close(struct tty_struct *tty, struct file *filp)
{
struct async_struct *info = (struct async_struct *) tty->driver_data;
mux_drv_refcount--;
if (mux_drv_refcount > 0)
return;
info->flags |= ASYNC_CLOSING;
/*
* Save the termios structure, since this port may have
* separate termios for callout and dialin.
*/
if (info->flags & ASYNC_NORMAL_ACTIVE)
info->state->normal_termios = *tty->termios;
if (info->flags & ASYNC_CALLOUT_ACTIVE)
info->state->callout_termios = *tty->termios;
/*
* At this point we stop accepting input. To do this, we
* disable the receive line status interrupts, and tell the
* interrupt driver to stop checking the data ready bit in the
* line status register.
*/
/* XXX CP: make mask for receive !!! */
if (tty->driver.flush_buffer)
tty->driver.flush_buffer(tty);
tty_ldisc_flush(tty);
tty->closing = 0;
info->event = 0;
info->tty = 0;
mux_drv_info = NULL;
if (info->blocked_open) {
if (info->close_delay) {
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(info->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;
}
/**
* get_async_struct - Get the async structure.
* @line: Minor number of the tty device.
* @ret_info: Ptr to the newly allocated async structure.
*
* Allocate and return an async structure for the specified
* tty device line.
*/
static int
get_async_struct(int line, struct async_struct **ret_info)
{
struct async_struct *info;
info = kmalloc(sizeof (struct async_struct), GFP_KERNEL);
if (!info) {
return -ENOMEM;
}
memset(info, 0, sizeof (struct async_struct));
init_waitqueue_head(&info->open_wait);
init_waitqueue_head(&info->close_wait);
init_waitqueue_head(&info->delta_msr_wait);
info->magic = SERIAL_MAGIC;
info->port = 0;
info->flags = 0;
info->io_type = 0;
info->iomem_base = (void *)(hpa + MUX_OFFSET);
info->iomem_reg_shift = 0;
info->xmit_fifo_size = MUX_FIFO_SIZE;
info->line = line;
info->tqueue.routine = NULL;
info->tqueue.data = info;
info->state = NULL;
*ret_info = info;
return 0;
}
/**
* mux_open - Open the serial mux driver.
* @tty: Ptr to the tty structure.
* @filp: Unused.
*
* This routine is called whenever a serial port is opened. It
* enables interrupts for a serial port, linking in its async structure
* into the IRQ chain. It also performs the serial-specific
* initialization for the tty structure.
*/
static int
mux_open(struct tty_struct *tty, struct file *filp)
{
struct async_struct *info;
int retval, line;
MOD_INC_USE_COUNT;
line = MINOR(tty->device) - tty->driver.minor_start;
if ((line < 0) || (line >= NR_PORTS)) {
MOD_DEC_USE_COUNT;
return -ENODEV;
}
retval = get_async_struct(line, &info);
if (retval) {
MOD_DEC_USE_COUNT;
return retval;
}
tty->driver_data = info;
info->tty = tty;
mux_drv_info = info;
info->tty->low_latency = 0;
info->session = current->session;
info->pgrp = current->pgrp;
mux_drv_refcount++;
return 0;
}
/**
* mux_probe - Determine if the Serial Mux should claim this device.
* @dev: The parisc device.
*
* Deterimine if the Sserial Mux should claim this chip (return 0)
* or not (return 1).
*/
static int __init
mux_probe(struct parisc_device *dev)
{
if(hpa) {
printk(KERN_INFO "Serial MUX driver already registered, skipping additonal MUXes for now.\n");
return 1;
}
init_timer(&mux_drv_timer);
mux_drv_timer.function = mux_drv_poll;
mod_timer(&mux_drv_timer, jiffies + MUX_POLL_DELAY);
hpa = dev->hpa;
printk(KERN_INFO "Serial MUX driver version %s at 0x%lx\n",
mux_drv_version, hpa);
/* Initialize the tty_driver structure */
memset(&mux_drv_driver, 0, sizeof (struct tty_driver));
mux_drv_driver.magic = TTY_DRIVER_MAGIC;
mux_drv_driver.driver_name = "Serial MUX driver";
#ifdef CONFIG_DEVFS_FS
mux_drv_driver.name = "ttb/%d";
#else
mux_drv_driver.name = "ttyB";
#endif
mux_drv_driver.major = MUX_MAJOR;
mux_drv_driver.minor_start = 0;
mux_drv_driver.num = NR_PORTS;
mux_drv_driver.type = TTY_DRIVER_TYPE_SERIAL;
mux_drv_driver.subtype = SERIAL_TYPE_NORMAL;
mux_drv_driver.init_termios = tty_std_termios;
mux_drv_driver.init_termios.c_cflag =
B9600 | CS8 | CREAD | HUPCL | CLOCAL;
mux_drv_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
mux_drv_driver.refcount = &mux_drv_refcount;
mux_drv_driver.table = mux_drv_table;
mux_drv_driver.termios = mux_drv_termios;
mux_drv_driver.termios_locked = mux_drv_termios_locked;
mux_drv_driver.open = mux_open;
mux_drv_driver.close = mux_close;
mux_drv_driver.write = mux_write;
mux_drv_driver.put_char = NULL;
mux_drv_driver.flush_chars = NULL;
mux_drv_driver.write_room = mux_write_room;
mux_drv_driver.chars_in_buffer = mux_chars_in_buffer;
mux_drv_driver.flush_buffer = mux_flush_buffer;
mux_drv_driver.ioctl = mux_ioctl;
mux_drv_driver.throttle = NULL;
mux_drv_driver.unthrottle = NULL;
mux_drv_driver.set_termios = NULL;
mux_drv_driver.stop = NULL;
mux_drv_driver.start = NULL;
mux_drv_driver.hangup = NULL;
mux_drv_driver.break_ctl = mux_break;
mux_drv_driver.send_xchar = NULL;
mux_drv_driver.wait_until_sent = NULL;
mux_drv_driver.read_proc = NULL;
if (tty_register_driver(&mux_drv_driver))
panic("Could not register the serial MUX driver\n");
return 0;
}
static struct parisc_device_id mux_tbl[] = {
{ HPHW_A_DIRECT, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0000D },
{ 0, }
};
MODULE_DEVICE_TABLE(parisc, mux_tbl);
static struct parisc_driver mux_driver = {
name: "Serial MUX driver",
id_table: mux_tbl,
probe: mux_probe,
};
/**
* mux_init - Serial MUX initalization procedure.
*
* Register the Serial MUX driver.
*/
static int __init mux_init(void)
{
return register_parisc_driver(&mux_driver);
}
/**
* mux_exit - Serial MUX cleanup procedure.
*
* Unregister the Serial MUX driver from the tty layer.
*/
static void __exit mux_exit(void)
{
int status = tty_unregister_driver(&mux_drv_driver);
if(status) {
printk("MUX: failed to unregister the Serial MUX driver (%d)\n", status);
}
}
module_init(mux_init);
module_exit(mux_exit);
MODULE_DESCRIPTION("Serial MUX driver");
MODULE_AUTHOR("Ryan Bradetich");
MODULE_LICENSE("GPL");