blob: 2bbd44c2e86801fa22b4b69768416f4b3ed484fd [file] [log] [blame]
/*
* Atari TT/Falcon Am8530 SCC serial ports implementation.
*
* Copyright 2005 Michael Schmitz
*
* Based on:
* drivers/char/vme_scc.c: MVME147, MVME162, BVME6000 SCC serial ports
* implementation.
* Copyright 1999 Richard Hirst <richard@sleepie.demon.co.uk>
*
* which, in turn, was
*
* Based on atari_SCC.c which was
* Copyright 1994-95 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
* Partially based on PC-Linux serial.c by Linus Torvalds and Theodore Ts'o
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*
*/
#include <linux/module.h>
#include <linux/kdev_t.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/tty_flip.h>
#include <linux/mm.h>
#include <linux/serial.h>
#include <linux/fcntl.h>
#include <linux/major.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/miscdevice.h>
#include <linux/console.h>
#include <linux/init.h>
#include <linux/uaccess.h>
#include <linux/generic_serial.h>
#include <asm/setup.h>
#include <asm/bootinfo.h>
#include <asm/atarihw.h>
#include <asm/atariints.h>
#include "scc.h"
#define SUPPORT_TT_SCC 1
#define SUPPORT_FALCON_SCC 1
#define SUPPORT_ST_SCC 1
#define CHANNEL_A 0
#define CHANNEL_B 1
#define SCC_MINOR_BASE 64
/* Shadows for all SCC write registers */
static unsigned char scc_shadow[2][16];
/* Location to access for SCC register access delay */
static volatile unsigned char *scc_del;
/* To keep track of STATUS_REG state for detection of Ext/Status int source */
static unsigned char scc_last_status_reg[2];
/***************************** Prototypes *****************************/
/* Function prototypes */
static void scc_disable_tx_interrupts(void *ptr);
static void scc_enable_tx_interrupts(void *ptr);
static void scc_disable_rx_interrupts(void *ptr);
static void scc_enable_rx_interrupts(void *ptr);
static int scc_carrier_raised(struct tty_port *port);
static void scc_shutdown_port(void *ptr);
static int scc_set_real_termios(void *ptr);
static void scc_hungup(void *ptr);
static void scc_close(void *ptr);
static int scc_chars_in_buffer(void *ptr);
static int scc_open(struct tty_struct *tty, struct file *filp);
static int scc_ioctl(struct tty_struct *tty, struct file *filp,
unsigned int cmd, unsigned long arg);
static void scc_throttle(struct tty_struct *tty);
static void scc_unthrottle(struct tty_struct *tty);
static irqreturn_t scc_tx_int(int irq, void *data);
static irqreturn_t scc_rx_int(int irq, void *data);
static irqreturn_t scc_stat_int(int irq, void *data);
static irqreturn_t scc_spcond_int(int irq, void *data);
static void scc_setsignals(struct scc_port *port, int dtr, int rts);
static int scc_break_ctl(struct tty_struct *tty, int break_state);
static struct tty_driver *scc_driver;
static struct scc_port scc_ports[2];
/*---------------------------------------------------------------------------
* Interface from generic_serial.c back here
*--------------------------------------------------------------------------*/
static struct real_driver scc_real_driver = {
.disable_tx_interrupts = scc_disable_tx_interrupts,
.enable_tx_interrupts = scc_enable_tx_interrupts,
.disable_rx_interrupts = scc_disable_rx_interrupts,
.enable_rx_interrupts = scc_enable_rx_interrupts,
.shutdown_port = scc_shutdown_port,
.set_real_termios = scc_set_real_termios,
.chars_in_buffer = scc_chars_in_buffer,
.close = scc_close,
.hungup = scc_hungup,
};
static struct tty_operations scc_ops = {
.open = scc_open,
.close = gs_close,
.write = gs_write,
.put_char = gs_put_char,
.flush_chars = gs_flush_chars,
.write_room = gs_write_room,
.chars_in_buffer = gs_chars_in_buffer,
.flush_buffer = gs_flush_buffer,
.ioctl = scc_ioctl,
.throttle = scc_throttle,
.unthrottle = scc_unthrottle,
.set_termios = gs_set_termios,
.stop = gs_stop,
.start = gs_start,
.hangup = gs_hangup,
.break_ctl = scc_break_ctl,
};
static const struct tty_port_operations scc_port_ops = {
.carrier_raised = scc_carrier_raised,
};
/* BRG values for the standard speeds and the various clock sources */
struct baud_entry {
unsigned clksrc; /* clock source to use or -1 for not possible */
unsigned div; /* divisor: 1, 2 and 4 correspond to
* direct 1:16, 1:32 and 1:64 modes,
* divisors >= 4 yield a BRG value of
* div/2-2 (in 1:16 mode)
*/
};
/* A pointer for each channel to the current baud table */
static struct baud_entry *scc_baud_table[2];
/* Baud table format:
*
* Each entry consists of the clock source (CLK_RTxC, CLK_TRxC or
* CLK_PCLK) and a divisor. The following rules apply to the divisor:
*
* - CLK_RTxC: 1 or even (1, 2 and 4 are the direct modes, > 4 use
* the BRG)
*
* - CLK_TRxC: 1, 2 or 4 (no BRG, only direct modes possible)
*
* - CLK_PCLK: >= 4 and even (no direct modes, only BRG)
*
*/
/* This table is used if RTxC = 3.672 MHz. This is the case for TT's
* channel A and for both channels on the Mega STE/Falcon. (TRxC is unused)
*/
static struct baud_entry bdtab_norm[20] = {
/* B0 */ { 0, 0 },
/* B50 */ { CLK_RTxC, 4590 },
/* B75 */ { CLK_RTxC, 3060 },
/* B110 */ { CLK_PCLK, 4576 },
/* B134 */ { CLK_PCLK, 3756 },
/* B150 */ { CLK_RTxC, 1530 },
/* B200 */ { CLK_PCLK, 2516 },
/* B300 */ { CLK_PCLK, 1678 },
/* B600 */ { CLK_PCLK, 838 },
/* B1200 */ { CLK_PCLK, 420 },
/* B1800 */ { CLK_PCLK, 280 },
/* B2400 */ { CLK_PCLK, 210 },
/* B4800 */ { CLK_RTxC, 48 },
/* B9600 */ { CLK_RTxC, 24 },
/* B19200 */ { CLK_RTxC, 12 },
/* B38400 */ { CLK_RTxC, 6 }, /* #15 spd_extra */
/* B57600 */ { CLK_RTxC, 4 }, /* #16 spd_hi */
/* B115200 */ { CLK_RTxC, 2 }, /* #17 spd_vhi */
/* B230400 */ { CLK_RTxC, 1 }, /* #18 spd_shi */
/* B460800 */ { 0, 0 } /* #19 spd_warp: Impossible */
};
/* This is a special table for the TT channel B with 307.2 kHz at RTxC
* and 2.4576 MHz at TRxC
*/
static struct baud_entry bdtab_TTChB[20] = {
/* B0 */ { 0, 0 },
/* B50 */ { CLK_RTxC, 384 },
/* B75 */ { CLK_RTxC, 256 },
/* B110 */ { CLK_PCLK, 4576 },
/* B134 */ { CLK_PCLK, 3756 },
/* B150 */ { CLK_RTxC, 128 },
/* B200 */ { CLK_RTxC, 96 },
/* B300 */ { CLK_RTxC, 64 },
/* B600 */ { CLK_RTxC, 32 },
/* B1200 */ { CLK_RTxC, 16 },
/* B1800 */ { CLK_PCLK, 280 },
/* B2400 */ { CLK_RTxC, 8 },
/* B4800 */ { CLK_RTxC, 4 },
/* B9600 */ { CLK_RTxC, 2 },
/* B19200 */ { CLK_RTxC, 1 },
/* B38400 */ { CLK_TRxC, 4 },
/* 57600 is not possible, use 76800 instead */
/* B57600 */ { CLK_TRxC, 2 },
/* 115200 is not possible, use 153600 instead */
/* B115200 */ { CLK_TRxC, 1 },
/* B230400 */ { 0, 0 }, /* #18 spd_shi: Impossible */
/* B460800 */ { 0, 0 } /* #19 spd_warp: Impossible */
};
/*----------------------------------------------------------------------------
* atari_scc_init() and support functions
*---------------------------------------------------------------------------*/
static int scc_init_drivers(void)
{
int error;
scc_driver = alloc_tty_driver(2);
if (!scc_driver)
return -ENOMEM;
scc_driver->owner = THIS_MODULE;
scc_driver->driver_name = "scc";
scc_driver->name = "ttyS";
scc_driver->major = TTY_MAJOR;
scc_driver->minor_start = SCC_MINOR_BASE;
scc_driver->type = TTY_DRIVER_TYPE_SERIAL;
scc_driver->subtype = SERIAL_TYPE_NORMAL;
scc_driver->init_termios = tty_std_termios;
scc_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
scc_driver->flags = TTY_DRIVER_REAL_RAW;
tty_set_operations(scc_driver, &scc_ops);
error = tty_register_driver(scc_driver);
if (error) {
pr_err("scc: Couldn't register scc driver, error = %d\n",
error);
put_tty_driver(scc_driver);
return 1;
}
return 0;
}
/* ports[] array is indexed by line no (i.e. [0] for ttyS0, [1] for ttyS1).
*/
static void scc_init_portstructs(void)
{
struct scc_port *port;
int i;
for (i = 0; i < 2; i++) {
port = scc_ports + i;
tty_port_init(&port->gs.port);
port->gs.port.ops = &scc_port_ops;
port->gs.magic = SCC_MAGIC;
port->gs.close_delay = HZ/2;
port->gs.closing_wait = 30 * HZ;
port->gs.rd = &scc_real_driver;
#ifdef NEW_WRITE_LOCKING
port->gs.port_write_sem = MUTEX;
#endif
init_waitqueue_head(&port->gs.port.open_wait);
init_waitqueue_head(&port->gs.port.close_wait);
}
}
static int atari_scc_a_request_irqs(struct scc_port *port)
{
int error;
error = request_irq(IRQ_SCCA_TX, scc_tx_int, IRQ_TYPE_PRIO, "SCC-A TX",
port);
if (error)
goto fail;
error = request_irq(IRQ_SCCA_STAT, scc_stat_int, IRQ_TYPE_PRIO,
"SCC-A status", port);
if (error)
goto fail_free_a_tx;
error = request_irq(IRQ_SCCA_RX, scc_rx_int, IRQ_TYPE_PRIO, "SCC-A RX",
port);
if (error)
goto fail_free_a_stat;
error = request_irq(IRQ_SCCA_SPCOND, scc_spcond_int, IRQ_TYPE_PRIO,
"SCC-A special cond", port);
if (error)
goto fail_free_a_rx;
return 0;
fail_free_a_rx:
free_irq(IRQ_SCCA_RX, port);
fail_free_a_stat:
free_irq(IRQ_SCCA_STAT, port);
fail_free_a_tx:
free_irq(IRQ_SCCA_TX, port);
fail:
return error;
}
static void atari_scc_a_free_irqs(struct scc_port *port)
{
free_irq(IRQ_SCCA_TX, port);
free_irq(IRQ_SCCA_STAT, port);
free_irq(IRQ_SCCA_RX, port);
free_irq(IRQ_SCCA_SPCOND, port);
}
static int atari_scc_b_request_irqs(struct scc_port *port)
{
int error;
error = request_irq(IRQ_SCCB_TX, scc_tx_int, IRQ_TYPE_PRIO, "SCC-B TX",
port);
if (error)
goto fail;
error = request_irq(IRQ_SCCB_STAT, scc_stat_int, IRQ_TYPE_PRIO,
"SCC-B status", port);
if (error)
goto fail_free_b_tx;
error = request_irq(IRQ_SCCB_RX, scc_rx_int, IRQ_TYPE_PRIO, "SCC-B RX",
port);
if (error)
goto fail_free_b_stat;
error = request_irq(IRQ_SCCB_SPCOND, scc_spcond_int, IRQ_TYPE_PRIO,
"SCC-B special cond", port);
if (error)
goto fail_free_b_rx;
return 0;
fail_free_b_rx:
free_irq(IRQ_SCCB_RX, port);
fail_free_b_stat:
free_irq(IRQ_SCCB_STAT, port);
fail_free_b_tx:
free_irq(IRQ_SCCB_TX, port);
fail:
return error;
}
static void atari_scc_b_free_irqs(struct scc_port *port)
{
free_irq(IRQ_SCCB_TX, port);
free_irq(IRQ_SCCB_STAT, port);
free_irq(IRQ_SCCB_RX, port);
free_irq(IRQ_SCCB_SPCOND, port);
}
#ifdef SUPPORT_TT_SCC
static int atari_tt_scc_init(void)
{
struct scc_port *port;
int error;
pr_info("SCC: Atari TT Serial Driver\n");
/* FIXME channel A may be switchable between modem and LAN port */
/* Init channel A */
if (atari_SCC_init_done)
pr_warning("SCC: already initialized, expect trouble!\n");
pr_debug("SCC: init channel A\n");
port = &scc_ports[0];
port->channel = CHANNEL_A;
port->ctrlp = (volatile unsigned char *)&scc.cha_a_ctrl;
port->datap = port->ctrlp + 1;
port->port_a = &scc_ports[0];
port->port_b = &scc_ports[1];
pr_debug("SCC: request channel A irqs, port = %p\n", port);
error = atari_scc_a_request_irqs(port);
if (error)
return error;
{
SCC_ACCESS_INIT(port);
pr_debug("SCC: read SCC status\n");
/*
* on the first access, read status register to reset internal
* pointers
*/
SCCread(STATUS_REG);
pr_debug("SCC: reset SCC\n");
/* FIXME: master reset, once only */
SCCwrite(MASTER_INT_CTRL, MIC_HARD_RESET);
udelay(40);
/* disable interrupts for this channel */
SCCwrite(INT_AND_DMA_REG, 0);
/* Set the interrupt vector ; 0x60 for all Atari models */
SCCwrite(INT_VECTOR_REG, 0x60);
/* Interrupt parameters: vector includes status, status low */
SCCwrite(MASTER_INT_CTRL, MIC_VEC_INCL_STAT);
SCCmod(MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB);
/* disable interrupts for this channel */
SCCwrite(INT_AND_DMA_REG, 0);
}
if (!atari_SCC_init_done) {
/* Init channel B */
pr_debug("SCC: init channel B\n");
port = &scc_ports[1];
port->channel = CHANNEL_B;
port->ctrlp = (volatile unsigned char *)&scc.cha_b_ctrl;
port->datap = port->ctrlp + 1;
port->port_a = &scc_ports[0];
port->port_b = &scc_ports[1];
pr_debug("SCC: request channel B irqs, port = %p\n", port);
error = atari_scc_b_request_irqs(port);
if (error) {
atari_scc_a_free_irqs(port);
return error;
}
{
SCC_ACCESS_INIT(port);
/* disable interrupts for this channel */
SCCwrite(INT_AND_DMA_REG, 0);
}
/* not implemented yet */
#if 0
error = request_irq(IRQ_TT_MFP_RI, scc_ri_int, IRQ_TYPE_SLOW,
"TT-MFP ring indicator (modem 2)", port);
if (error) {
atari_scc_b_free_irqs(port);
atari_scc_a_free_irqs(port);
return error;
}
#endif
}
/* once only: initalize MFP timer C for RTxC */
tt_mfp.tim_ct_cd = (tt_mfp.tim_ct_cd & ~0x70) | 0x10;
tt_mfp.tim_dt_c = 1;
atari_turnoff_irq(IRQ_TT_MFP_TIMC);
/* set baud tables */
scc_baud_table[CHANNEL_A] = bdtab_norm;
scc_baud_table[CHANNEL_B] = bdtab_TTChB;
/* Initialise the tty driver structures and register */
pr_debug("SCC: scc_init_portstructs()\n");
scc_init_portstructs();
pr_debug("SCC: scc_init_drivers()\n");
scc_init_drivers();
return 0;
}
#else /* !SUPPORT_TT_SCC */
static inline int atari_tt_scc_init(void)
{
return -ENODEV;
}
#endif /* !SUPPORT_TT_SCC */
#ifdef SUPPORT_FALCON_SCC
static int atari_falcon_scc_init(void)
{
struct scc_port *port;
int error;
pr_info("SCC: Atari Falcon Serial Driver\n");
if (atari_SCC_init_done)
pr_warning("SCC: already initialized, expect trouble!\n");
/* Init channel A */
port = &scc_ports[0];
port->channel = CHANNEL_A;
port->ctrlp = (volatile unsigned char *)&scc.cha_a_ctrl;
port->datap = port->ctrlp + 2;
port->port_a = &scc_ports[0];
port->port_b = &scc_ports[1];
error = atari_scc_a_request_irqs(port);
if (error)
return error;
{
SCC_ACCESS_INIT(port);
/*
* on the first access, read status register to reset internal
* pointers
*/
SCCread(STATUS_REG);
/* FIXME: master reset, once only */
SCCwrite(MASTER_INT_CTRL, MIC_HARD_RESET);
udelay(40);
/* disable interrupts for this channel */
SCCwrite(INT_AND_DMA_REG, 0);
/* Set the interrupt vector */
SCCwrite(INT_VECTOR_REG, 0x60);
/* Interrupt parameters: vector includes status, status low */
SCCwrite(MASTER_INT_CTRL, MIC_VEC_INCL_STAT);
SCCmod(MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB);
}
/* conditionalize if port in use by console ?? */
/* Init channel B */
port = &scc_ports[1];
port->channel = CHANNEL_B;
port->ctrlp = (volatile unsigned char *)&scc.cha_b_ctrl;
port->datap = port->ctrlp + 2;
port->port_a = &scc_ports[0];
port->port_b = &scc_ports[1];
error = atari_scc_b_request_irqs(port);
if (error) {
atari_scc_a_free_irqs(port);
return error;
}
{
SCC_ACCESS_INIT(port); /* Either channel will do */
/* disable interrupts for this channel */
SCCwrite(INT_AND_DMA_REG, 0);
}
/* set baud tables */
scc_baud_table[CHANNEL_A] = bdtab_norm;
scc_baud_table[CHANNEL_B] = bdtab_norm;
/* Initialise the tty driver structures and register */
scc_init_portstructs();
scc_init_drivers();
return 0;
}
#else /* !SUPPORT_FALCON_SCC */
static inline int atari_falcon_scc_init(void)
{
return -ENODEV;
}
#endif /* !SUPPORT_FALCON_SCC */
#ifdef SUPPORT_ST_SCC
static int atari_st_scc_init(void)
{
struct scc_port *port;
int escc = ATARIHW_PRESENT(ST_ESCC);
int error;
pr_info("SCC: Atari MegaST/E Serial Driver\n");
/* FIXME: ports reversed logic */
/* Init channel A */
port = &scc_ports[1];
port->channel = CHANNEL_A;
port->ctrlp = (volatile unsigned char *)(escc ? &st_escc.cha_a_ctrl
: &scc.cha_a_ctrl);
port->datap = port->ctrlp + 4;
port->port_a = &scc_ports[1];
port->port_b = &scc_ports[0];
error = atari_scc_a_request_irqs(port);
if (error)
return error;
{
SCC_ACCESS_INIT(port);
/*
* on the first access, read status register to reset internal
* pointers
*/
SCCread(STATUS_REG);
/* FIXME: master reset, once only */
SCCwrite(MASTER_INT_CTRL, MIC_HARD_RESET);
udelay(40);
/* disable interrupts for this channel */
SCCwrite(INT_AND_DMA_REG, 0);
/* Set the interrupt vector */
SCCwrite(INT_VECTOR_REG, 0x60);
/* Interrupt parameters: vector includes status, status low */
SCCwrite(MASTER_INT_CTRL, MIC_VEC_INCL_STAT);
SCCmod(MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB);
}
/* Init channel B */
port = &scc_ports[0];
port->channel = CHANNEL_B;
port->ctrlp = (volatile unsigned char *)(escc ? &st_escc.cha_b_ctrl
: &scc.cha_b_ctrl);
port->datap = port->ctrlp + 4;
port->port_a = &scc_ports[0];
port->port_b = &scc_ports[1];
error = atari_scc_b_request_irqs(port);
if (error) {
atari_scc_a_free_irqs(port);
return error;
}
{
SCC_ACCESS_INIT(port); /* Either channel will do */
/* disable interrupts for this channel */
SCCwrite(INT_AND_DMA_REG, 0);
}
/* set baud tables */
scc_baud_table[CHANNEL_A] = bdtab_norm;
scc_baud_table[CHANNEL_B] = bdtab_norm;
/* Initialise the tty driver structures and register */
scc_init_portstructs();
scc_init_drivers();
return 0;
}
#else /* !SUPPORT_ST_SCC */
static inline int atari_st_scc_init(void)
{
return -ENODEV;
}
#endif /* !SUPPORT_ST_SCC */
int atari_scc_init(void)
{
int res = -ENODEV;
static int called;
if (called)
return res;
called = 1;
if (!(ATARIHW_PRESENT(SCC) || ATARIHW_PRESENT(ST_ESCC)))
return -ENODEV;
scc_del = &st_mfp.par_dt_reg;
if (MACH_IS_TT)
res = atari_tt_scc_init();
if (MACH_IS_FALCON)
res = atari_falcon_scc_init();
if (MACH_IS_ST)
res = atari_st_scc_init();
return res;
}
void atari_scc_cleanup(void)
{
struct scc_port *port;
tty_unregister_driver(scc_driver);
port = &scc_ports[0];
pr_debug("SCC: free channel A irqs, port = %p\n", port);
atari_scc_a_free_irqs(port);
port = &scc_ports[1];
pr_debug("SCC: free channel A irqs, port = %p\n", port);
atari_scc_b_free_irqs(port);
}
module_init(atari_scc_init);
module_exit(atari_scc_cleanup);
/*---------------------------------------------------------------------------
* Interrupt handlers
*--------------------------------------------------------------------------*/
static irqreturn_t scc_rx_int(int irq, void *data)
{
unsigned char ch;
struct scc_port *port = data;
struct tty_struct *tty = port->gs.port.tty;
SCC_ACCESS_INIT(port);
pr_debug("SCC: rx_int ...\n");
ch = SCCread_NB(RX_DATA_REG);
if (!tty) {
pr_warning("scc_rx_int with NULL tty!\n");
SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET);
return IRQ_HANDLED;
}
tty_insert_flip_char(tty, ch, 0);
#if 0
if (tty->flip.count < TTY_FLIPBUF_SIZE) {
*tty->flip.char_buf_ptr = ch;
*tty->flip.flag_buf_ptr = 0;
tty->flip.flag_buf_ptr++;
tty->flip.char_buf_ptr++;
tty->flip.count++;
}
#endif
/*
* Check if another character is already ready; in that case, the
* spcond_int() function must be used, because this character may have
* an * error condition that isn't signalled by the interrupt vector
* used!
*/
if (SCCread(INT_PENDING_REG) &
(port->channel == CHANNEL_A ? IPR_A_RX : IPR_B_RX)) {
scc_spcond_int(irq, data);
return IRQ_HANDLED;
}
SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET);
tty_flip_buffer_push(tty);
pr_debug("SCC: rx_int done\n");
return IRQ_HANDLED;
}
static irqreturn_t scc_spcond_int(int irq, void *data)
{
struct scc_port *port = data;
struct tty_struct *tty = port->gs.port.tty;
unsigned char stat, ch, err;
int int_pending_mask = port->channel == CHANNEL_A ? IPR_A_RX : IPR_B_RX;
SCC_ACCESS_INIT(port);
pr_debug("SCC: spcond_int ...\n");
if (!tty) {
pr_warning("scc_spcond_int with NULL tty!\n");
SCCwrite(COMMAND_REG, CR_ERROR_RESET);
SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET);
return IRQ_HANDLED;
}
do {
stat = SCCread(SPCOND_STATUS_REG);
ch = SCCread_NB(RX_DATA_REG);
if (stat & SCSR_RX_OVERRUN)
err = TTY_OVERRUN;
else if (stat & SCSR_PARITY_ERR)
err = TTY_PARITY;
else if (stat & SCSR_CRC_FRAME_ERR)
err = TTY_FRAME;
else
err = 0;
tty_insert_flip_char(tty, ch, err);
#if 0
if (tty->flip.count < TTY_FLIPBUF_SIZE) {
*tty->flip.char_buf_ptr = ch;
*tty->flip.flag_buf_ptr = err;
tty->flip.flag_buf_ptr++;
tty->flip.char_buf_ptr++;
tty->flip.count++;
}
#endif
/* ++TeSche: *All* errors have to be cleared manually,
* else the condition persists for the next chars
*/
if (err)
SCCwrite(COMMAND_REG, CR_ERROR_RESET);
} while (SCCread(INT_PENDING_REG) & int_pending_mask);
SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET);
tty_flip_buffer_push(tty);
pr_debug("SCC: spcond_int done\n");
return IRQ_HANDLED;
}
/* not implemented yet */
#if 0
static void scc_ri_int(int irq, void *data)
{
struct scc_port *port = data;
/* update input line counter */
port->icount.rng++;
wake_up_interruptible(&port->delta_msr_wait);
}
#endif
static irqreturn_t scc_tx_int(int irq, void *data)
{
struct scc_port *port = data;
SCC_ACCESS_INIT(port);
pr_debug("SCC: tx_int irq %d port %p ...\n", irq, data);
if (!port->gs.port.tty) {
pr_warning("scc_tx_int with NULL tty!\n");
SCCmod(INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0);
SCCwrite(COMMAND_REG, CR_TX_PENDING_RESET);
SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET);
return IRQ_HANDLED;
}
while ((SCCread_NB(STATUS_REG) & SR_TX_BUF_EMPTY)) {
if (port->x_char) {
pr_debug("SCC: tx_int writing char %c\n", port->x_char);
SCCwrite(TX_DATA_REG, port->x_char);
port->x_char = 0;
} else if ((port->gs.xmit_cnt <= 0) ||
port->gs.port.tty->stopped ||
port->gs.port.tty->hw_stopped) {
pr_debug("SCC: nothing to do!\n");
break;
} else {
pr_debug("SCC: tx_int writing buf %c\n",
port->gs.xmit_buf[port->gs.xmit_tail]);
SCCwrite(TX_DATA_REG,
port->gs.xmit_buf[port->gs.xmit_tail++]);
port->gs.xmit_tail = port->gs.xmit_tail &
(SERIAL_XMIT_SIZE-1);
if (--port->gs.xmit_cnt <= 0)
break;
}
}
if ((port->gs.xmit_cnt <= 0) || port->gs.port.tty->stopped ||
port->gs.port.tty->hw_stopped) {
pr_debug("SCC: nothing to do, disabling int\n");
/* disable tx interrupts */
SCCmod(INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0);
/* disable tx_int on next tx underrun? */
SCCwrite(COMMAND_REG, CR_TX_PENDING_RESET);
port->gs.port.flags &= ~GS_TX_INTEN;
}
if (port->gs.port.tty && port->gs.xmit_cnt <= port->gs.wakeup_chars) {
pr_debug("SCC: waking up tty!\n");
tty_wakeup(port->gs.port.tty);
}
SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET);
pr_debug("SCC: tx_int done\n");
return IRQ_HANDLED;
}
static irqreturn_t scc_stat_int(int irq, void *data)
{
struct scc_port *port = data;
unsigned channel = port->channel;
unsigned char last_sr, sr, changed;
SCC_ACCESS_INIT(port);
pr_debug("SCC: stat_int ...\n");
last_sr = scc_last_status_reg[channel];
sr = scc_last_status_reg[channel] = SCCread_NB(STATUS_REG);
changed = last_sr ^ sr;
if (changed & SR_DCD) {
port->c_dcd = !!(sr & SR_DCD);
if (!(port->gs.port.flags & ASYNC_CHECK_CD))
; /* Don't report DCD changes */
else if (port->c_dcd) {
/* Are we blocking in open? */
wake_up_interruptible(&port->gs.port.open_wait);
} else {
if (port->gs.port.tty)
tty_hangup(port->gs.port.tty);
}
}
/* FIXME: CTS and DSR status changes? */
SCCwrite(COMMAND_REG, CR_EXTSTAT_RESET);
SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET);
pr_debug("SCC: stat_int done\n");
return IRQ_HANDLED;
}
/*---------------------------------------------------------------------------
* generic_serial.c callback funtions
*--------------------------------------------------------------------------*/
static void scc_disable_tx_interrupts(void *ptr)
{
struct scc_port *port = ptr;
unsigned long flags;
SCC_ACCESS_INIT(port);
pr_debug("SCC: disable_tx_int ...\n");
local_irq_save(flags);
SCCmod(INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0);
port->gs.port.flags &= ~GS_TX_INTEN;
local_irq_restore(flags);
pr_debug("SCC: disable_tx_int done!\n");
}
static void scc_enable_tx_interrupts(void *ptr)
{
struct scc_port *port = ptr;
unsigned long flags;
SCC_ACCESS_INIT(port);
pr_debug("SCC: enable_tx_int ...\n");
local_irq_save(flags);
SCCmod(INT_AND_DMA_REG, 0xff, IDR_TX_INT_ENAB);
/* restart the transmitter */
scc_tx_int(0, port);
local_irq_restore(flags);
pr_debug("SCC: enable_tx_int done!\n");
}
static void scc_disable_rx_interrupts(void *ptr)
{
struct scc_port *port = ptr;
unsigned long flags;
SCC_ACCESS_INIT(port);
pr_debug("SCC: disable_rx_int ...\n");
local_irq_save(flags);
SCCmod(INT_AND_DMA_REG,
~(IDR_RX_INT_MASK|IDR_PARERR_AS_SPCOND|IDR_EXTSTAT_INT_ENAB), 0);
local_irq_restore(flags);
pr_debug("SCC: disable_rx_int done!\n");
}
static void scc_enable_rx_interrupts(void *ptr)
{
struct scc_port *port = ptr;
unsigned long flags;
SCC_ACCESS_INIT(port);
pr_debug("SCC: enable_rx_int ...\n");
local_irq_save(flags);
SCCmod(INT_AND_DMA_REG, 0xff,
IDR_EXTSTAT_INT_ENAB|IDR_PARERR_AS_SPCOND|IDR_RX_INT_ALL);
local_irq_restore(flags);
pr_debug("SCC: enable_rx_int done!\n");
}
static int scc_carrier_raised(struct tty_port *port)
{
struct scc_port *sc = container_of(port, struct scc_port, gs.port);
unsigned channel = sc->channel;
pr_debug("SCC: get_CD!\n");
return !!(scc_last_status_reg[channel] & SR_DCD);
}
static void scc_shutdown_port(void *ptr)
{
struct scc_port *port = ptr;
pr_debug("SCC: shutdown_port ...\n");
port->gs.port.flags &= ~GS_ACTIVE;
if (port->gs.port.tty && port->gs.port.tty->termios->c_cflag & HUPCL)
scc_setsignals(port, 0, 0);
pr_debug("SCC: shutdown_port done!\n");
}
static int scc_set_real_termios(void *ptr)
{
/* the SCC has char sizes 5,7,6,8 in that order! */
static int chsize_map[4] = { 0, 2, 1, 3 };
unsigned int cflag, baud, baudbits, baudidx, brgmode;
unsigned int clkmode, clksrc, div, chsize, channel, brgval = 0;
unsigned long flags;
struct scc_port *port = ptr;
SCC_ACCESS_INIT(port);
if (!port->gs.port.tty || !port->gs.port.tty->termios)
return 0;
channel = port->channel;
pr_debug("SCC: termios for channel %u\n", channel);
cflag = port->gs.port.tty->termios->c_cflag;
baud = port->gs.baud;
baudbits = cflag & CBAUD;
chsize = (cflag & CSIZE) >> 4;
if (baud == 0) {
/* speed == 0 -> drop DTR */
local_irq_save(flags);
SCCmod(TX_CTRL_REG, ~TCR_DTR, 0);
local_irq_restore(flags);
return 0;
} else if ((MACH_IS_TT && (baud < 50 || baud > 115200)) ||
(MACH_IS_FALCON && (baud < 50 || baud > 230400))) {
pr_debug("SCC: Bad speed requested, %d\n", baud);
return 0;
}
if (cflag & CLOCAL)
port->gs.port.flags &= ~ASYNC_CHECK_CD;
else
port->gs.port.flags |= ASYNC_CHECK_CD;
/* calculate brgval for Atari; enable direct modes! */
/*
* convert baud rate from gs.baud to table index, set custom divisor
* eventually
*/
div = 0;
clksrc = 0;
baudidx = 0;
switch (baud) {
case 50:
baudidx = 1;
break;
case 75:
baudidx = 2;
break;
case 110:
baudidx = 3;
break;
case 134:
baudidx = 4;
break;
case 150:
baudidx = 5;
break;
case 200:
baudidx = 6;
break;
case 300:
baudidx = 7;
break;
case 600:
baudidx = 8;
break;
case 1200:
baudidx = 9;
break;
case 1800:
baudidx = 10;
break;
case 2400:
baudidx = 11;
break;
case 4800:
baudidx = 12;
break;
case 9600:
baudidx = 13;
break;
case 19200:
baudidx = 14;
break;
case 38400:
baudidx = 15;
break;
case 57600:
baudidx = 16;
break;
case 115200:
baudidx = 17;
break;
case 230400:
baudidx = 18;
break;
default:
baudidx = 15;
break;
}
/* do we have a custom divisor ?? */
if (!div) {
if (baudidx > 19)
baudidx = 19;
clksrc = scc_baud_table[channel][baudidx].clksrc;
div = scc_baud_table[channel][baudidx].div;
if (!div) {
pr_debug("SCC_change_speed: divisor = 0 !!!\n");
return 0;
}
}
pr_debug("SCC: termios baud %d baudbits %d baudidx %d \n clksrc %d "
"div %d\n", baud, baudbits, baudidx, clksrc, div);
/* compute the SCC's clock source, clock mode, BRG mode and BRG
* value from clksrc and div
*/
if (div <= 4) {
clkmode = (div == 1 ? A1CR_CLKMODE_x16 :
div == 2 ? A1CR_CLKMODE_x32 :
A1CR_CLKMODE_x64);
clksrc = (clksrc == CLK_RTxC
? CCR_TXCLK_RTxC | CCR_RXCLK_RTxC
: CCR_TXCLK_TRxC | CCR_RXCLK_TRxC);
brgmode = 0; /* off */
brgval = 0;
} else {
brgval = div/2 - 2;
brgmode = (DCR_BRG_ENAB |
(clksrc == CLK_PCLK ? DCR_BRG_USE_PCLK : 0));
clkmode = A1CR_CLKMODE_x16;
clksrc = CCR_TXCLK_BRG | CCR_RXCLK_BRG;
}
/*
* pr_info("SCC: termios baud %d baudbits %d baudidx %d \n clksrc %d "
* "clkmode %d div %d brgval %d brgmode %d\n", baud, baudbits,
* baudidx, clksrc, clkmode, div, brgval, brgmode);
*/
/* Now we have all parameters and can go to set them: */
local_irq_save(flags);
pr_debug(" brgval=%d brgmode=%02x clkmode=%02x clksrc=%02x\n", brgval,
brgmode, clkmode, clksrc);
/* receiver's character size and auto-enables */
#if 0 /* auto-enable considered harmful ... */
SCCmod(RX_CTRL_REG, ~(RCR_CHSIZE_MASK|RCR_AUTO_ENAB_MODE),
(chsize_map[chsize] << 6) |
((cflag & CRTSCTS) ? RCR_AUTO_ENAB_MODE : 0));
#else
/* receiver's character size */
SCCmod(RX_CTRL_REG, ~RCR_CHSIZE_MASK, chsize_map[chsize] << 6);
#endif
pr_debug(" RX_CTRL_REG <- %02x\n", SCCread(RX_CTRL_REG));
/* clock mode changes depending on baud rate */
/* parity and stop bits (both, Tx and Rx) and clock mode */
SCCmod(AUX1_CTRL_REG,
~(A1CR_PARITY_MASK | A1CR_MODE_MASK | A1CR_CLKMODE_MASK),
((cflag & PARENB
? (cflag & PARODD ? A1CR_PARITY_ODD : A1CR_PARITY_EVEN)
: A1CR_PARITY_NONE)
| (cflag & CSTOPB ? A1CR_MODE_ASYNC_2 : A1CR_MODE_ASYNC_1)
| clkmode));
pr_debug(" AUX1_CTRL_REG <- %02x\n", SCCread(AUX1_CTRL_REG));
/* sender's character size, set DTR for valid baud rate */
SCCmod(TX_CTRL_REG, ~TCR_CHSIZE_MASK,
chsize_map[chsize] << 5 | TCR_DTR);
pr_debug(" TX_CTRL_REG <- %02x\n", SCCread(TX_CTRL_REG));
/* clock sources change for TT !! */
/* clock sources never change */
/* clock sources */
SCCmod(CLK_CTRL_REG, ~(CCR_TXCLK_MASK | CCR_RXCLK_MASK), clksrc);
pr_debug(" CLK_CTRL_REG <- %02x\n", SCCread(CLK_CTRL_REG));
/* disable BRG before changing the value */
SCCmod(DPLL_CTRL_REG, ~DCR_BRG_ENAB, 0);
/* BRG value */
SCCwrite(TIMER_LOW_REG, brgval & 0xff);
SCCwrite(TIMER_HIGH_REG, (brgval >> 8) & 0xff);
/* BRG enable, and clock source never changes */
/* SCCmod(DPLL_CTRL_REG, 0xff, DCR_BRG_ENAB); */
SCCmod(DPLL_CTRL_REG, ~(DCR_BRG_ENAB | DCR_BRG_USE_PCLK), brgmode);
pr_debug(" TIMER_LOW_REG <- %02x\n", SCCread(TIMER_LOW_REG));
pr_debug(" TIMER_HIGH_REG <- %02x\n", SCCread(TIMER_HIGH_REG));
pr_debug(" DPLL_CTRL_REG <- %02x\n", SCCread(DPLL_CTRL_REG));
local_irq_restore(flags);
pr_debug("SCC: done termios for channel %d\n", channel);
return 0;
}
static int scc_chars_in_buffer(void *ptr)
{
struct scc_port *port = ptr;
#ifdef DEBUG
int rv;
#endif
SCC_ACCESS_INIT(port);
#ifdef DEBUG
rv = (SCCread(SPCOND_STATUS_REG) & SCSR_ALL_SENT) ? 0 : 1;
pr_debug("SCC: chars_in_buffer: %d\n", rv);
return rv;
#else
return (SCCread(SPCOND_STATUS_REG) & SCSR_ALL_SENT) ? 0 : 1;
#endif
}
/* Comment taken from sx.c (2.4.0):
I haven't the foggiest why the decrement use count has to happen
here. The whole linux serial drivers stuff needs to be redesigned.
My guess is that this is a hack to minimize the impact of a bug
elsewhere. Thinking about it some more. (try it sometime) Try
running minicom on a serial port that is driven by a modularized
driver. Have the modem hangup. Then remove the driver module. Then
exit minicom. I expect an "oops". -- REW */
static void scc_hungup(void *ptr)
{
pr_debug("SCC: hungup ...\n");
scc_disable_tx_interrupts(ptr);
scc_disable_rx_interrupts(ptr);
pr_debug("SCC: hungup done\n");
}
static void scc_close(void *ptr)
{
pr_debug("SCC: close ...\n");
scc_disable_tx_interrupts(ptr);
scc_disable_rx_interrupts(ptr);
pr_debug("SCC: close done\n");
}
/*---------------------------------------------------------------------------
* Internal support functions
*--------------------------------------------------------------------------*/
static void scc_setsignals(struct scc_port *port, int dtr, int rts)
{
unsigned long flags;
unsigned char t;
SCC_ACCESS_INIT(port);
pr_debug("SCC: setsignals dtr %d rts %d...\n", dtr, rts);
local_irq_save(flags);
t = SCCread(TX_CTRL_REG);
if (dtr >= 0)
t = dtr? (t | TCR_DTR): (t & ~TCR_DTR);
if (rts >= 0)
t = rts? (t | TCR_RTS): (t & ~TCR_RTS);
SCCwrite(TX_CTRL_REG, t);
local_irq_restore(flags);
pr_debug("SCC: setsignals done\n");
}
static void scc_send_xchar(struct tty_struct *tty, char ch)
{
struct scc_port *port = tty->driver_data;
pr_debug("SCC: send_xchar ...\n");
port->x_char = ch;
if (ch)
scc_enable_tx_interrupts(port);
pr_debug("SCC: send_xchar done\n");
}
/*---------------------------------------------------------------------------
* Driver entrypoints referenced from above
*--------------------------------------------------------------------------*/
static int scc_open(struct tty_struct *tty, struct file *filp)
{
int line = tty->index;
int retval;
struct scc_port *port = &scc_ports[line];
int i, channel = port->channel;
unsigned long flags;
SCC_ACCESS_INIT(port);
static const struct {
unsigned reg, val;
} scc_init_tab[] = {
/* no parity, 1 stop bit, async, 1:16 */
{ AUX1_CTRL_REG,
A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x64 },
/* parity error is special cond, ints disabled, no DMA */
{ INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB },
/* Rx 8 bits/char, no auto enable, Rx off */
{ RX_CTRL_REG, RCR_CHSIZE_8 },
/* DTR off, Tx 8 bits/char, RTS off, Tx off */
{ TX_CTRL_REG, TCR_CHSIZE_8 },
/* special features off */
{ AUX2_CTRL_REG, 0 },
/* RTxC is XTAL, TRxC is input, both clocks = RTxC */
{ CLK_CTRL_REG,
CCR_TRxCOUT_XTAL | CCR_TXCLK_RTxC | CCR_RXCLK_RTxC },
{ DPLL_CTRL_REG, 0 },
/* Start Rx */
{ RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 },
/* Start Tx */
{ TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 },
/* Ext/Stat ints: CTS, DCD, SYNC (DSR) */
{ INT_CTRL_REG,
ICR_ENAB_DCD_INT | ICR_ENAB_CTS_INT | ICR_ENAB_SYNC_INT },
/* Reset Ext/Stat ints */
{ COMMAND_REG, CR_EXTSTAT_RESET },
/* ...again */
{ COMMAND_REG, CR_EXTSTAT_RESET },
/* Rx int always, TX int off, Ext/Stat int on */
{ INT_AND_DMA_REG, IDR_EXTSTAT_INT_ENAB |
IDR_PARERR_AS_SPCOND | IDR_RX_INT_ALL }
};
if (atari_SCC_init_done && line == 1)
return -ENODEV;
pr_debug("SCC: open port ...\n");
if (!(port->gs.port.flags & ASYNC_INITIALIZED)) {
pr_debug("SCC: init port ...\n");
local_irq_save(flags);
SCCmod(MASTER_INT_CTRL, 0x3f,
channel == 0 ? MIC_CH_A_RESET : MIC_CH_B_RESET);
udelay(40); /* extra delay after a reset */
for (i = 0; i < ARRAY_SIZE(scc_init_tab); ++i)
SCCwrite(scc_init_tab[i].reg, scc_init_tab[i].val);
/*
* remember status register for detection of DCD and CTS
* changes
*/
scc_last_status_reg[channel] = SCCread(STATUS_REG);
port->c_dcd = 0; /* Prevent initial 1->0 interrupt */
scc_setsignals(port, 1, 1);
local_irq_restore(flags);
pr_debug("SCC: init port done!\n");
}
tty->driver_data = port;
port->gs.port.tty = tty;
port->gs.port.count++;
pr_debug(KERN_WARNING "SCC: gs init port ...\n");
retval = gs_init_port(&port->gs);
if (retval) {
port->gs.port.count--;
return retval;
}
pr_debug(KERN_WARNING "SCC: gs init port done!\n");
port->gs.port.flags |= GS_ACTIVE;
pr_debug(KERN_WARNING "SCC: gs wait ready ...\n");
retval = gs_block_til_ready(port, filp);
pr_debug(KERN_WARNING "SCC: gs wait ready done!\n");
if (retval) {
port->gs.port.count--;
return retval;
}
port->c_dcd = tty_port_carrier_raised(&port->gs.port);
pr_debug(KERN_WARNING "SCC: enable rx ints ...\n");
scc_enable_rx_interrupts(port);
pr_debug(KERN_WARNING "SCC: enable rx ints done!\n");
pr_info("SCC: open port done!\n");
return 0;
}
static void scc_throttle(struct tty_struct *tty)
{
struct scc_port *port = tty->driver_data;
unsigned long flags;
SCC_ACCESS_INIT(port);
pr_debug("SCC: throttle ...\n");
if (tty->termios->c_cflag & CRTSCTS) {
local_irq_save(flags);
SCCmod(TX_CTRL_REG, ~TCR_RTS, 0);
local_irq_restore(flags);
}
if (I_IXOFF(tty))
scc_send_xchar(tty, STOP_CHAR(tty));
pr_debug("SCC: throttle done!\n");
}
static void scc_unthrottle(struct tty_struct *tty)
{
struct scc_port *port = tty->driver_data;
unsigned long flags;
SCC_ACCESS_INIT(port);
pr_debug("SCC: unthrottle ...\n");
if (tty->termios->c_cflag & CRTSCTS) {
local_irq_save(flags);
SCCmod(TX_CTRL_REG, 0xff, TCR_RTS);
local_irq_restore(flags);
}
if (I_IXOFF(tty))
scc_send_xchar(tty, START_CHAR(tty));
pr_debug("SCC: unthrottle done!\n");
}
static int scc_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct scc_port *port = tty->driver_data;
int retval;
pr_debug("SCC: ioctl! cmd %d, arg %lu\n", cmd, arg);
/* if (serial_paranoia_check(info, tty->device, "zs_ioctl")) */
/* return -ENODEV; */
if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
(cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) &&
(cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) {
if (tty->flags & (1 << TTY_IO_ERROR))
return -EIO;
}
switch (cmd) {
case TCSBRK: /* SVID version: non-zero arg --> no break */
pr_debug("SCC: ioctl TCSBRK\n");
retval = tty_check_change(tty);
if (retval)
return retval;
tty_wait_until_sent(tty, 0);
/* if (!arg) */
/* send_break(info, HZ/4); */
return 0;
case TCSBRKP: /* support for POSIX tcsendbreak() */
pr_debug("SCC: ioctl TCSBRKP\n");
retval = tty_check_change(tty);
if (retval)
return retval;
tty_wait_until_sent(tty, 0);
/* send_break(info, arg ? arg*(HZ/10) : HZ/4); */
return 0;
case TIOCGSOFTCAR:
pr_debug("SCC: ioctl TIOCGSOFTCAR\n");
if (put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg))
return -EFAULT;
return 0;
case TIOCSSOFTCAR:
pr_debug("SCC: ioctl TIOCSSOFTCAR\n");
if (get_user(arg, (unsigned long *)arg))
return -EFAULT;
tty->termios->c_cflag =
((tty->termios->c_cflag & ~CLOCAL) |
(arg ? CLOCAL : 0));
return 0;
case TIOCMGET:
pr_debug("SCC: ioctl TIOCMGET\n");
/* return get_modem_info(info, (unsigned int *)arg); */
return 0;
case TIOCMBIS:
case TIOCMBIC:
case TIOCMSET:
pr_debug("SCC: ioctl TIOCMSET\n");
/* return set_modem_info(info, cmd, (unsigned int *)arg); */
return 0;
case TIOCGSERIAL:
pr_debug("SCC: ioctl TIOCGSERIAL\n");
return 0;
/* return get_serial_info(info, (struct serial_struct *)arg); */
case TIOCSSERIAL:
pr_debug("SCC: ioctl TIOCSSERIAL\n");
return 0;
/* return set_serial_info(info, (struct serial_struct *)arg); */
case TIOCSERGETLSR: /* Get line status register */
pr_debug("SCC: ioctl TIOCSERGETLSR\n");
return 0;
/* return get_lsr_info(info, (unsigned int *)arg); */
case TIOCSERGSTRUCT:
pr_debug("SCC: ioctl TIOCSERGSTRUCT\n");
return 0;
if (copy_to_user((struct scc_port *)arg, port,
sizeof(struct scc_port)))
return -EFAULT;
return 0;
default:
pr_debug("SCC: ioctl default\n");
return -ENOIOCTLCMD;
}
return 0;
}
static int scc_break_ctl(struct tty_struct *tty, int break_state)
{
struct scc_port *port = tty->driver_data;
unsigned long flags;
SCC_ACCESS_INIT(port);
pr_debug("SCC: break ctl ...\n");
local_irq_save(flags);
SCCmod(TX_CTRL_REG, ~TCR_SEND_BREAK, break_state ? TCR_SEND_BREAK : 0);
local_irq_restore(flags);
pr_debug("SCC: break ctl done!\n");
return 0;
}
#if defined(CONFIG_SERIAL_CONSOLE) && !defined(MODULE)
/*---------------------------------------------------------------------------
* Serial console stuff...
*--------------------------------------------------------------------------*/
#define scc_delay() \
asm volatile ("tstb %0" : : "m" (*scc_del) : "cc")
#define SCC_WRITE(reg,val) \
do { \
scc.cha_b_ctrl = (reg); \
scc_delay(); \
scc.cha_b_ctrl = (val); \
scc_delay(); \
} while (0)
/*
* loops_per_jiffy isn't initialized yet, so we can't use udelay().
* This does a delay of ~ 60us.
*/
#define LONG_DELAY() \
do { \
int i; \
for (i = 100; i > 0; i--) \
scc_delay(); \
} while (0)
static void atari_init_scc_port(int cflag)
{
static int clksrc_table[9] =
/* reg 11: 0x50 = BRG, 0x00 = RTxC, 0x28 = TRxC */
{ 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x00, 0x00 };
static int brgsrc_table[9] =
/* reg 14: 0 = RTxC, 2 = PCLK */
{ 2, 2, 2, 2, 2, 2, 0, 2, 2 };
static int clkmode_table[9] =
/* reg 4: 0x40 = x16, 0x80 = x32, 0xc0 = x64 */
{ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0xc0, 0x80 };
static int div_table[9] =
/* reg12 (BRG low) */
{ 208, 138, 103, 50, 24, 11, 1, 0, 0 };
int baud = cflag & CBAUD;
int clksrc, clkmode, div, reg3, reg5;
scc_del = &st_mfp.par_dt_reg;
if (cflag & CBAUDEX)
baud += B38400;
if (baud < B1200 || baud > B38400+2) {
/* use default 9600bps for non-implemented rates */
baud = B9600;
}
baud -= B1200; /* tables starts at 1200bps */
clksrc = clksrc_table[baud];
clkmode = clkmode_table[baud];
div = div_table[baud];
if (ATARIHW_PRESENT(TT_MFP) && baud >= 6) {
/*
* special treatment for TT, where rates >= 38400 are done via
* TRxC
*/
clksrc = 0x28; /* TRxC */
clkmode = baud == 6 ? 0xc0 :
baud == 7 ? 0x80 : /* really 76800bps */
0x40; /* really 153600bps */
div = 0;
}
reg3 = (cflag & CSIZE) == CS8 ? 0xc0 : 0x40;
reg5 = (cflag & CSIZE) == CS8 ? 0x60 : 0x20 | 0x82 /* assert DTR/RTS */;
(void)scc.cha_b_ctrl; /* reset reg pointer */
SCC_WRITE(9, 0xc0); /* reset */
LONG_DELAY(); /* extra delay after WR9 access */
SCC_WRITE(4, (cflag & PARENB) ? ((cflag & PARODD) ? 0x01 : 0x03) : 0 |
0x04 /* 1 stopbit */ |
clkmode);
SCC_WRITE(3, reg3);
SCC_WRITE(5, reg5);
SCC_WRITE(9, 0); /* no interrupts */
LONG_DELAY(); /* extra delay after WR9 access */
SCC_WRITE(10, 0); /* NRZ mode */
SCC_WRITE(11, clksrc); /* main clock source */
SCC_WRITE(12, div); /* BRG value */
SCC_WRITE(13, 0); /* BRG high byte */
SCC_WRITE(14, brgsrc_table[baud]);
SCC_WRITE(14, brgsrc_table[baud] | (div ? 1 : 0));
SCC_WRITE(3, reg3 | 1);
SCC_WRITE(5, reg5 | 8);
atari_SCC_reset_done = 1;
atari_SCC_init_done = 1;
}
static void scc_ch_write(char ch)
{
volatile char *p = NULL;
if (MACH_IS_TT || MACH_IS_FALCON)
p = (volatile char *)&scc.cha_b_ctrl;
if (MACH_IS_ST)
p = (volatile char *)&scc.cha_b_ctrl;
if (MACH_IS_STE)
p = (volatile char *)&st_escc.cha_b_ctrl;
do {
scc_delay();
} while (!(*p & 4));
/* scc_delay(); */
/* *p = 8; */
scc_delay();
*(p+1) = ch;
}
/* The console must be locked when we get here. */
static void scc_console_write(struct console *co, const char *str,
unsigned count)
{
unsigned long flags;
/* printk("scc_console_write: %s\n", str); */
local_irq_save(flags);
while (count--) {
if (*str == '\n')
scc_ch_write('\r');
scc_ch_write(*str++);
}
local_irq_restore(flags);
/* printk("scc_console_write done!\n"); */
}
static struct tty_driver *scc_console_device(struct console *c, int *index)
{
*index = c->index;
return scc_driver;
}
static int __init scc_console_setup(struct console *co, char *options)
{
pr_debug("scc_console_setup: initializing SCC port B\n");
atari_init_scc_port(B9600|CS8);
pr_debug("scc_console_setup: done!\n");
return 0;
}
static struct console sercons = {
.name = "ttyS",
.write = scc_console_write,
.device = scc_console_device,
.setup = scc_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
};
static int __init atari_scc_console_init(void)
{
if (MACH_IS_TT || MACH_IS_ST || MACH_IS_FALCON)
register_console(&sercons);
return 0;
}
console_initcall(atari_scc_console_init);
#endif /* CONFIG_SERIAL_CONSOLE && !MODULE */
/***************************** End of Functions *********************/
MODULE_AUTHOR("Michael Schmitz");
MODULE_DESCRIPTION("Atari Amd8350 SCC serial driver");
MODULE_LICENSE("GPL");