/*
* linux/drivers/char/riscom.c -- RISCom/8 multiport serial driver.
*
* Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com)
*
* This code is loosely based on the Linux serial driver, written by
* Linus Torvalds, Theodore T'so and others. The RISCom/8 card
* programming info was obtained from various drivers for other OSes
* (FreeBSD, ISC, etc), but no source code from those drivers were
* directly included in this driver.
*
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Revision 1.1
*
* ChangeLog:
* Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 27-Jun-2001
* - get rid of check_region and several cleanups
*/
#include <linux/module.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/tty.h>
#include <linux/mm.h>
#include <linux/serial.h>
#include <linux/fcntl.h>
#include <linux/major.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/tty_flip.h>
#include <linux/spinlock.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include "riscom8.h"
#include "riscom8_reg.h"
/* Am I paranoid or not ? ;-) */
#define RISCOM_PARANOIA_CHECK
/*
* Crazy InteliCom/8 boards sometimes have swapped CTS & DSR signals.
* You can slightly speed up things by #undefing the following option,
* if you are REALLY sure that your board is correct one.
*/
#define RISCOM_BRAIN_DAMAGED_CTS
/*
* The following defines are mostly for testing purposes. But if you need
* some nice reporting in your syslog, you can define them also.
*/
#undef RC_REPORT_FIFO
#undef RC_REPORT_OVERRUN
#define RISCOM_LEGAL_FLAGS \
(ASYNC_HUP_NOTIFY | ASYNC_SAK | ASYNC_SPLIT_TERMIOS | \
ASYNC_SPD_HI | ASYNC_SPEED_VHI | ASYNC_SESSION_LOCKOUT | \
ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP)
static struct tty_driver *riscom_driver;
static DEFINE_SPINLOCK(riscom_lock);
static struct riscom_board rc_board[RC_NBOARD] = {
{
.base = RC_IOBASE1,
},
{
.base = RC_IOBASE2,
},
{
.base = RC_IOBASE3,
},
{
.base = RC_IOBASE4,
},
};
static struct riscom_port rc_port[RC_NBOARD * RC_NPORT];
/* RISCom/8 I/O ports addresses (without address translation) */
static unsigned short rc_ioport[] = {
#if 1
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09, 0x0a, 0x0b, 0x0c,
#else
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09, 0x0a, 0x0b, 0x0c, 0x10,
0x11, 0x12, 0x18, 0x28, 0x31, 0x32, 0x39, 0x3a, 0x40, 0x41, 0x61, 0x62,
0x63, 0x64, 0x6b, 0x70, 0x71, 0x78, 0x7a, 0x7b, 0x7f, 0x100, 0x101
#endif
};
#define RC_NIOPORT ARRAY_SIZE(rc_ioport)
static int rc_paranoia_check(struct riscom_port const *port,
char *name, const char *routine)
{
#ifdef RISCOM_PARANOIA_CHECK
static const char badmagic[] = KERN_INFO
"rc: Warning: bad riscom port magic number for device %s in %s\n";
static const char badinfo[] = KERN_INFO
"rc: Warning: null riscom port for device %s in %s\n";
if (!port) {
printk(badinfo, name, routine);
return 1;
}
if (port->magic != RISCOM8_MAGIC) {
printk(badmagic, name, routine);
return 1;
}
#endif
return 0;
}
/*
*
* Service functions for RISCom/8 driver.
*
*/
/* Get board number from pointer */
static inline int board_No(struct riscom_board const *bp)
{
return bp - rc_board;
}
/* Get port number from pointer */
static inline int port_No(struct riscom_port const *port)
{
return RC_PORT(port - rc_port);
}
/* Get pointer to board from pointer to port */
static inline struct riscom_board *port_Board(struct riscom_port const *port)
{
return &rc_board[RC_BOARD(port - rc_port)];
}
/* Input Byte from CL CD180 register */
static inline unsigned char rc_in(struct riscom_board const *bp,
unsigned short reg)
{
return inb(bp->base + RC_TO_ISA(reg));
}
/* Output Byte to CL CD180 register */
static inline void rc_out(struct riscom_board const *bp, unsigned short reg,
unsigned char val)
{
outb(val, bp->base + RC_TO_ISA(reg));
}
/* Wait for Channel Command Register ready */
static void rc_wait_CCR(struct riscom_board const *bp)
{
unsigned long delay;
/* FIXME: need something more descriptive then 100000 :) */
for (delay = 100000; delay; delay--)
if (!rc_in(bp, CD180_CCR))
return;
printk(KERN_INFO "rc%d: Timeout waiting for CCR.\n", board_No(bp));
}
/*
* RISCom/8 probe functions.
*/
static int rc_request_io_range(struct riscom_board * const bp)
{
int i;
for (i = 0; i < RC_NIOPORT; i++)
if (!request_region(RC_TO_ISA(rc_ioport[i]) + bp->base, 1,
"RISCom/8")) {
goto out_release;
}
return 0;
out_release:
printk(KERN_INFO "rc%d: Skipping probe at 0x%03x. IO address in use.\n",
board_No(bp), bp->base);
while (--i >= 0)
release_region(RC_TO_ISA(rc_ioport[i]) + bp->base, 1);
return 1;
}
static void rc_release_io_range(struct riscom_board * const bp)
{
int i;
for (i = 0; i < RC_NIOPORT; i++)
release_region(RC_TO_ISA(rc_ioport[i]) + bp->base, 1);
}
/* Reset and setup CD180 chip */
static void __init rc_init_CD180(struct riscom_board const *bp)
{
unsigned long flags;
spin_lock_irqsave(&riscom_lock, flags);
rc_out(bp, RC_CTOUT, 0); /* Clear timeout */
rc_wait_CCR(bp); /* Wait for CCR ready */
rc_out(bp, CD180_CCR, CCR_HARDRESET); /* Reset CD180 chip */
spin_unlock_irqrestore(&riscom_lock, flags);
msleep(50); /* Delay 0.05 sec */
spin_lock_irqsave(&riscom_lock, flags);
rc_out(bp, CD180_GIVR, RC_ID); /* Set ID for this chip */
rc_out(bp, CD180_GICR, 0); /* Clear all bits */
rc_out(bp, CD180_PILR1, RC
|