Commit 0b520647 authored by Linus Torvalds's avatar Linus Torvalds

Merge master.kernel.org:/home/davem/BK/sparc-2.5

into home.transmeta.com:/home/torvalds/v2.5/linux
parents 35aa61ec e79e45cb
......@@ -10,6 +10,7 @@
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/string.h>
#include <linux/spinlock.h>
#include <asm/page.h>
#include <asm/oplib.h>
......@@ -17,6 +18,11 @@
#include <asm/smp.h>
#include <asm/spitfire.h>
/* Used to synchronize acceses to NatSemi SUPER I/O chip configure
* operations in asm/ns87303.h
*/
spinlock_t ns87303_lock = SPIN_LOCK_UNLOCKED;
struct prom_cpuinfo linux_cpus[NR_CPUS] __initdata = { { 0 } };
unsigned prom_cpu_nodes[NR_CPUS];
int linux_num_cpus = 0;
......
......@@ -45,7 +45,6 @@ cpuinfo_sparc cpu_data[NR_CPUS];
/* Please don't make this stuff initdata!!! --DaveM */
static unsigned char boot_cpu_id;
static int smp_activated;
/* Kernel spinlock */
spinlock_t kernel_flag __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
......@@ -223,85 +222,46 @@ extern unsigned long sparc64_cpu_startup;
*/
static struct thread_info *cpu_new_thread = NULL;
static void __init smp_boot_cpus(unsigned int max_cpus)
static int __devinit smp_boot_one_cpu(unsigned int cpu)
{
int cpucount = 0, i;
printk("Entering UltraSMPenguin Mode...\n");
local_irq_enable();
smp_store_cpu_info(boot_cpu_id);
if (linux_num_cpus == 1)
return;
for (i = 0; i < NR_CPUS; i++) {
if (i == boot_cpu_id)
continue;
if ((cpucount + 1) == max_cpus)
goto ignorecpu;
if (test_bit(i, &phys_cpu_present_map)) {
unsigned long entry =
(unsigned long)(&sparc64_cpu_startup);
unsigned long cookie =
(unsigned long)(&cpu_new_thread);
struct task_struct *p;
int timeout;
int no;
int timeout, no, ret;
prom_printf("Starting CPU %d... ", i);
kernel_thread(NULL, NULL, CLONE_IDLETASK);
cpucount++;
p = prev_task(&init_task);
init_idle(p, i);
init_idle(p, cpu);
unhash_process(p);
callin_flag = 0;
for (no = 0; no < linux_num_cpus; no++)
if (linux_cpus[no].mid == i)
if (linux_cpus[no].mid == cpu)
break;
cpu_new_thread = p->thread_info;
set_bit(i, &cpu_callout_map);
prom_startcpu(linux_cpus[no].prom_node,
entry, cookie);
set_bit(cpu, &cpu_callout_map);
prom_startcpu(linux_cpus[no].prom_node, entry, cookie);
for (timeout = 0; timeout < 5000000; timeout++) {
if (callin_flag)
break;
udelay(100);
}
if (callin_flag) {
prom_cpu_nodes[i] = linux_cpus[no].prom_node;
prom_printf("OK\n");
prom_cpu_nodes[cpu] = linux_cpus[no].prom_node;
ret = 0;
} else {
cpucount--;
printk("Processor %d is stuck.\n", i);
prom_printf("FAILED\n");
clear_bit(i, &cpu_callout_map);
}
ignorecpu:
}
printk("Processor %d is stuck.\n", cpu);
clear_bit(cpu, &cpu_callout_map);
ret = -ENODEV;
}
cpu_new_thread = NULL;
if (cpucount == 0) {
if (max_cpus != 1)
printk("Error: only one processor found.\n");
} else {
unsigned long bogosum = 0;
for (i = 0; i < NR_CPUS; i++) {
if (cpu_online(i))
bogosum += cpu_data[i].udelay_val;
}
printk("Total of %d processors activated "
"(%lu.%02lu BogoMIPS).\n",
cpucount + 1,
bogosum/(500000/HZ),
(bogosum/(5000/HZ))%100);
smp_activated = 1;
}
return ret;
}
static void spitfire_xcall_helper(u64 data0, u64 data1, u64 data2, u64 pstate, unsigned long cpu)
......@@ -1119,8 +1079,6 @@ static void __init smp_setup_percpu_timer(void)
void __init smp_tick_init(void)
{
int i;
boot_cpu_id = hard_smp_processor_id();
current_tick_offset = timer_tick_offset;
......@@ -1129,19 +1087,10 @@ void __init smp_tick_init(void)
prom_halt();
}
atomic_set(&sparc64_num_cpus_online, 1);
memset(&cpu_online_map, 0, sizeof(cpu_online_map));
atomic_inc(&sparc64_num_cpus_online);
set_bit(boot_cpu_id, &cpu_online_map);
prom_cpu_nodes[boot_cpu_id] = linux_cpus[0].prom_node;
prof_counter(boot_cpu_id) = prof_multiplier(boot_cpu_id) = 1;
for (i = 0; i < linux_num_cpus; i++) {
if (linux_cpus[i].mid < NR_CPUS) {
set_bit(linux_cpus[i].mid,
&phys_cpu_present_map);
atomic_inc(&sparc64_num_cpus_possible);
}
}
}
cycles_t cacheflush_time;
......@@ -1272,19 +1221,59 @@ int setup_profiling_timer(unsigned int multiplier)
void __init smp_prepare_cpus(unsigned int max_cpus)
{
smp_boot_cpus(max_cpus);
int i;
for (i = 0; i < linux_num_cpus; i++) {
if (linux_cpus[i].mid < max_cpus) {
set_bit(linux_cpus[i].mid,
&phys_cpu_present_map);
atomic_inc(&sparc64_num_cpus_possible);
}
}
if (atomic_read(&sparc64_num_cpus_possible) > max_cpus) {
for (i = linux_num_cpus - 1; i >= 0; i--) {
if (linux_cpus[i].mid != boot_cpu_id) {
clear_bit(linux_cpus[i].mid,
&phys_cpu_present_map);
atomic_dec(&sparc64_num_cpus_possible);
if (atomic_read(&sparc64_num_cpus_possible) <= max_cpus)
break;
}
}
}
smp_store_cpu_info(boot_cpu_id);
}
int __devinit __cpu_up(unsigned int cpu)
{
int ret = smp_boot_one_cpu(cpu);
if (!ret) {
set_bit(cpu, &smp_commenced_mask);
while (!test_bit(cpu, &cpu_online_map))
mb();
return 0;
if (!test_bit(cpu, &cpu_online_map))
ret = -ENODEV;
}
return ret;
}
void __init smp_cpus_done(unsigned int max_cpus)
{
unsigned long bogosum = 0;
int i;
for (i = 0; i < NR_CPUS; i++) {
if (cpu_online(i))
bogosum += cpu_data[i].udelay_val;
}
printk("Total of %d processors activated "
"(%lu.%02lu BogoMIPS).\n",
num_online_cpus(),
bogosum/(500000/HZ),
(bogosum/(5000/HZ))%100);
/* We want to run this with all the other cpus spinning
* in the kernel.
*/
......
......@@ -52,6 +52,7 @@
#include <asm/isa.h>
#endif
#include <asm/a.out.h>
#include <asm/ns87303.h>
struct poll {
int fd;
......@@ -373,3 +374,6 @@ EXPORT_SYMBOL(kbd_pt_regs);
#ifdef CONFIG_DEBUG_BUGVERBOSE
EXPORT_SYMBOL(do_BUG);
#endif
/* for ns8703 */
EXPORT_SYMBOL(ns87303_lock);
......@@ -250,8 +250,8 @@ solaris_sys_table:
.word solaris_fstatvfs64 /* fstatvfs64 dP 219 */
.word solaris_setrlimit64 /* setrlimit64 dP 220 */
.word solaris_getrlimit64 /* getrlimit64 dP 221 */
.word CHAIN(pread) /* pread64 dpdD 222 */
.word CHAIN(pwrite) /* pwrite64 dpdD 223 */
.word CHAIN(pread64) /* pread64 dpdD 222 */
.word CHAIN(pwrite64) /* pwrite64 dpdD 223 */
.word CHAIN(creat) /* creat64 so 224 */
.word solaris_open /* open64 soo 225 */
.word solaris_unimplemented /* 226 */
......
......@@ -546,8 +546,11 @@ static void myri_interrupt(int irq, void *dev_id, struct pt_regs *regs)
struct myri_eth *mp = (struct myri_eth *) dev->priv;
unsigned long lregs = mp->lregs;
struct myri_channel *chan = &mp->shmem->channel;
unsigned long flags;
u32 status;
spin_lock_irqsave(&mp->irq_lock, flags);
status = sbus_readl(lregs + LANAI_ISTAT);
DIRQ(("myri_interrupt: status[%08x] ", status));
if (status & ISTAT_HOST) {
......@@ -569,6 +572,8 @@ static void myri_interrupt(int irq, void *dev_id, struct pt_regs *regs)
myri_enable_irq(lregs, mp->cregs);
}
DIRQ(("\n"));
spin_unlock_irqrestore(&mp->irq_lock, flags);
}
static int myri_open(struct net_device *dev)
......@@ -622,7 +627,7 @@ static int myri_start_xmit(struct sk_buff *skb, struct net_device *dev)
return 1;
}
save_and_cli(flags);
spin_lock_irqsave(&mp->irq_lock, flags);
DHDR(("xmit[skbdata(%p)]\n", skb->data));
#ifdef DEBUG_HEADER
......@@ -669,7 +674,7 @@ static int myri_start_xmit(struct sk_buff *skb, struct net_device *dev)
DTX(("tbusy=0, returning 0\n"));
netif_start_queue(dev);
restore_flags(flags);
spin_unlock_irqrestore(&mp->irq_lock, flags);
return 0;
}
......@@ -900,6 +905,7 @@ static int __init myri_ether_init(struct net_device *dev, struct sbus_dev *sdev,
printk("%s: MyriCOM MyriNET Ethernet ", dev->name);
mp = (struct myri_eth *) dev->priv;
spin_lock_init(&mp->irq_lock);
mp->myri_sdev = sdev;
/* Clean out skb arrays. */
......
......@@ -269,6 +269,7 @@ struct myri_eth {
/* These are frequently accessed, keep together
* to obtain good cache hit rates.
*/
spinlock_t irq_lock;
struct myri_shmem *shmem; /* Shared data structures. */
unsigned long cregs; /* Control register space. */
struct recvq *rqack; /* Where we ack rx's. */
......
......@@ -147,7 +147,6 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file,
DATA *data = (DATA *) file->private_data;
char buffer[OPROMMAXPARAM+1], *buf;
struct openpromio *opp;
unsigned long flags;
int bufsize, len, error = 0;
extern char saved_command_line[];
static int cnt;
......@@ -163,18 +162,14 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file,
switch (cmd) {
case OPROMGETOPT:
case OPROMGETPROP:
save_and_cli(flags);
len = prom_getproplen(node, opp->oprom_array);
restore_flags(flags);
if (len <= 0 || len > bufsize) {
error = copyout((void *)arg, opp, sizeof(int));
break;
}
save_and_cli(flags);
len = prom_getproperty(node, opp->oprom_array, buffer, bufsize);
restore_flags(flags);
memcpy(opp->oprom_array, buffer, len);
opp->oprom_array[len] = '\0';
......@@ -185,9 +180,7 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file,
case OPROMNXTOPT:
case OPROMNXTPROP:
save_and_cli(flags);
buf = prom_nextprop(node, opp->oprom_array, buffer);
restore_flags(flags);
len = strlen(buf);
if (len == 0 || len + 1 > bufsize) {
......@@ -207,10 +200,8 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file,
buf = opp->oprom_array + strlen(opp->oprom_array) + 1;
len = opp->oprom_array + bufsize - buf;
save_and_cli(flags);
error = prom_setprop(options_node, opp->oprom_array,
buf, len);
restore_flags(flags);
if (error < 0)
error = -EINVAL;
......@@ -226,13 +217,11 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file,
node = *((int *) opp->oprom_array);
save_and_cli(flags);
switch (cmd) {
case OPROMNEXT: node = __prom_getsibling(node); break;
case OPROMCHILD: node = __prom_getchild(node); break;
case OPROMSETCUR: break;
}
restore_flags(flags);
data->current_node = node;
*((int *)opp->oprom_array) = node;
......@@ -264,9 +253,7 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file,
break;
case OPROMPATH2NODE:
save_and_cli(flags);
node = prom_finddevice(opp->oprom_array);
restore_flags(flags);
data->current_node = node;
*((int *)opp->oprom_array) = node;
opp->oprom_size = sizeof(int);
......@@ -361,7 +348,6 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file,
{
DATA *data = (DATA *) file->private_data;
struct opiocdesc op;
unsigned long flags;
int error, node, len;
char *str, *tmp;
char buffer[64];
......@@ -379,9 +365,7 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file,
if (error)
return error;
save_and_cli(flags);
len = prom_getproplen(op.op_nodeid,str);
restore_flags(flags);
if (len > op.op_buflen) {
kfree(str);
......@@ -405,9 +389,7 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file,
return -ENOMEM;
}
save_and_cli(flags);
prom_getproperty(op.op_nodeid, str, tmp, len);
restore_flags(flags);
tmp[len] = '\0';
......@@ -431,9 +413,7 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file,
if (error)
return error;
save_and_cli(flags);
tmp = prom_nextprop(op.op_nodeid,str,buffer);
restore_flags(flags);
if (tmp) {
len = strlen(tmp);
......@@ -481,9 +461,7 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file,
return error;
}
save_and_cli(flags);
len = prom_setprop(op.op_nodeid,str,tmp,op.op_buflen+1);
restore_flags(flags);
if (len != op.op_buflen)
return -EINVAL;
......@@ -503,12 +481,10 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file,
if (copy_from_user(&node, (void *)arg, sizeof(int)))
return -EFAULT;
save_and_cli(flags);
if (cmd == OPIOCGETNEXT)
node = __prom_getsibling(node);
else
node = __prom_getchild(node);
restore_flags(flags);
if (__copy_to_user((void *)arg, &node, sizeof(int)))
return -EFAULT;
......@@ -624,7 +600,6 @@ static struct miscdevice openprom_dev = {
static int __init openprom_init(void)
{
unsigned long flags;
int error;
error = misc_register(&openprom_dev);
......@@ -633,10 +608,8 @@ static int __init openprom_init(void)
return error;
}
save_and_cli(flags);
options_node = prom_getchild(prom_root_node);
options_node = prom_searchsiblings(options_node,"options");
restore_flags(flags);
if (options_node == 0 || options_node == -1) {
printk(KERN_ERR "openprom: unable to find options node\n");
......
......@@ -318,7 +318,7 @@ static int qlogicpti_reset_hardware(struct Scsi_Host *host)
risc_code_addr = 0x1000; /* all load addresses are at 0x1000 */
save_flags(flags); cli();
spin_lock_irqsave(&qpti->lock, flags);
sbus_writew(HCCTRL_PAUSE, qpti->qregs + HCCTRL);
......@@ -366,7 +366,7 @@ static int qlogicpti_reset_hardware(struct Scsi_Host *host)
if (qlogicpti_mbox_command(qpti, param, 1)) {
printk(KERN_EMERG "qlogicpti%d: Cannot execute ISP firmware.\n",
qpti->qpti_id);
restore_flags(flags);
spin_unlock_irqrestore(&qpti->lock, flags);
return 1;
}
......@@ -377,7 +377,7 @@ static int qlogicpti_reset_hardware(struct Scsi_Host *host)
(param[0] != MBOX_COMMAND_COMPLETE)) {
printk(KERN_EMERG "qlogicpti%d: Cannot set initiator SCSI ID.\n",
qpti->qpti_id);
restore_flags(flags);
spin_unlock_irqrestore(&qpti->lock, flags);
return 1;
}
......@@ -392,7 +392,7 @@ static int qlogicpti_reset_hardware(struct Scsi_Host *host)
if (qlogicpti_mbox_command(qpti, param, 1)) {
printk(KERN_EMERG "qlogicpti%d: Cannot init response queue.\n",
qpti->qpti_id);
restore_flags(flags);
spin_unlock_irqrestore(&qpti->lock, flags);
return 1;
}
......@@ -404,7 +404,7 @@ static int qlogicpti_reset_hardware(struct Scsi_Host *host)
if (qlogicpti_mbox_command(qpti, param, 1)) {
printk(KERN_EMERG "qlogicpti%d: Cannot init request queue.\n",
qpti->qpti_id);
restore_flags(flags);
spin_unlock_irqrestore(&qpti->lock, flags);
return 1;
}
......@@ -450,7 +450,7 @@ static int qlogicpti_reset_hardware(struct Scsi_Host *host)
qlogicpti_mbox_command(qpti, param, 0);
qpti->send_marker = 1;
restore_flags(flags);
spin_unlock_irqrestore(&qpti->lock, flags);
return 0;
}
......@@ -468,7 +468,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti)
risc_code_addr = 0x1000; /* all f/w modules load at 0x1000 */
risc_code_length = sbus_risc_code_length01;
save_flags(flags); cli();
spin_lock_irqsave(&qpti->lock, flags);
/* Verify the checksum twice, one before loading it, and once
* afterwards via the mailbox commands.
......@@ -476,7 +476,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti)
for (i = 0; i < risc_code_length; i++)
csum += risc_code[i];
if (csum) {
restore_flags(flags);
spin_unlock_irqrestore(&qpti->lock, flags);
printk(KERN_EMERG "qlogicpti%d: Aieee, firmware checksum failed!",
qpti->qpti_id);
return 1;
......@@ -488,7 +488,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti)
while (--timeout && (sbus_readw(qpti->qregs + SBUS_CTRL) & SBUS_CTRL_RESET))
udelay(20);
if (!timeout) {
restore_flags(flags);
spin_unlock_irqrestore(&qpti->lock, flags);
printk(KERN_EMERG "qlogicpti%d: Cannot reset the ISP.", qpti->qpti_id);
return 1;
}
......@@ -528,7 +528,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti)
if (qlogicpti_mbox_command(qpti, param, 1)) {
printk(KERN_EMERG "qlogicpti%d: Cannot stop firmware for reload.\n",
qpti->qpti_id);
restore_flags(flags);
spin_unlock_irqrestore(&qpti->lock, flags);
return 1;
}
......@@ -541,7 +541,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti)
param[0] != MBOX_COMMAND_COMPLETE) {
printk("qlogicpti%d: Firmware dload failed, I'm bolixed!\n",
qpti->qpti_id);
restore_flags(flags);
spin_unlock_irqrestore(&qpti->lock, flags);
return 1;
}
}
......@@ -561,7 +561,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti)
(param[0] != MBOX_COMMAND_COMPLETE)) {
printk(KERN_EMERG "qlogicpti%d: New firmware csum failure!\n",
qpti->qpti_id);
restore_flags(flags);
spin_unlock_irqrestore(&qpti->lock, flags);
return 1;
}
......@@ -575,7 +575,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti)
(param[0] != MBOX_COMMAND_COMPLETE)) {
printk(KERN_EMERG "qlogicpti%d: AboutFirmware cmd fails.\n",
qpti->qpti_id);
restore_flags(flags);
spin_unlock_irqrestore(&qpti->lock, flags);
return 1;
}
......@@ -591,7 +591,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti)
(param[0] != MBOX_COMMAND_COMPLETE)) {
printk(KERN_EMERG "qlogicpti%d: could not set clock rate.\n",
qpti->qpti_id);
restore_flags(flags);
spin_unlock_irqrestore(&qpti->lock, flags);
return 1;
}
......@@ -608,7 +608,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti)
qlogicpti_mbox_command(qpti, param, 1);
}
restore_flags(flags);
spin_unlock_irqrestore(&qpti->lock, flags);
return 0;
}
......@@ -1166,8 +1166,8 @@ static void ourdone(Scsi_Cmnd *Cmnd)
int qlogicpti_queuecommand_slow(Scsi_Cmnd *Cmnd, void (*done)(Scsi_Cmnd *))
{
unsigned long flags;
struct qlogicpti *qpti = (struct qlogicpti *) Cmnd->host->hostdata;
unsigned long flags;
/*
* done checking this host adapter?
......@@ -1178,12 +1178,13 @@ int qlogicpti_queuecommand_slow(Scsi_Cmnd *Cmnd, void (*done)(Scsi_Cmnd *))
if (qpti->sbits && qpti->sbits != 0xffff) {
/* See above about in ourdone this ugliness... */
Cmnd->SCp.Message = ((unsigned long)done) & 0xffffffff;
#ifdef __sparc_v9__
#ifdef CONFIG_SPARC64
Cmnd->SCp.Status = ((unsigned long)done >> 32UL) & 0xffffffff;
#endif
return qlogicpti_queuecommand(Cmnd, ourdone);
}
save_flags(flags); cli();
spin_lock_irqsave(&qpti->lock, flags);
/*
* We've peeked at all targets for this bus- time
......@@ -1226,7 +1227,8 @@ int qlogicpti_queuecommand_slow(Scsi_Cmnd *Cmnd, void (*done)(Scsi_Cmnd *))
if (qpti == NULL)
Cmnd->host->hostt->queuecommand = qlogicpti_queuecommand;
restore_flags(flags);
spin_unlock_irqrestore(&qpti->lock, flags);
return qlogicpti_queuecommand(Cmnd, done);
}
......
/* $Id: sab82532.c,v 1.66 2002/01/08 16:00:16 davem Exp $
* sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
/* sunsab.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
* Copyright (C) 2002 David S. Miller (davem@redhat.com)
*
* Rewrote buffer handling to use CIRC(Circular Buffer) macros.
* Maxim Krasnyanskiy <maxk@qualcomm.com>
......@@ -10,92 +10,61 @@
* rates to be programmed into the UART. Also eliminated a lot of
* duplicated code in the console setup.
* Theodore Ts'o <tytso@mit.edu>, 2001-Oct-12
*
* Ported to new 2.5.x UART layer.
* David S. Miller <davem@redhat.com>
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/serialP.h>
#include <linux/serial_reg.h>
#include <linux/console.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/circ_buf.h>
#include <linux/serial.h>
#include <linux/console.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/sab82532.h>
#include <asm/uaccess.h>
#include <asm/ebus.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/oplib.h>
#include <asm/ebus.h>
#include "sunserial.h"
static DECLARE_TASK_QUEUE(tq_serial);
/* This is (one of many) a special gross hack to allow SU and
* SAB serials to co-exist on the same machine. -DaveM
*/
#undef SERIAL_BH
#define SERIAL_BH AURORA_BH
static struct tty_driver serial_driver, callout_driver;
static int sab82532_refcount;
#undef SERIAL_PARANOIA_CHECK
#define SERIAL_DO_RESTART
/* Set of debugging defines */
#undef SERIAL_DEBUG_OPEN
#undef SERIAL_DEBUG_FLOW
#undef SERIAL_DEBUG_MODEM
#undef SERIAL_DEBUG_WAIT_UNTIL_SENT
#undef SERIAL_DEBUG_SEND_BREAK
#undef SERIAL_DEBUG_INTR
#undef SERIAL_DEBUG_FIFO
#define SERIAL_DEBUG_OVERFLOW 1
/* Trace things on serial device, useful for console debugging: */
#undef SERIAL_LOG_DEVICE
#ifdef SERIAL_LOG_DEVICE
static void dprint_init(int tty);
#endif
static void change_speed(struct sab82532 *info);
static void sab82532_wait_until_sent(struct tty_struct *tty, int timeout);
#include <linux/serial_core.h>
#include "suncore.h"
#include "sunsab.h"
struct uart_sunsab_port {
struct uart_port port; /* Generic UART port */
union sab82532_async_regs *regs; /* Chip registers */
unsigned long irqflags; /* IRQ state flags */
int xmit_fifo_size; /* TX fifo size */
int recv_fifo_size; /* RX fifo size */
int dsr; /* Current DSR state */
unsigned int cec_timeout; /* Chip poll timeout... */
unsigned int tec_timeout; /* likewise */
unsigned char interrupt_mask0;/* ISR0 masking */
unsigned char interrupt_mask1;/* ISR1 masking */
unsigned char pvr_dtr_bit; /* Which PVR bit is DTR */
unsigned char pvr_dsr_bit; /* Which PVR bit is DSR */
int type; /* SAB82532 version */
int sab_line; /* Internal numbering */
unsigned int irq; /* Device interrupt */
};
/*
* This assumes you have a 29.4912 MHz clock for your UART.
*/
#define BASE_BAUD ( 29491200 / 16 )
static struct sab82532 *sab82532_chain = 0;
static struct tty_struct *sab82532_table[NR_PORTS];
static struct termios *sab82532_termios[NR_PORTS];
static struct termios *sab82532_termios_locked[NR_PORTS];
#ifdef CONFIG_SERIAL_CONSOLE
extern int serial_console;
static struct console sab82532_console;
static int sab82532_console_init(void);
static void batten_down_hatches(struct sab82532 *info);
#endif
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
#define SAB_BASE_BAUD ( 29491200 / 16 )
static char *sab82532_version[16] = {
"V1.0", "V2.0", "V3.2", "V(0x03)",
......@@ -103,841 +72,601 @@ static char *sab82532_version[16] = {
"V(0x08)", "V(0x09)", "V(0x0a)", "V(0x0b)",
"V(0x0c)", "V(0x0d)", "V(0x0e)", "V(0x0f)"
};
static char serial_version[16];
/*
* tmp_buf is used as a temporary buffer by sab82532_write. We need to
* lock it in case the copy_from_user blocks while swapping in a page,
* and some other program tries to do a serial write at the same time.
* Since the lock will only come under contention when the system is
* swapping and available memory is low, it makes sense to share one
* buffer across all the serial ports, since it significantly saves
* memory if large numbers of serial ports are open.
*/
static unsigned char *tmp_buf = 0;
static DECLARE_MUTEX(tmp_buf_sem);
static inline int serial_paranoia_check(struct sab82532 *info,
kdev_t device, const char *routine)
{
#ifdef SERIAL_PARANOIA_CHECK
static const char *badmagic =
"Warning: bad magic number for serial struct (%s) in %s\n";
static const char *badinfo =
"Warning: null sab82532 for (%s) in %s\n";
if (!info) {
printk(badinfo, kdevname(device), routine);
return 1;
}
if (info->magic != SERIAL_MAGIC) {
printk(badmagic, kdevname(device), routine);
return 1;
}
#endif
return 0;
}
/*
* This is used to figure out the divisor speeds.
*
* The formula is: Baud = BASE_BAUD / ((N + 1) * (1 << M)),
*
* with 0 <= N < 64 and 0 <= M < 16
*
* 12-Oct-2001 - Replaced table driven approach with code written by
* Theodore Ts'o <tytso@alum.mit.edu> which exactly replicates the
* table. (Modulo bugs for the 307200 and 61440 baud rates, which
* were clearly incorrectly calculated in the original table. This is
* why tables filled with magic constants are evil.)
*/
static void calc_ebrg(int baud, int *n_ret, int *m_ret)
{
int n, m;
if (baud == 0) {
*n_ret = 0;
*m_ret = 0;
return;
}
/*
* We scale numbers by 10 so that we get better accuracy
* without having to use floating point. Here we increment m
* until n is within the valid range.
*/
n = (BASE_BAUD*10) / baud;
m = 0;
while (n >= 640) {
n = n / 2;
m++;
}
n = (n+5) / 10;
/*
* We try very hard to avoid speeds with M == 0 since they may
* not work correctly for XTAL frequences above 10 MHz.
*/
if ((m == 0) && ((n & 1) == 0)) {
n = n / 2;
m++;
}
*n_ret = n - 1;
*m_ret = m;
}
#define SAB82532_MAX_TEC_TIMEOUT 200000 /* 1 character time (at 50 baud) */
#define SAB82532_MAX_CEC_TIMEOUT 50000 /* 2.5 TX CLKs (at 50 baud) */
static __inline__ void sab82532_tec_wait(struct sab82532 *info)
static __inline__ void sunsab_tec_wait(struct uart_sunsab_port *up)
{
int timeout = info->tec_timeout;
int timeout = up->tec_timeout;
while ((readb(&info->regs->r.star) & SAB82532_STAR_TEC) && --timeout)
while ((readb(&up->regs->r.star) & SAB82532_STAR_TEC) && --timeout)
udelay(1);
}
static __inline__ void sab82532_cec_wait(struct sab82532 *info)
static __inline__ void sunsab_cec_wait(struct uart_sunsab_port *up)
{
int timeout = info->cec_timeout;
int timeout = up->cec_timeout;
while ((readb(&info->regs->r.star) & SAB82532_STAR_CEC) && --timeout)
while ((readb(&up->regs->r.star) & SAB82532_STAR_CEC) && --timeout)
udelay(1);
}
static __inline__ void sab82532_start_tx(struct sab82532 *info)
{
unsigned long flags;
int i;
save_flags(flags); cli();
if (info->xmit.head == info->xmit.tail)
goto out;
if (!test_bit(SAB82532_XPR, &info->irqflags))
goto out;
info->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS);
writeb(info->interrupt_mask1, &info->regs->w.imr1);
clear_bit(SAB82532_ALLS, &info->irqflags);
clear_bit(SAB82532_XPR, &info->irqflags);
for (i = 0; i < info->xmit_fifo_size; i++) {
writeb(info->xmit.buf[info->xmit.tail],
&info->regs->w.xfifo[i]);
info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1);
info->icount.tx++;
if (info->xmit.head == info->xmit.tail)
break;
}
/* Issue a Transmit Frame command. */
sab82532_cec_wait(info);
writeb(SAB82532_CMDR_XF, &info->regs->w.cmdr);
out:
restore_flags(flags);
}
/*
* ------------------------------------------------------------
* sab82532_stop() and sab82532_start()
*
* This routines are called before setting or resetting tty->stopped.
* They enable or disable transmitter interrupts, as necessary.
* ------------------------------------------------------------
*/
static void sab82532_stop(struct tty_struct *tty)
{
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
unsigned long flags;
if (serial_paranoia_check(info, tty->device, "sab82532_stop"))
return;
save_flags(flags); cli();
info->interrupt_mask1 |= SAB82532_IMR1_XPR;
writeb(info->interrupt_mask1, &info->regs->w.imr1);
restore_flags(flags);
}
static void sab82532_start(struct tty_struct *tty)
{
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
unsigned long flags;
if (serial_paranoia_check(info, tty->device, "sab82532_start"))
return;
save_flags(flags); cli();
info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
writeb(info->interrupt_mask1, &info->regs->w.imr1);
sab82532_start_tx(info);
restore_flags(flags);
}
/*
* ----------------------------------------------------------------------
*
* Here starts the interrupt handling routines. All of the following
* subroutines are declared as inline and are folded into
* sab82532_interrupt(). They were separated out for readability's sake.
*
* Note: sab82532_interrupt() is a "fast" interrupt, which means that it
* runs with interrupts turned off. People who may want to modify
* sab82532_interrupt() should try to keep the interrupt handler as fast as
* possible. After you are done making modifications, it is not a bad
* idea to do:
*
* gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
*
* and look at the resulting assemble code in serial.s.
*
* - Ted Ts'o (tytso@mit.edu), 7-Mar-93
* -----------------------------------------------------------------------
*/
/*
* This routine is used by the interrupt handler to schedule
* processing in the software interrupt portion of the driver.
*/
static void sab82532_sched_event(struct sab82532 *info, int event)
{
info->event |= 1 << event;
queue_task(&info->tqueue, &tq_serial);
mark_bh(SERIAL_BH);
}
static void receive_chars(struct sab82532 *info,
union sab82532_irq_status *stat)
static void receive_chars(struct uart_sunsab_port *up,
union sab82532_irq_status *stat,
struct pt_regs *regs)
{
struct tty_struct *tty = info->tty;
struct tty_struct *tty = up->port.info->tty;
unsigned char buf[32];
unsigned char status;
int saw_console_brk = 0;
int free_fifo = 0;
int i, count = 0;
int count = 0;
int i;
/* Read number of BYTES (Character + Status) available. */
if (stat->sreg.isr0 & SAB82532_ISR0_RPF) {
count = info->recv_fifo_size;
count = up->recv_fifo_size;
free_fifo++;
}
if (stat->sreg.isr0 & SAB82532_ISR0_TCD) {
count = readb(&info->regs->r.rbcl) & (info->recv_fifo_size - 1);
count = readb(&up->regs->r.rbcl) & (up->recv_fifo_size - 1);
free_fifo++;
}
/* Issue a FIFO read command in case we where idle. */
if (stat->sreg.isr0 & SAB82532_ISR0_TIME) {
sab82532_cec_wait(info);
writeb(SAB82532_CMDR_RFRD, &info->regs->w.cmdr);
sunsab_cec_wait(up);
writeb(SAB82532_CMDR_RFRD, &up->regs->w.cmdr);
return;
}
if (stat->sreg.isr0 & SAB82532_ISR0_RFO) {
#ifdef SERIAL_DEBUG_OVERFLOW
printk("sab82532: receive_chars: RFO");
#endif
if (stat->sreg.isr0 & SAB82532_ISR0_RFO)
free_fifo++;
}
/* Read the FIFO. */
for (i = 0; i < count; i++)
buf[i] = readb(&info->regs->r.rfifo[i]);
buf[i] = readb(&up->regs->r.rfifo[i]);
/* Issue Receive Message Complete command. */
if (free_fifo) {
sab82532_cec_wait(info);
writeb(SAB82532_CMDR_RMC, &info->regs->w.cmdr);
sunsab_cec_wait(up);
writeb(SAB82532_CMDR_RMC, &up->regs->w.cmdr);
}
if (!tty)
return;
for (i = 0; i < count; i++) {
unsigned char ch = buf[i];
for (i = 0; i < count; ) {
if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
#ifdef SERIAL_DEBUG_OVERFLOW
printk("sab82532: receive_chars: tty overrun\n");
#endif
info->icount.buf_overrun++;
break;
if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) {
tty->flip.tqueue.routine((void *)tty);
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
return; // if TTY_DONT_FLIP is set
}
tty->flip.count++;
*tty->flip.char_buf_ptr++ = buf[i++];
status = buf[i++];
info->icount.rx++;
*tty->flip.char_buf_ptr = ch;
*tty->flip.flag_buf_ptr = TTY_NORMAL;
up->port.icount.rx++;
#ifdef SERIAL_DEBUG_INTR
printk("DR%02x:%02x...", (unsigned char)*(tty->flip.char_buf_ptr - 1), status);
#endif
if (unlikely(stat->sreg.isr0 & (SAB82532_ISR0_PERR |
SAB82532_ISR0_FERR |
SAB82532_ISR0_RFO)) ||
unlikely(stat->sreg.isr1 & SAB82532_ISR1_BRK)) {
/*
* For statistics only
*/
if (stat->sreg.isr1 & SAB82532_ISR1_BRK) {
stat->sreg.isr0 &= ~(SAB82532_ISR0_PERR |
SAB82532_ISR0_FERR);
up->port.icount.brk++;
if (up->port.line == up->port.cons->index)
saw_console_brk = 1;
/*
* We do the SysRQ and SAK checking
* here because otherwise the break
* may get masked by ignore_status_mask
* or read_status_mask.
*/
if (uart_handle_break(&up->port))
continue;
} else if (stat->sreg.isr0 & SAB82532_ISR0_PERR)
up->port.icount.parity++;
else if (stat->sreg.isr0 & SAB82532_ISR0_FERR)
up->port.icount.frame++;
if (stat->sreg.isr0 & SAB82532_ISR0_RFO)
up->port.icount.overrun++;
if (status & SAB82532_RSTAT_PE) {
*tty->flip.flag_buf_ptr++ = TTY_PARITY;
info->icount.parity++;
} else if (status & SAB82532_RSTAT_FE) {
*tty->flip.flag_buf_ptr++ = TTY_FRAME;
info->icount.frame++;
/*
* Mask off conditions which should be ingored.
*/
stat->sreg.isr0 &= (up->port.read_status_mask & 0xff);
stat->sreg.isr1 &= ((up->port.read_status_mask >> 8) & 0xff);
if (stat->sreg.isr1 & SAB82532_ISR1_BRK) {
*tty->flip.flag_buf_ptr = TTY_BREAK;
} else if (stat->sreg.isr0 & SAB82532_ISR0_PERR)
*tty->flip.flag_buf_ptr = TTY_PARITY;
else if (stat->sreg.isr0 & SAB82532_ISR0_FERR)
*tty->flip.flag_buf_ptr = TTY_FRAME;
}
else
*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
if (uart_handle_sysrq_char(&up->port, ch, regs))
continue;
if ((stat->sreg.isr0 & (up->port.ignore_status_mask & 0xff)) == 0 &&
(stat->sreg.isr1 & ((up->port.ignore_status_mask >> 8) & 0xff)) == 0){
tty->flip.flag_buf_ptr++;
tty->flip.char_buf_ptr++;
tty->flip.count++;
}
if ((stat->sreg.isr0 & SAB82532_ISR0_RFO) &&
tty->flip.count < TTY_FLIPBUF_SIZE) {
/*
* Overrun is special, since it's reported
* immediately, and doesn't affect the current
* character.
*/
*tty->flip.flag_buf_ptr = TTY_OVERRUN;
tty->flip.flag_buf_ptr++;
tty->flip.char_buf_ptr++;
tty->flip.count++;
}
}
tty_flip_buffer_push(tty);
queue_task(&tty->flip.tqueue, &tq_timer);
if (saw_console_brk)
sun_do_break();
}
static void transmit_chars(struct sab82532 *info,
static void sunsab_stop_tx(struct uart_port *, unsigned int);
static void transmit_chars(struct uart_sunsab_port *up,
union sab82532_irq_status *stat)
{
struct circ_buf *xmit = &up->port.info->xmit;
int i;
if (stat->sreg.isr1 & SAB82532_ISR1_ALLS) {
info->interrupt_mask1 |= SAB82532_IMR1_ALLS;
writeb(info->interrupt_mask1, &info->regs->w.imr1);
set_bit(SAB82532_ALLS, &info->irqflags);
up->interrupt_mask1 |= SAB82532_IMR1_ALLS;
writeb(up->interrupt_mask1, &up->regs->w.imr1);
set_bit(SAB82532_ALLS, &up->irqflags);
}
if (!(stat->sreg.isr1 & SAB82532_ISR1_XPR))
return;
if (!(readb(&info->regs->r.star) & SAB82532_STAR_XFW)) {
#ifdef SERIAL_DEBUG_FIFO
printk("%s: XPR, but no XFW (?)\n", __FUNCTION__);
#endif
if (!(readb(&up->regs->r.star) & SAB82532_STAR_XFW))
return;
}
set_bit(SAB82532_XPR, &info->irqflags);
if (!info->tty) {
info->interrupt_mask1 |= SAB82532_IMR1_XPR;
writeb(info->interrupt_mask1, &info->regs->w.imr1);
return;
}
set_bit(SAB82532_XPR, &up->irqflags);
if ((info->xmit.head == info->xmit.tail) ||
info->tty->stopped || info->tty->hw_stopped) {
info->interrupt_mask1 |= SAB82532_IMR1_XPR;
writeb(info->interrupt_mask1, &info->regs->w.imr1);
if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
up->interrupt_mask1 |= SAB82532_IMR1_XPR;
writeb(up->interrupt_mask1, &up->regs->w.imr1);
return;
}
info->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS);
writeb(info->interrupt_mask1, &info->regs->w.imr1);
clear_bit(SAB82532_ALLS, &info->irqflags);
up->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS);
writeb(up->interrupt_mask1, &up->regs->w.imr1);
clear_bit(SAB82532_ALLS, &up->irqflags);
/* Stuff 32 bytes into Transmit FIFO. */
clear_bit(SAB82532_XPR, &info->irqflags);
for (i = 0; i < info->xmit_fifo_size; i++) {
writeb(info->xmit.buf[info->xmit.tail],
&info->regs->w.xfifo[i]);
info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1);
info->icount.tx++;
if (info->xmit.head == info->xmit.tail)
clear_bit(SAB82532_XPR, &up->irqflags);
for (i = 0; i < up->xmit_fifo_size; i++) {
writeb(xmit->buf[xmit->tail],
&up->regs->w.xfifo[i]);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
up->port.icount.tx++;
if (uart_circ_empty(xmit))
break;
}
/* Issue a Transmit Frame command. */
sab82532_cec_wait(info);
writeb(SAB82532_CMDR_XF, &info->regs->w.cmdr);
sunsab_cec_wait(up);
writeb(SAB82532_CMDR_XF, &up->regs->w.cmdr);
if (CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) < WAKEUP_CHARS)
sab82532_sched_event(info, RS_EVENT_WRITE_WAKEUP);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_event(&up->port, EVT_WRITE_WAKEUP);
#ifdef SERIAL_DEBUG_INTR
printk("THRE...");
#endif
if (uart_circ_empty(xmit))
sunsab_stop_tx(&up->port, 0);
}
static void check_status(struct sab82532 *info,
static void check_status(struct uart_sunsab_port *up,
union sab82532_irq_status *stat)
{
struct tty_struct *tty = info->tty;
int modem_change = 0;
if (stat->sreg.isr1 & SAB82532_ISR1_BRK) {
#ifdef CONFIG_SERIAL_CONSOLE
if (info->is_console) {
sun_do_break(info);
return;
}
#endif
if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
info->icount.buf_overrun++;
goto check_modem;
}
tty->flip.count++;
*tty->flip.flag_buf_ptr++ = TTY_PARITY;
*tty->flip.char_buf_ptr++ = 0;
info->icount.brk++;
}
if (!tty)
return;
if (stat->sreg.isr0 & SAB82532_ISR0_RFO) {
if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
info->icount.buf_overrun++;
goto check_modem;
}
tty->flip.count++;
*tty->flip.flag_buf_ptr++ = TTY_PARITY;
*tty->flip.char_buf_ptr++ = 0;
info->icount.overrun++;
}
check_modem:
if (stat->sreg.isr0 & SAB82532_ISR0_CDSC) {
info->dcd = (readb(&info->regs->r.vstr) & SAB82532_VSTR_CD) ? 0 : 1;
info->icount.dcd++;
modem_change++;
#ifdef SERIAL_DEBUG_MODEM
printk("DCD change: %d\n", info->icount.dcd);
#endif
}
if (stat->sreg.isr1 & SAB82532_ISR1_CSC) {
info->cts = readb(&info->regs->r.star) & SAB82532_STAR_CTS;
info->icount.cts++;
modem_change++;
#ifdef SERIAL_DEBUG_MODEM
printk("CTS change: %d, CTS %s\n", info->icount.cts, info->cts ? "on" : "off");
#endif
}
if ((readb(&info->regs->r.pvr) & info->pvr_dsr_bit) ^ info->dsr) {
info->dsr = (readb(&info->regs->r.pvr) & info->pvr_dsr_bit) ? 0 : 1;
info->icount.dsr++;
modem_change++;
#ifdef SERIAL_DEBUG_MODEM
printk("DSR change: %d\n", info->icount.dsr);
#endif
}
if (modem_change)
wake_up_interruptible(&info->delta_msr_wait);
if ((info->flags & ASYNC_CHECK_CD) &&
(stat->sreg.isr0 & SAB82532_ISR0_CDSC)) {
#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
printk("ttys%d CD now %s...", info->line,
(info->dcd) ? "on" : "off");
#endif
if (info->dcd)
wake_up_interruptible(&info->open_wait);
else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
(info->flags & ASYNC_CALLOUT_NOHUP))) {
#ifdef SERIAL_DEBUG_OPEN
printk("scheduling hangup...");
#endif
MOD_INC_USE_COUNT;
if (schedule_task(&info->tqueue_hangup) == 0)
MOD_DEC_USE_COUNT;
}
}
if (stat->sreg.isr0 & SAB82532_ISR0_CDSC)
uart_handle_dcd_change(&up->port,
!(readb(&up->regs->r.vstr) & SAB82532_VSTR_CD));
if (info->flags & ASYNC_CTS_FLOW) {
if (info->tty->hw_stopped) {
if (info->cts) {
if (stat->sreg.isr1 & SAB82532_ISR1_CSC)
uart_handle_cts_change(&up->port,
(readb(&up->regs->r.star) & SAB82532_STAR_CTS));
#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
printk("CTS tx start...");
#endif
info->tty->hw_stopped = 0;
sab82532_sched_event(info,
RS_EVENT_WRITE_WAKEUP);
info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
writeb(info->interrupt_mask1, &info->regs->w.imr1);
sab82532_start_tx(info);
if ((readb(&up->regs->r.pvr) & up->pvr_dsr_bit) ^ up->dsr) {
up->dsr = (readb(&up->regs->r.pvr) & up->pvr_dsr_bit) ? 0 : 1;
up->port.icount.dsr++;
}
} else {
if (!(info->cts)) {
#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
printk("CTS tx stop...");
#endif
info->tty->hw_stopped = 1;
}
}
}
wake_up_interruptible(&up->port.info->delta_msr_wait);
}
/*
* This is the serial driver's generic interrupt routine
*/
static void sab82532_interrupt(int irq, void *dev_id, struct pt_regs *regs)
static void sunsab_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct sab82532 *info = dev_id;
struct uart_sunsab_port *up = dev_id;
union sab82532_irq_status status;
unsigned long flags;
#ifdef SERIAL_DEBUG_INTR
printk("sab82532_interrupt(%d)...", irq);
#endif
spin_lock_irqsave(&up->port.lock, flags);
status.stat = 0;
if (readb(&info->regs->r.gis) & SAB82532_GIS_ISA0)
status.sreg.isr0 = readb(&info->regs->r.isr0);
if (readb(&info->regs->r.gis) & SAB82532_GIS_ISA1)
status.sreg.isr1 = readb(&info->regs->r.isr1);
#ifdef SERIAL_DEBUG_INTR
printk("%d<%02x.%02x>", info->line,
status.sreg.isr0, status.sreg.isr1);
#endif
if (!status.stat)
goto next;
if (readb(&up->regs->r.gis) & SAB82532_GIS_ISA0)
status.sreg.isr0 = readb(&up->regs->r.isr0);
if (readb(&up->regs->r.gis) & SAB82532_GIS_ISA1)
status.sreg.isr1 = readb(&up->regs->r.isr1);
if (status.stat) {
if (status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
SAB82532_ISR0_RFO | SAB82532_ISR0_RPF))
receive_chars(info, &status);
receive_chars(up, &status, regs);
if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) ||
(status.sreg.isr1 & (SAB82532_ISR1_BRK | SAB82532_ISR1_CSC)))
check_status(info, &status);
(status.sreg.isr1 & SAB82532_ISR1_CSC))
check_status(up, &status);
if (status.sreg.isr1 & (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR))
transmit_chars(info, &status);
transmit_chars(up, &status);
}
next:
info = info->next;
status.stat = 0;
if (readb(&info->regs->r.gis) & SAB82532_GIS_ISB0)
status.sreg.isr0 = readb(&info->regs->r.isr0);
if (readb(&info->regs->r.gis) & SAB82532_GIS_ISB1)
status.sreg.isr1 = readb(&info->regs->r.isr1);
#ifdef SERIAL_DEBUG_INTR
printk("%d<%02x.%02x>", info->line,
status.sreg.isr0, status.sreg.isr1);
#endif
spin_unlock(&up->port.lock);
up++;
spin_lock(&up->port.lock);
if (!status.stat)
goto done;
status.stat = 0;
if (readb(&up->regs->r.gis) & SAB82532_GIS_ISB0)
status.sreg.isr0 = readb(&up->regs->r.isr0);
if (readb(&up->regs->r.gis) & SAB82532_GIS_ISB1)
status.sreg.isr1 = readb(&up->regs->r.isr1);
if (status.stat) {
if (status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
SAB82532_ISR0_RFO | SAB82532_ISR0_RPF))
receive_chars(info, &status);
receive_chars(up, &status, regs);
if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) ||
(status.sreg.isr1 & (SAB82532_ISR1_BRK | SAB82532_ISR1_CSC)))
check_status(info, &status);
check_status(up, &status);
if (status.sreg.isr1 & (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR))
transmit_chars(info, &status);
transmit_chars(up, &status);
}
done:
;
#ifdef SERIAL_DEBUG_INTR
printk("end.\n");
#endif
spin_unlock_irqrestore(&up->port.lock, flags);
}
/*
* -------------------------------------------------------------------
* Here ends the serial interrupt routines.
* -------------------------------------------------------------------
*/
/* port->lock is not held. */
static unsigned int sunsab_tx_empty(struct uart_port *port)
{
struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
int ret;
/*
* This routine is used to handle the "bottom half" processing for the
* serial driver, known also the "software interrupt" processing.
* This processing is done at the kernel interrupt level, after the
* sab82532_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This
* is where time-consuming activities which can not be done in the
* interrupt driver proper are done; the interrupt driver schedules
* them using sab82532_sched_event(), and they get done here.
*/
static void do_serial_bh(void)
/* Do not need a lock for a state test like this. */
if (test_bit(SAB82532_ALLS, &up->irqflags))
ret = TIOCSER_TEMT;
else
ret = 0;
return ret;
}
/* port->lock held by caller. */
static void sunsab_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
if (mctrl & TIOCM_RTS) {
writeb(readb(&up->regs->rw.mode) & ~SAB82532_MODE_FRTS,
&up->regs->rw.mode);
writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_RTS,
&up->regs->rw.mode);
} else {
writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_FRTS,
&up->regs->rw.mode);
writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_RTS,
&up->regs->rw.mode);
}
if (mctrl & TIOCM_DTR) {
writeb(readb(&up->regs->rw.pvr) & ~(up->pvr_dtr_bit), &up->regs->rw.pvr);
} else {
writeb(readb(&up->regs->rw.pvr) | up->pvr_dtr_bit, &up->regs->rw.pvr);
}
}
/* port->lock is not held. */
static unsigned int sunsab_get_mctrl(struct uart_port *port)
{
struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
unsigned long flags;
unsigned char val;
unsigned int result;
result = 0;
spin_lock_irqsave(&up->port.lock, flags);
val = readb(&up->regs->r.pvr);
result |= (val & up->pvr_dsr_bit) ? 0 : TIOCM_DSR;
val = readb(&up->regs->r.vstr);
result |= (val & SAB82532_VSTR_CD) ? 0 : TIOCM_CAR;
val = readb(&up->regs->r.star);
result |= (val & SAB82532_STAR_CTS) ? TIOCM_CTS : 0;
spin_unlock_irqrestore(&up->port.lock, flags);
return result;
}
/* port->lock held by caller. */
static void sunsab_stop_tx(struct uart_port *port, unsigned int tty_stop)
{
run_task_queue(&tq_serial);
struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
up->interrupt_mask1 |= SAB82532_IMR1_XPR;
writeb(up->interrupt_mask1, &up->regs->w.imr1);
}
static void do_softint(void *private_)
/* port->lock held by caller. */
static void sunsab_start_tx(struct uart_port *port, unsigned int tty_start)
{
struct sab82532 *info = (struct sab82532 *)private_;
struct tty_struct *tty;
struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
struct circ_buf *xmit = &up->port.info->xmit;
int i;
tty = info->tty;
if (!tty)
if (!test_bit(SAB82532_XPR, &up->irqflags))
return;
if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
tty->ldisc.write_wakeup)
(tty->ldisc.write_wakeup)(tty);
wake_up_interruptible(&tty->write_wait);
up->interrupt_mask1 &= ~SAB82532_IMR1_XPR;
writeb(up->interrupt_mask1, &up->regs->w.imr1);
clear_bit(SAB82532_ALLS, &up->irqflags);
clear_bit(SAB82532_XPR, &up->irqflags);
for (i = 0; i < up->xmit_fifo_size; i++) {
writeb(xmit->buf[xmit->tail],
&up->regs->w.xfifo[i]);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
up->port.icount.tx++;
if (uart_circ_empty(xmit))
break;
}
/* Issue a Transmit Frame command. */
sunsab_cec_wait(up);
writeb(SAB82532_CMDR_XF, &up->regs->w.cmdr);
}
/*
* This routine is called from the scheduler tqueue when the interrupt
* routine has signalled that a hangup has occurred. The path of
* hangup processing is:
*
* serial interrupt routine -> (scheduler tqueue) ->
* do_serial_hangup() -> tty->hangup() -> sab82532_hangup()
*
*/
static void do_serial_hangup(void *private_)
/* port->lock is not held. */
static void sunsab_send_xchar(struct uart_port *port, char ch)
{
struct sab82532 *info = (struct sab82532 *) private_;
struct tty_struct *tty;
struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
unsigned long flags;
tty = info->tty;
if (tty)
tty_hangup(tty);
MOD_DEC_USE_COUNT;
spin_lock_irqsave(&up->port.lock, flags);
sunsab_tec_wait(up);
writeb(ch, &up->regs->w.tic);
spin_unlock_irqrestore(&up->port.lock, flags);
}
static void
sab82532_init_line(struct sab82532 *info)
/* port->lock held by caller. */
static void sunsab_stop_rx(struct uart_port *port)
{
unsigned char stat, tmp;
struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
/*
* Wait for any commands or immediate characters
*/
sab82532_cec_wait(info);
sab82532_tec_wait(info);
up->interrupt_mask0 |= SAB82532_ISR0_TCD;
writeb(up->interrupt_mask1, &up->regs->w.imr0);
}
/*
* Clear the FIFO buffers.
*/
writeb(SAB82532_CMDR_RRES, &info->regs->w.cmdr);
sab82532_cec_wait(info);
writeb(SAB82532_CMDR_XRES, &info->regs->w.cmdr);
/* port->lock held by caller. */
static void sunsab_enable_ms(struct uart_port *port)
{
/* For now we always receive these interrupts. */
}
/*
* Clear the interrupt registers.
*/
stat = readb(&info->regs->r.isr0);
stat = readb(&info->regs->r.isr1);
/* port->lock is not held. */
static void sunsab_break_ctl(struct uart_port *port, int break_state)
{
struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
unsigned long flags;
unsigned char val;
spin_lock_irqsave(&up->port.lock, flags);
val = readb(&up->regs->rw.dafo);
if (break_state)
val |= SAB82532_DAFO_XBRK;
else
val &= ~SAB82532_DAFO_XBRK;
writeb(val, &up->regs->rw.dafo);
spin_unlock_irqrestore(&up->port.lock, flags);
}
/* port->lock is not held. */
static int sunsab_startup(struct uart_port *port)
{
struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
unsigned long flags;
unsigned char tmp;
spin_lock_irqsave(&up->port.lock, flags);
/*
* Wait for any commands or immediate characters
*/
sunsab_cec_wait(up);
sunsab_tec_wait(up);
/*
* Clear the FIFO buffers.
*/
writeb(SAB82532_CMDR_RRES, &up->regs->w.cmdr);
sunsab_cec_wait(up);
writeb(SAB82532_CMDR_XRES, &up->regs->w.cmdr);
/*
* Clear the interrupt registers.
*/
(void) readb(&up->regs->r.isr0);
(void) readb(&up->regs->r.isr1);
/*
* Now, initialize the UART
*/
writeb(0, &info->regs->w.ccr0); /* power-down */
writeb(0, &up->regs->w.ccr0); /* power-down */
writeb(SAB82532_CCR0_MCE | SAB82532_CCR0_SC_NRZ |
SAB82532_CCR0_SM_ASYNC, &info->regs->w.ccr0);
writeb(SAB82532_CCR1_ODS | SAB82532_CCR1_BCR | 7, &info->regs->w.ccr1);
SAB82532_CCR0_SM_ASYNC, &up->regs->w.ccr0);
writeb(SAB82532_CCR1_ODS | SAB82532_CCR1_BCR | 7, &up->regs->w.ccr1);
writeb(SAB82532_CCR2_BDF | SAB82532_CCR2_SSEL |
SAB82532_CCR2_TOE, &info->regs->w.ccr2);
writeb(0, &info->regs->w.ccr3);
writeb(SAB82532_CCR4_MCK4 | SAB82532_CCR4_EBRG, &info->regs->w.ccr4);
SAB82532_CCR2_TOE, &up->regs->w.ccr2);
writeb(0, &up->regs->w.ccr3);
writeb(SAB82532_CCR4_MCK4 | SAB82532_CCR4_EBRG, &up->regs->w.ccr4);
writeb(SAB82532_MODE_RTS | SAB82532_MODE_FCTS |
SAB82532_MODE_RAC, &info->regs->w.mode);
writeb(SAB82532_RFC_DPS | SAB82532_RFC_RFDF, &info->regs->w.rfc);
switch (info->recv_fifo_size) {
SAB82532_MODE_RAC, &up->regs->w.mode);
writeb(SAB82532_RFC_DPS | SAB82532_RFC_RFDF, &up->regs->w.rfc);
switch (up->recv_fifo_size) {
case 1:
tmp = readb(&info->regs->w.rfc);
tmp = readb(&up->regs->w.rfc);
tmp |= SAB82532_RFC_RFTH_1;
writeb(tmp, &info->regs->w.rfc);
writeb(tmp, &up->regs->w.rfc);
break;
case 4:
tmp = readb(&info->regs->w.rfc);
tmp = readb(&up->regs->w.rfc);
tmp |= SAB82532_RFC_RFTH_4;
writeb(tmp, &info->regs->w.rfc);
writeb(tmp, &up->regs->w.rfc);
break;
case 16:
tmp = readb(&info->regs->w.rfc);
tmp = readb(&up->regs->w.rfc);
tmp |= SAB82532_RFC_RFTH_16;
writeb(tmp, &info->regs->w.rfc);
writeb(tmp, &up->regs->w.rfc);
break;
default:
info->recv_fifo_size = 32;
up->recv_fifo_size = 32;
/* fall through */
case 32:
tmp = readb(&info->regs->w.rfc);
tmp = readb(&up->regs->w.rfc);
tmp |= SAB82532_RFC_RFTH_32;
writeb(tmp, &info->regs->w.rfc);
writeb(tmp, &up->regs->w.rfc);
break;
}
tmp = readb(&info->regs->rw.ccr0);
tmp |= SAB82532_CCR0_PU; /* power-up */
writeb(tmp, &info->regs->rw.ccr0);
}
static int startup(struct sab82532 *info)
{
unsigned long flags;
unsigned long page;
int retval = 0;
page = get_free_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
save_flags(flags); cli();
if (info->flags & ASYNC_INITIALIZED) {
free_page(page);
goto errout;
}
if (!info->regs) {
if (info->tty)
set_bit(TTY_IO_ERROR, &info->tty->flags);
free_page(page);
retval = -ENODEV;
goto errout;
}
if (info->xmit.buf)
free_page(page);
else
info->xmit.buf = (unsigned char *)page;
#ifdef SERIAL_DEBUG_OPEN
printk("starting up serial port %d...", info->line);
#endif
/*
* Initialize the Hardware
*/
sab82532_init_line(info);
if (info->tty->termios->c_cflag & CBAUD) {
u8 tmp;
};
tmp = readb(&info->regs->rw.mode);
tmp &= ~(SAB82532_MODE_FRTS);
tmp |= SAB82532_MODE_RTS;
writeb(tmp, &info->regs->rw.mode);
tmp = readb(&info->regs->rw.pvr);
tmp &= ~(info->pvr_dtr_bit);
writeb(tmp, &info->regs->rw.pvr);
}
tmp = readb(&up->regs->rw.ccr0);
tmp |= SAB82532_CCR0_PU; /* power-up */
writeb(tmp, &up->regs->rw.ccr0);
/*
* Finally, enable interrupts
*/
info->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
SAB82532_IMR0_PLLA;
writeb(info->interrupt_mask0, &info->regs->w.imr0);
info->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS |
up->interrupt_mask0 = (SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
SAB82532_IMR0_PLLA);
writeb(up->interrupt_mask0, &up->regs->w.imr0);
up->interrupt_mask1 = (SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS |
SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN |
SAB82532_IMR1_CSC | SAB82532_IMR1_XON |
SAB82532_IMR1_XPR;
writeb(info->interrupt_mask1, &info->regs->w.imr1);
set_bit(SAB82532_ALLS, &info->irqflags);
if (info->tty)
clear_bit(TTY_IO_ERROR, &info->tty->flags);
info->xmit.head = info->xmit.tail = 0;
set_bit(SAB82532_XPR, &info->irqflags);
SAB82532_IMR1_XPR);
writeb(up->interrupt_mask1, &up->regs->w.imr1);
set_bit(SAB82532_ALLS, &up->irqflags);
set_bit(SAB82532_XPR, &up->irqflags);
/*
* and set the speed of the serial port
*/
change_speed(info);
spin_unlock_irqrestore(&up->port.lock, flags);
info->flags |= ASYNC_INITIALIZED;
restore_flags(flags);
return 0;
errout:
restore_flags(flags);
return retval;
}
/*
* This routine will shutdown a serial port; interrupts are disabled, and
* DTR is dropped if the hangup on close termio flag is on.
*/
static void shutdown(struct sab82532 *info)
/* port->lock is not held. */
static void sunsab_shutdown(struct uart_port *port)
{
struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
unsigned long flags;
u8 tmp;
if (!(info->flags & ASYNC_INITIALIZED))
return;
#ifdef SERIAL_DEBUG_OPEN
printk("Shutting down serial port %d...", info->line);
#endif
save_flags(flags); cli(); /* Disable interrupts */
/*
* clear delta_msr_wait queue to avoid mem leaks: we may free the irq
* here so the queue might never be waken up
*/
wake_up_interruptible(&info->delta_msr_wait);
if (info->xmit.buf) {
free_page((unsigned long)info->xmit.buf);
info->xmit.buf = 0;
}
unsigned char tmp;
if (info->is_console) {
info->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
SAB82532_IMR0_PLLA | SAB82532_IMR0_CDSC;
writeb(info->interrupt_mask0, &info->regs->w.imr0);
info->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS |
SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN |
SAB82532_IMR1_CSC | SAB82532_IMR1_XON |
SAB82532_IMR1_XPR;
writeb(info->interrupt_mask1, &info->regs->w.imr1);
if (info->tty)
set_bit(TTY_IO_ERROR, &info->tty->flags);
info->flags &= ~ASYNC_INITIALIZED;
restore_flags(flags);
return;
}
spin_lock_irqsave(&up->port.lock, flags);
/* Disable Interrupts */
info->interrupt_mask0 = 0xff;
writeb(info->interrupt_mask0, &info->regs->w.imr0);
info->interrupt_mask1 = 0xff;
writeb(info->interrupt_mask1, &info->regs->w.imr1);
if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
tmp = readb(&info->regs->r.mode);
tmp |= (SAB82532_MODE_FRTS | SAB82532_MODE_RTS);
writeb(tmp, &info->regs->rw.mode);
writeb(readb(&info->regs->rw.pvr) | info->pvr_dtr_bit,
&info->regs->rw.pvr);
}
up->interrupt_mask0 = 0xff;
writeb(up->interrupt_mask0, &up->regs->w.imr0);
up->interrupt_mask1 = 0xff;
writeb(up->interrupt_mask1, &up->regs->w.imr1);
/* Disable break condition */
tmp = readb(&info->regs->rw.dafo);
tmp &= ~(SAB82532_DAFO_XBRK);
writeb(tmp, &info->regs->rw.dafo);
tmp = readb(&up->regs->rw.dafo);
tmp &= ~SAB82532_DAFO_XBRK;
writeb(tmp, &up->regs->rw.dafo);
/* Disable Receiver */
tmp = readb(&info->regs->rw.mode);
tmp &= ~(SAB82532_MODE_RAC);
writeb(tmp, &info->regs->rw.mode);
tmp = readb(&up->regs->rw.mode);
tmp &= ~SAB82532_MODE_RAC;
writeb(tmp, &up->regs->rw.mode);
/* Power Down */
tmp = readb(&info->regs->rw.ccr0);
tmp &= ~(SAB82532_CCR0_PU);
writeb(tmp, &info->regs->rw.ccr0);
tmp = readb(&up->regs->rw.ccr0);
tmp &= ~SAB82532_CCR0_PU;
writeb(tmp, &up->regs->rw.ccr0);
if (info->tty)
set_bit(TTY_IO_ERROR, &info->tty->flags);
info->flags &= ~ASYNC_INITIALIZED;
restore_flags(flags);
spin_unlock_irqrestore(&up->port.lock, flags);
}
/*
* This routine is called to set the UART divisor registers to match
* the specified baud rate for a serial port.
* This is used to figure out the divisor speeds.
*
* The formula is: Baud = SAB_BASE_BAUD / ((N + 1) * (1 << M)),
*
* with 0 <= N < 64 and 0 <= M < 16
*/
static void change_speed(struct sab82532 *info)
static void calc_ebrg(int baud, int *n_ret, int *m_ret)
{
int n, m;
if (baud == 0) {
*n_ret = 0;
*m_ret = 0;
return;
}
/*
* We scale numbers by 10 so that we get better accuracy
* without having to use floating point. Here we increment m
* until n is within the valid range.
*/
n = (SAB_BASE_BAUD * 10) / baud;
m = 0;
while (n >= 640) {
n = n / 2;
m++;
}
n = (n+5) / 10;
/*
* We try very hard to avoid speeds with M == 0 since they may
* not work correctly for XTAL frequences above 10 MHz.
*/
if ((m == 0) && ((n & 1) == 0)) {
n = n / 2;
m++;
}
*n_ret = n - 1;
*m_ret = m;
}
/* Internal routine, port->lock is held and local interrupts are disabled. */
static void sunsab_convert_to_sab(struct uart_sunsab_port *up, unsigned int cflag,
unsigned int iflag, int baud)
{
unsigned long flags;
unsigned int ebrg;
tcflag_t cflag;
unsigned char dafo;
int bits, n, m;
if (!info->tty || !info->tty->termios)
return;
cflag = info->tty->termios->c_cflag;
/* Byte size and parity */
switch (cflag & CSIZE) {
case CS5: dafo = SAB82532_DAFO_CHL5; bits = 7; break;
......@@ -959,1658 +688,483 @@ static void change_speed(struct sab82532 *info)
}
if (cflag & PARODD) {
#ifdef CMSPAR
if (cflag & CMSPAR)
dafo |= SAB82532_DAFO_PAR_MARK;
else
#endif
dafo |= SAB82532_DAFO_PAR_ODD;
} else {
#ifdef CMSPAR
if (cflag & CMSPAR)
dafo |= SAB82532_DAFO_PAR_SPACE;
else
#endif
dafo |= SAB82532_DAFO_PAR_EVEN;
}
/* Determine EBRG values based on baud rate */
info->baud = tty_get_baud_rate(info->tty);
calc_ebrg(info->baud, &n, &m);
calc_ebrg(baud, &n, &m);
ebrg = n | (m << 6);
if (info->baud) {
info->timeout = (info->xmit_fifo_size * HZ * bits) / info->baud;
info->tec_timeout = (10 * 1000000) / info->baud;
info->cec_timeout = info->tec_timeout >> 2;
} else {
info->timeout = 0;
info->tec_timeout = SAB82532_MAX_TEC_TIMEOUT;
info->cec_timeout = SAB82532_MAX_CEC_TIMEOUT;
}
info->timeout += HZ / 50; /* Add .02 seconds of slop */
up->tec_timeout = (10 * 1000000) / baud;
up->cec_timeout = up->tec_timeout >> 2;
/* CTS flow control flags */
if (cflag & CRTSCTS)
info->flags |= ASYNC_CTS_FLOW;
else
info->flags &= ~(ASYNC_CTS_FLOW);
if (cflag & CLOCAL)
info->flags &= ~(ASYNC_CHECK_CD);
else
info->flags |= ASYNC_CHECK_CD;
if (info->tty)
info->tty->hw_stopped = 0;
/* We encode read_status_mask and ignore_status_mask like so:
*
* ---------------------
* | ... | ISR1 | ISR0 |
* ---------------------
* .. 15 8 7 0
*/
up->port.read_status_mask = (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
SAB82532_ISR0_RFO | SAB82532_ISR0_RPF |
SAB82532_ISR0_CDSC);
up->port.read_status_mask |= (SAB82532_ISR1_CSC |
SAB82532_ISR1_ALLS |
SAB82532_ISR1_XPR) << 8;
if (iflag & INPCK)
up->port.read_status_mask |= (SAB82532_ISR0_PERR |
SAB82532_ISR0_FERR);
if (iflag & (BRKINT | PARMRK))
up->port.read_status_mask |= (SAB82532_ISR1_BRK << 8);
/*
* Set up parity check flag
* XXX: not implemented, yet.
*/
#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
* Characteres to ignore
*/
up->port.ignore_status_mask = 0;
if (iflag & IGNPAR)
up->port.ignore_status_mask |= (SAB82532_ISR0_PERR |
SAB82532_ISR0_FERR);
if (iflag & IGNBRK) {
up->port.ignore_status_mask |= (SAB82532_ISR1_BRK << 8);
/*
* Characters to ignore
* XXX: not implemented, yet.
* If we're ignoring parity and break indicators,
* ignore overruns too (for real raw support).
*/
if (iflag & IGNPAR)
up->port.ignore_status_mask |= SAB82532_ISR0_RFO;
}
/*
* !!! ignore all characters if CREAD is not set
* XXX: not implemented, yet.
* ignore all characters if CREAD is not set
*/
if ((cflag & CREAD) == 0)
info->ignore_status_mask |= SAB82532_ISR0_RPF |
SAB82532_ISR0_TCD |
SAB82532_ISR0_TIME;
save_flags(flags); cli();
sab82532_cec_wait(info);
sab82532_tec_wait(info);
writeb(dafo, &info->regs->w.dafo);
writeb(ebrg & 0xff, &info->regs->w.bgr);
writeb(readb(&info->regs->rw.ccr2) & ~(0xc0), &info->regs->rw.ccr2);
writeb(readb(&info->regs->rw.ccr2) | ((ebrg >> 2) & 0xc0), &info->regs->rw.ccr2);
if (info->flags & ASYNC_CTS_FLOW) {
writeb(readb(&info->regs->rw.mode) & ~(SAB82532_MODE_RTS), &info->regs->rw.mode);
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_FRTS, &info->regs->rw.mode);
writeb(readb(&info->regs->rw.mode) & ~(SAB82532_MODE_FCTS), &info->regs->rw.mode);
info->interrupt_mask1 &= ~(SAB82532_IMR1_CSC);
writeb(info->interrupt_mask1, &info->regs->w.imr1);
up->port.ignore_status_mask |= (SAB82532_ISR0_RPF |
SAB82532_ISR0_TCD);
/* Now bang the new settings into the chip. */
sunsab_cec_wait(up);
sunsab_tec_wait(up);
writeb(dafo, &up->regs->w.dafo);
writeb(ebrg & 0xff, &up->regs->w.bgr);
writeb((readb(&up->regs->rw.ccr2) & ~0xc0) | ((ebrg >> 2) & 0xc0),
&up->regs->rw.ccr2);
if (cflag & CRTSCTS) {
writeb(readb(&up->regs->rw.mode) & ~SAB82532_MODE_RTS,
&up->regs->rw.mode);
writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_FRTS,
&up->regs->rw.mode);
writeb(readb(&up->regs->rw.mode) & ~SAB82532_MODE_FCTS,
&up->regs->rw.mode);
up->interrupt_mask1 &= ~SAB82532_IMR1_CSC;
writeb(up->interrupt_mask1, &up->regs->w.imr1);
} else {
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_RTS, &info->regs->rw.mode);
writeb(readb(&info->regs->rw.mode) & ~(SAB82532_MODE_FRTS), &info->regs->rw.mode);
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_FCTS, &info->regs->rw.mode);
info->interrupt_mask1 |= SAB82532_IMR1_CSC;
writeb(info->interrupt_mask1, &info->regs->w.imr1);
writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_RTS,
&up->regs->rw.mode);
writeb(readb(&up->regs->rw.mode) & ~SAB82532_MODE_FRTS,
&up->regs->rw.mode);
writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_FCTS,
&up->regs->rw.mode);
up->interrupt_mask1 |= SAB82532_IMR1_CSC;
writeb(up->interrupt_mask1, &up->regs->w.imr1);
}
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_RAC, &info->regs->rw.mode);
restore_flags(flags);
writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_RAC, &up->regs->rw.mode);
}
static void sab82532_put_char(struct tty_struct *tty, unsigned char ch)
/* port->lock is not held. */
static void sunsab_change_speed(struct uart_port *port, unsigned int cflag,
unsigned int iflag, unsigned int quot)
{
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
unsigned long flags;
int baud;
if (serial_paranoia_check(info, tty->device, "sab82532_put_char"))
return;
spin_lock_irqsave(&up->port.lock, flags);
if (!tty || !info->xmit.buf)
return;
/* Undo what generic UART core did. */
baud = (SAB_BASE_BAUD / (quot * 16));
save_flags(flags); cli();
if (!CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE)) {
restore_flags(flags);
return;
}
sunsab_convert_to_sab(up, cflag, iflag, baud);
info->xmit.buf[info->xmit.head] = ch;
info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1);
restore_flags(flags);
spin_unlock_irqrestore(&up->port.lock, flags);
}
static void sab82532_flush_chars(struct tty_struct *tty)
static const char *sunsab_type(struct uart_port *port)
{
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
unsigned long flags;
if (serial_paranoia_check(info, tty->device, "sab82532_flush_chars"))
return;
if ((info->xmit.head == info->xmit.tail) ||
tty->stopped || tty->hw_stopped || !info->xmit.buf)
return;
save_flags(flags); cli();
info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
writeb(info->interrupt_mask1, &info->regs->w.imr1);
sab82532_start_tx(info);
restore_flags(flags);
return "SunSAB";
}
static int sab82532_write(struct tty_struct * tty, int from_user,
const unsigned char *buf, int count)
static void sunsab_release_port(struct uart_port *port)
{
int c, ret = 0;
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
unsigned long flags;
}
if (serial_paranoia_check(info, tty->device, "sab82532_write"))
static int sunsab_request_port(struct uart_port *port)
{
return 0;
}
if (!tty || !info->xmit.buf || !tmp_buf)
return 0;
static void sunsab_config_port(struct uart_port *port, int flags)
{
}
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,
SERIAL_XMIT_SIZE);
if (count < c)
c = count;
if (c <= 0)
break;
static int sunsab_verify_port(struct uart_port *port, struct serial_struct *ser)
{
return -EINVAL;
}
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,
SERIAL_XMIT_SIZE);
if (c1 < c)
c = c1;
memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c);
info->xmit.head = (info->xmit.head + c) & (SERIAL_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,
SERIAL_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) & (SERIAL_XMIT_SIZE-1);
buf += c;
count -= c;
ret += c;
}
restore_flags(flags);
}
static struct uart_ops sunsab_pops = {
.tx_empty = sunsab_tx_empty,
.set_mctrl = sunsab_set_mctrl,
.get_mctrl = sunsab_get_mctrl,
.stop_tx = sunsab_stop_tx,
.start_tx = sunsab_start_tx,
.send_xchar = sunsab_send_xchar,
.stop_rx = sunsab_stop_rx,
.enable_ms = sunsab_enable_ms,
.break_ctl = sunsab_break_ctl,
.startup = sunsab_startup,
.shutdown = sunsab_shutdown,
.change_speed = sunsab_change_speed,
.type = sunsab_type,
.release_port = sunsab_release_port,
.request_port = sunsab_request_port,
.config_port = sunsab_config_port,
.verify_port = sunsab_verify_port,
};
if ((info->xmit.head != info->xmit.tail) &&
!tty->stopped && !tty->hw_stopped) {
info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
writeb(info->interrupt_mask1, &info->regs->w.imr1);
sab82532_start_tx(info);
}
static struct uart_driver sunsab_reg = {
.owner = THIS_MODULE,
.driver_name = "serial",
#ifdef CONFIG_DEVFS_FS
.dev_name = "tts/%d",
#else
.dev_name = "ttyS",
#endif
.major = TTY_MAJOR,
};
restore_flags(flags);
return ret;
}
static struct uart_sunsab_port *sunsab_ports;
static int num_channels;
static int sab82532_write_room(struct tty_struct *tty)
static __inline__ void sunsab_console_putchar(struct uart_sunsab_port *up, char c)
{
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
unsigned long flags;
if (serial_paranoia_check(info, tty->device, "sab82532_write_room"))
return 0;
spin_lock_irqsave(&up->port.lock, flags);
return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
sunsab_tec_wait(up);
writeb(c, &up->regs->w.tic);
spin_unlock_irqrestore(&up->port.lock, flags);
}
static int sab82532_chars_in_buffer(struct tty_struct *tty)
static void sunsab_console_write(struct console *con, const char *s, unsigned n)
{
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
struct uart_sunsab_port *up = &sunsab_ports[con->index];
int i;
if (serial_paranoia_check(info, tty->device, "sab82532_chars_in_buffer"))
return 0;
for (i = 0; i < n; i++) {
if (*s == '\n')
sunsab_console_putchar(up, '\r');
sunsab_console_putchar(up, *s++);
}
sunsab_tec_wait(up);
}
return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
static kdev_t sunsab_console_device(struct console *con)
{
return mk_kdev(sunsab_reg.major, sunsab_reg.minor + con->index);
}
static void sab82532_flush_buffer(struct tty_struct *tty)
static int sunsab_console_setup(struct console *con, char *options)
{
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
struct uart_sunsab_port *up = &sunsab_ports[con->index];
unsigned long flags;
int baud;
if (serial_paranoia_check(info, tty->device, "sab82532_flush_buffer"))
return;
printk("Console: ttyS%d (SAB82532)\n",
(sunsab_reg.minor - 64) + con->index);
save_flags(flags); cli();
info->xmit.head = info->xmit.tail = 0;
restore_flags(flags);
sunserial_console_termios(con);
wake_up_interruptible(&tty->write_wait);
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
tty->ldisc.write_wakeup)
(tty->ldisc.write_wakeup)(tty);
}
/* Firmware console speed is limited to 150-->38400 baud so
* this hackish cflag thing is OK.
*/
switch (con->cflag & CBAUD) {
case B150: baud = 150; break;
case B300: baud = 300; break;
case B600: baud = 600; break;
case B1200: baud = 1200; break;
case B2400: baud = 2400; break;
case B4800: baud = 4800; break;
default: case B9600: baud = 9600; break;
case B19200: baud = 19200; break;
case B38400: baud = 38400; break;
};
/*
* This function is used to send a high-priority XON/XOFF character to
* the device
/*
* Temporary fix.
*/
static void sab82532_send_xchar(struct tty_struct *tty, char ch)
{
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
unsigned long flags;
spin_lock_init(&up->port.lock);
if (serial_paranoia_check(info, tty->device, "sab82532_send_xchar"))
return;
/*
* Initialize the hardware
*/
sunsab_startup(&up->port);
save_flags(flags); cli();
sab82532_tec_wait(info);
writeb(ch, &info->regs->w.tic);
restore_flags(flags);
}
spin_lock_irqsave(&up->port.lock, flags);
/*
* ------------------------------------------------------------
* sab82532_throttle()
*
* This routine is called by the upper-layer tty layer to signal that
* incoming characters should be throttled.
* ------------------------------------------------------------
/*
* Finally, enable interrupts
*/
static void sab82532_throttle(struct tty_struct * tty)
{
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
#ifdef SERIAL_DEBUG_THROTTLE
char buf[64];
printk("throttle %s: %d....\n", _tty_name(tty, buf),
tty->ldisc.chars_in_buffer(tty));
#endif
up->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
SAB82532_IMR0_PLLA | SAB82532_IMR0_CDSC;
writeb(up->interrupt_mask0, &up->regs->w.imr0);
up->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS |
SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN |
SAB82532_IMR1_CSC | SAB82532_IMR1_XON |
SAB82532_IMR1_XPR;
writeb(up->interrupt_mask1, &up->regs->w.imr1);
if (serial_paranoia_check(info, tty->device, "sab82532_throttle"))
return;
sunsab_convert_to_sab(up, con->cflag, 0, baud);
if (I_IXOFF(tty))
sab82532_send_xchar(tty, STOP_CHAR(tty));
spin_unlock_irqrestore(&up->port.lock, flags);
if (tty->termios->c_cflag & CRTSCTS) {
u8 mode = readb(&info->regs->r.mode);
mode &= ~(SAB82532_MODE_FRTS | SAB82532_MODE_RTS);
writeb(mode, &info->regs->w.mode);
}
return 0;
}
static void sab82532_unthrottle(struct tty_struct * tty)
{
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
#ifdef SERIAL_DEBUG_THROTTLE
char buf[64];
static struct console sunsab_console = {
.name = "ttyS",
.write = sunsab_console_write,
.device = sunsab_console_device,
.setup = sunsab_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
};
printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
tty->ldisc.chars_in_buffer(tty));
#endif
static void __init sunsab_console_init(void)
{
int i;
if (serial_paranoia_check(info, tty->device, "sab82532_unthrottle"))
if (con_is_present())
return;
if (I_IXOFF(tty)) {
if (info->x_char)
info->x_char = 0;
else
sab82532_send_xchar(tty, START_CHAR(tty));
}
for (i = 0; i < num_channels; i++) {
int this_minor = sunsab_reg.minor + i;
if (tty->termios->c_cflag & CRTSCTS) {
u8 mode = readb(&info->regs->r.mode);
mode &= ~(SAB82532_MODE_RTS);
mode |= SAB82532_MODE_FRTS;
writeb(mode, &info->regs->w.mode);
if ((this_minor - 64) == (serial_console - 1))
break;
}
}
/*
* ------------------------------------------------------------
* sab82532_ioctl() and friends
* ------------------------------------------------------------
*/
if (i == num_channels)
return;
static int get_serial_info(struct sab82532 *info,
struct serial_struct *retinfo)
{
struct serial_struct tmp;
if (!retinfo)
return -EFAULT;
memset(&tmp, 0, sizeof(tmp));
tmp.type = info->type;
tmp.line = info->line;
tmp.port = (unsigned long)info->regs;
tmp.irq = info->irq;
tmp.flags = info->flags;
tmp.xmit_fifo_size = info->xmit_fifo_size;
tmp.baud_base = info->baud_base;
tmp.close_delay = info->close_delay;
tmp.closing_wait = info->closing_wait;
tmp.custom_divisor = info->custom_divisor;
tmp.hub6 = 0;
if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
return -EFAULT;
return 0;
sunsab_console.index = i;
register_console(&sunsab_console);
}
static int set_serial_info(struct sab82532 *info,
struct serial_struct *new_info)
static void __init for_each_sab_edev(void (*callback)(struct linux_ebus_device *, void *), void *arg)
{
return 0;
}
struct linux_ebus *ebus;
struct linux_ebus_device *edev = NULL;
for_each_ebus(ebus) {
for_each_ebusdev(edev, ebus) {
if (!strcmp(edev->prom_name, "se")) {
callback(edev, arg);
continue;
} else if (!strcmp(edev->prom_name, "serial")) {
char compat[32];
int clen;
/*
* get_lsr_info - get line status register info
*
* Purpose: Let user call ioctl() to get info when the UART physically
* is emptied. On bus types like RS485, the transmitter must
* release the bus after transmitting. This must be done when
* the transmit shift register is empty, not be done when the
* transmit holding register is empty. This functionality
* allows an RS485 driver to be written in user space.
/* On RIO this can be an SE, check it. We could
* just check ebus->is_rio, but this is more portable.
*/
static int get_lsr_info(struct sab82532 * info, unsigned int *value)
{
unsigned int result;
result = (!info->xmit.buf && test_bit(SAB82532_ALLS, &info->irqflags))
? TIOCSER_TEMT : 0;
return put_user(result, value);
}
static int get_modem_info(struct sab82532 * info, unsigned int *value)
{
unsigned int result;
result = ((readb(&info->regs->r.mode) & SAB82532_MODE_RTS) ?
((readb(&info->regs->r.mode) & SAB82532_MODE_FRTS) ? 0 : TIOCM_RTS)
: TIOCM_RTS)
| ((readb(&info->regs->r.pvr) & info->pvr_dtr_bit) ? 0 : TIOCM_DTR)
| ((readb(&info->regs->r.vstr) & SAB82532_VSTR_CD) ? 0 : TIOCM_CAR)
| ((readb(&info->regs->r.pvr) & info->pvr_dsr_bit) ? 0 : TIOCM_DSR)
| ((readb(&info->regs->r.star) & SAB82532_STAR_CTS) ? TIOCM_CTS : 0);
return put_user(result,value);
}
static int set_modem_info(struct sab82532 * info, unsigned int cmd,
unsigned int *value)
{
unsigned int arg;
if (get_user(arg, value))
return -EFAULT;
switch (cmd) {
case TIOCMBIS:
if (arg & TIOCM_RTS) {
writeb(readb(&info->regs->rw.mode) & ~(SAB82532_MODE_FRTS), &info->regs->rw.mode);
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_RTS, &info->regs->rw.mode);
}
if (arg & TIOCM_DTR) {
writeb(readb(&info->regs->rw.pvr) & ~(info->pvr_dtr_bit), &info->regs->rw.pvr);
}
break;
case TIOCMBIC:
if (arg & TIOCM_RTS) {
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_FRTS, &info->regs->rw.mode);
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_RTS, &info->regs->rw.mode);
clen = prom_getproperty(edev->prom_node, "compatible",
compat, sizeof(compat));
if (clen > 0) {
if (strncmp(compat, "sab82532", 8) == 0) {
callback(edev, arg);
continue;
}
if (arg & TIOCM_DTR) {
writeb(readb(&info->regs->rw.pvr) | info->pvr_dtr_bit, &info->regs->rw.pvr);
}
break;
case TIOCMSET:
if (arg & TIOCM_RTS) {
writeb(readb(&info->regs->rw.mode) & ~(SAB82532_MODE_FRTS), &info->regs->rw.mode);
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_RTS, &info->regs->rw.mode);
} else {
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_FRTS, &info->regs->rw.mode);
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_RTS, &info->regs->rw.mode);
}
if (arg & TIOCM_DTR) {
writeb(readb(&info->regs->rw.pvr) & ~(info->pvr_dtr_bit), &info->regs->rw.pvr);
} else {
writeb(readb(&info->regs->rw.pvr) | info->pvr_dtr_bit, &info->regs->rw.pvr);
}
break;
default:
return -EINVAL;
}
return 0;
}
/*
* This routine sends a break character out the serial port.
*/
static void sab82532_break(struct tty_struct *tty, int break_state)
static void __init sab_count_callback(struct linux_ebus_device *edev, void *arg)
{
struct sab82532 * info = (struct sab82532 *)tty->driver_data;
unsigned long flags;
int *count_p = arg;
if (serial_paranoia_check(info, tty->device, "sab82532_break"))
return;
if (!info->regs)
return;
#ifdef SERIAL_DEBUG_SEND_BREAK
printk("sab82532_break(%d) jiff=%lu...", break_state, jiffies);
#endif
save_flags(flags); cli();
if (break_state == -1)
writeb(readb(&info->regs->rw.dafo) | SAB82532_DAFO_XBRK, &info->regs->rw.dafo);
else
writeb(readb(&info->regs->rw.dafo) & ~(SAB82532_DAFO_XBRK), &info->regs->rw.dafo);
restore_flags(flags);
}
static int sab82532_ioctl(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg)
{
struct sab82532 * info = (struct sab82532 *)tty->driver_data;
struct async_icount cprev, cnow; /* kernel counter temps */
struct serial_icounter_struct *p_cuser; /* user space */
if (serial_paranoia_check(info, tty->device, "sab82532_ioctl"))
return -ENODEV;
if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
(cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) &&
(cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT) &&
(cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
if (tty->flags & (1 << TTY_IO_ERROR))
return -EIO;
}
switch (cmd) {
case TIOCGSOFTCAR:
return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg);
case TIOCSSOFTCAR:
if (get_user(arg, (unsigned int *) arg))
return -EFAULT;
tty->termios->c_cflag =
((tty->termios->c_cflag & ~CLOCAL) |
(arg ? CLOCAL : 0));
return 0;
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 TIOCSERGSTRUCT:
if (copy_to_user((struct sab82532 *) arg,
info, sizeof(struct sab82532)))
return -EFAULT;
return 0;
/*
* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
* - mask passed in arg for lines of interest
* (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
* Caller should use TIOCGICOUNT to see which one it was
*/
case TIOCMIWAIT:
cli();
/* note the counters on entry */
cprev = info->icount;
sti();
while (1) {
interruptible_sleep_on(&info->delta_msr_wait);
/* see if a signal did it */
if (signal_pending(current))
return -ERESTARTSYS;
cli();
cnow = info->icount; /* atomic copy */
sti();
if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
return -EIO; /* no change => error */
if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
return 0;
}
cprev = cnow;
}
/* NOTREACHED */
/*
* Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
* Return: write counters to the user passed counter struct
* NB: both 1->0 and 0->1 transitions are counted except for
* RI where only 0->1 is counted.
*/
case TIOCGICOUNT:
cli();
cnow = info->icount;
sti();
p_cuser = (struct serial_icounter_struct *) arg;
if (put_user(cnow.cts, &p_cuser->cts) ||
put_user(cnow.dsr, &p_cuser->dsr) ||
put_user(cnow.rng, &p_cuser->rng) ||
put_user(cnow.dcd, &p_cuser->dcd))
return -EFAULT;
return 0;
default:
return -ENOIOCTLCMD;
}
return 0;
}
static void sab82532_set_termios(struct tty_struct *tty,
struct termios *old_termios)
{
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
if ( (tty->termios->c_cflag == old_termios->c_cflag)
&& ( RELEVANT_IFLAG(tty->termios->c_iflag)
== RELEVANT_IFLAG(old_termios->c_iflag)))
return;
change_speed(info);
/* Handle transition to B0 status */
if ((old_termios->c_cflag & CBAUD) &&
!(tty->termios->c_cflag & CBAUD)) {
writeb(readb(&info->regs->w.mode) | SAB82532_MODE_FRTS, &info->regs->w.mode);
writeb(readb(&info->regs->w.mode) | SAB82532_MODE_RTS, &info->regs->w.mode);
writeb(readb(&info->regs->w.pvr) | info->pvr_dtr_bit, &info->regs->w.pvr);
}
/* Handle transition away from B0 status */
if (!(old_termios->c_cflag & CBAUD) &&
(tty->termios->c_cflag & CBAUD)) {
writeb(readb(&info->regs->w.pvr) & ~(info->pvr_dtr_bit), &info->regs->w.pvr);
if (tty->termios->c_cflag & CRTSCTS) {
writeb(readb(&info->regs->w.mode) & ~(SAB82532_MODE_RTS), &info->regs->w.mode);
writeb(readb(&info->regs->w.mode) | SAB82532_MODE_FRTS, &info->regs->w.mode);
} else if (test_bit(TTY_THROTTLED, &tty->flags)) {
writeb(readb(&info->regs->w.mode) & ~(SAB82532_MODE_FRTS | SAB82532_MODE_RTS), &info->regs->w.mode);
} else {
writeb(readb(&info->regs->w.mode) & ~(SAB82532_MODE_FRTS), &info->regs->w.mode);
writeb(readb(&info->regs->w.mode) | SAB82532_MODE_RTS, &info->regs->w.mode);
}
}
/* Handle turning off CRTSCTS */
if ((old_termios->c_cflag & CRTSCTS) &&
!(tty->termios->c_cflag & CRTSCTS)) {
tty->hw_stopped = 0;
sab82532_start(tty);
}
}
/*
* ------------------------------------------------------------
* sab82532_close()
*
* 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 sab82532_close(struct tty_struct *tty, struct file * filp)
{
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
unsigned long flags;
if (!info || serial_paranoia_check(info, tty->device, "sab82532_close"))
return;
save_flags(flags); cli();
if (tty_hung_up_p(filp)) {
MOD_DEC_USE_COUNT;
restore_flags(flags);
return;
}
#ifdef SERIAL_DEBUG_OPEN
printk("sab82532_close ttys%d, count = %d\n", info->line, info->count);
#endif
if ((tty->count == 1) && (info->count != 1)) {
/*
* Uh, oh. tty->count is 1, which means that the tty
* structure will be freed. info->count should always
* be one in these conditions. If it's greater than
* one, we've got real problems, since it means the
* serial port won't be shutdown.
*/
printk("sab82532_close: bad serial port count; tty->count is 1,"
" info->count is %d\n", info->count);
info->count = 1;
}
if (--info->count < 0) {
printk("sab82532_close: bad serial port count for ttys%d: %d\n",
info->line, info->count);
info->count = 0;
}
if (info->count) {
MOD_DEC_USE_COUNT;
restore_flags(flags);
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->normal_termios = *tty->termios;
if (info->flags & ASYNC_CALLOUT_ACTIVE)
info->callout_termios = *tty->termios;
/*
* Now we wait for the transmit buffer to clear; and we notify
* the line discipline to only process XON/XOFF characters.
*/
tty->closing = 1;
if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
tty_wait_until_sent(tty, info->closing_wait);
/*
* At this point we stop accepting input. To do this, we
* disable the receive line status interrupts, and turn off
* the receiver.
*/
info->interrupt_mask0 |= SAB82532_IMR0_TCD;
writeb(info->interrupt_mask0, &info->regs->w.imr0);
if (info->flags & ASYNC_INITIALIZED) {
/*
* Before we drop DTR, make sure the UART transmitter
* has completely drained; this is especially
* important if there is a transmit FIFO!
*/
sab82532_wait_until_sent(tty, info->timeout);
}
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 = 0;
if (info->blocked_open) {
if (info->close_delay) {
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;
restore_flags(flags);
}
/*
* sab82532_wait_until_sent() --- wait until the transmitter is empty
*/
static void sab82532_wait_until_sent(struct tty_struct *tty, int timeout)
{
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
unsigned long orig_jiffies, char_time;
if (serial_paranoia_check(info,tty->device,"sab82532_wait_until_sent"))
return;
/*
* Set the check interval to be 1/5 of the estimated time to
* send a single character, and make it at least 1. The check
* interval should also be less than the timeout.
*
* Note: we have to use pretty tight timings here to satisfy
* the NIST-PCTS.
*/
char_time = (info->timeout - HZ/50) / info->xmit_fifo_size;
char_time = char_time / 5;
if (char_time == 0)
char_time = 1;
if (timeout)
char_time = MIN(char_time, timeout);
#ifdef SERIAL_DEBUG_WAIT_UNTIL_SENT
printk("In sab82532_wait_until_sent(%d) check=%lu "
"xmit_cnt = %ld, alls = %d (jiff=%lu)...\n",
timeout, char_time,
CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE),
test_bit(SAB82532_ALLS, &info->irqflags), jiffies);
#endif
orig_jiffies = jiffies;
while ((info->xmit.head != info->xmit.tail) ||
!test_bit(SAB82532_ALLS, &info->irqflags)) {
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(char_time);
if (signal_pending(current))
break;
if (timeout && time_after(jiffies, orig_jiffies + timeout))
break;
}
#ifdef SERIAL_DEBUG_WAIT_UNTIL_SENT
printk("xmit_cnt = %ld, alls = %d (jiff=%lu)...done\n",
CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE),
test_bit(SAB82532_ALLS, &info->irqflags), jiffies);
#endif
}
/*
* sab82532_hangup() --- called by tty_hangup() when a hangup is signaled.
*/
static void sab82532_hangup(struct tty_struct *tty)
{
struct sab82532 * info = (struct sab82532 *)tty->driver_data;
if (serial_paranoia_check(info, tty->device, "sab82532_hangup"))
return;
#ifdef CONFIG_SERIAL_CONSOLE
if (info->is_console)
return;
#endif
sab82532_flush_buffer(tty);
shutdown(info);
info->event = 0;
info->count = 0;
info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
info->tty = 0;
wake_up_interruptible(&info->open_wait);
}
/*
* ------------------------------------------------------------
* sab82532_open() and friends
* ------------------------------------------------------------
*/
static int block_til_ready(struct tty_struct *tty, struct file * filp,
struct sab82532 *info)
{
DECLARE_WAITQUEUE(wait, current);
int retval;
int do_clocal = 0;
/*
* 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);
#ifdef SERIAL_DO_RESTART
if (info->flags & ASYNC_HUP_NOTIFY)
return -EAGAIN;
else
return -ERESTARTSYS;
#else
return -EAGAIN;
#endif
}
/*
* 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 (info->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, info->count is dropped by one, so that
* sab82532_close() knows when to free things. We restore it upon
* exit, either normal or abnormal.
*/
retval = 0;
add_wait_queue(&info->open_wait, &wait);
#ifdef SERIAL_DEBUG_OPEN
printk("block_til_ready before block: ttyS%d, count = %d\n",
info->line, info->count);
#endif
cli();
if (!tty_hung_up_p(filp))
info->count--;
sti();
info->blocked_open++;
while (1) {
cli();
if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
(tty->termios->c_cflag & CBAUD)) {
writeb(readb(&info->regs->rw.pvr) & ~(info->pvr_dtr_bit), &info->regs->rw.pvr);
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_FRTS, &info->regs->rw.mode);
writeb(readb(&info->regs->rw.mode) & ~(SAB82532_MODE_RTS), &info->regs->rw.mode);
}
sti();
set_current_state(TASK_INTERRUPTIBLE);
if (tty_hung_up_p(filp) ||
!(info->flags & ASYNC_INITIALIZED)) {
#ifdef SERIAL_DO_RESTART
if (info->flags & ASYNC_HUP_NOTIFY)
retval = -EAGAIN;
else
retval = -ERESTARTSYS;
#else
retval = -EAGAIN;
#endif
break;
}
if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
!(info->flags & ASYNC_CLOSING) &&
(do_clocal || !(readb(&info->regs->r.vstr) & SAB82532_VSTR_CD)))
break;
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
#ifdef SERIAL_DEBUG_OPEN
printk("block_til_ready blocking: ttyS%d, count = %d, flags = %x, clocal = %d, vstr = %02x\n",
info->line, info->count, info->flags, do_clocal, readb(&info->regs->r.vstr));
#endif
schedule();
}
current->state = TASK_RUNNING;
remove_wait_queue(&info->open_wait, &wait);
if (!tty_hung_up_p(filp))
info->count++;
info->blocked_open--;
#ifdef SERIAL_DEBUG_OPEN
printk("block_til_ready after blocking: ttys%d, count = %d\n",
info->line, info->count);
#endif
if (retval)
return retval;
info->flags |= ASYNC_NORMAL_ACTIVE;
return 0;
}
/*
* 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 sab82532_open(struct tty_struct *tty, struct file * filp)
{
struct sab82532 *info = sab82532_chain;
int retval, line;
unsigned long page;
#ifdef SERIAL_DEBUG_OPEN
printk("sab82532_open: count = %d\n", info->count);
#endif
line = minor(tty->device) - tty->driver.minor_start;
if ((line < 0) || (line >= NR_PORTS))
return -ENODEV;
while (info) {
if (info->line == line)
break;
info = info->next;
}
if (!info) {
printk("sab82532_open: can't find info for line %d\n",
line);
return -ENODEV;
}
if (serial_paranoia_check(info, tty->device, "sab82532_open"))
return -ENODEV;
#ifdef SERIAL_DEBUG_OPEN
printk("sab82532_open %s%d, count = %d\n", tty->driver.name, info->line,
info->count);
#endif
if (!tmp_buf) {
page = get_free_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
if (tmp_buf)
free_page(page);
else
tmp_buf = (unsigned char *) page;
}
info->count++;
tty->driver_data = info;
info->tty = tty;
/*
* 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);
#ifdef SERIAL_DO_RESTART
return ((info->flags & ASYNC_HUP_NOTIFY) ?
-EAGAIN : -ERESTARTSYS);
#else
return -EAGAIN;
#endif
}
/*
* Start up serial port
*/
retval = startup(info);
if (retval)
return retval;
MOD_INC_USE_COUNT;
retval = block_til_ready(tty, filp, info);
if (retval) {
#ifdef SERIAL_DEBUG_OPEN
printk("sab82532_open returning after block_til_ready with %d\n",
retval);
#endif
return retval;
}
if ((info->count == 1) &&
(info->flags & ASYNC_SPLIT_TERMIOS)) {
if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
*tty->termios = info->normal_termios;
else
*tty->termios = info->callout_termios;
change_speed(info);
}
#ifdef CONFIG_SERIAL_CONSOLE
if (sab82532_console.cflag && sab82532_console.index == line) {
tty->termios->c_cflag = sab82532_console.cflag;
sab82532_console.cflag = 0;
change_speed(info);
}
#endif
info->session = current->session;
info->pgrp = current->pgrp;
#ifdef SERIAL_DEBUG_OPEN
printk("sab82532_open ttys%d successful... count %d", info->line, info->count);
#endif
return 0;
(*count_p)++;
}
/*
* /proc fs routines....
*/
static __inline__ int
line_info(char *buf, struct sab82532 *info)
{
unsigned long flags;
char stat_buf[30];
int ret;
ret = sprintf(buf, "%u: uart:SAB82532 ", info->line);
switch (info->type) {
case 0:
ret += sprintf(buf+ret, "V1.0 ");
break;
case 1:
ret += sprintf(buf+ret, "V2.0 ");
break;
case 2:
ret += sprintf(buf+ret, "V3.2 ");
break;
default:
ret += sprintf(buf+ret, "V?.? ");
break;
}
ret += sprintf(buf+ret, "port:%lX irq:%s",
(unsigned long)info->regs, __irq_itoa(info->irq));
if (!info->regs) {
ret += sprintf(buf+ret, "\n");
return ret;
}
/*
* Figure out the current RS-232 lines
*/
stat_buf[0] = 0;
stat_buf[1] = 0;
save_flags(flags); cli();
if (readb(&info->regs->r.mode) & SAB82532_MODE_RTS) {
if (!(readb(&info->regs->r.mode) & SAB82532_MODE_FRTS))
strcat(stat_buf, "|RTS");
} else {
strcat(stat_buf, "|RTS");
}
if (readb(&info->regs->r.star) & SAB82532_STAR_CTS)
strcat(stat_buf, "|CTS");
if (!(readb(&info->regs->r.pvr) & info->pvr_dtr_bit))
strcat(stat_buf, "|DTR");
if (!(readb(&info->regs->r.pvr) & info->pvr_dsr_bit))
strcat(stat_buf, "|DSR");
if (!(readb(&info->regs->r.vstr) & SAB82532_VSTR_CD))
strcat(stat_buf, "|CD");
restore_flags(flags);
if (info->baud)
ret += sprintf(buf+ret, " baud:%u", info->baud);
ret += sprintf(buf+ret, " tx:%u rx:%u",
info->icount.tx, info->icount.rx);
if (info->icount.frame)
ret += sprintf(buf+ret, " fe:%u", info->icount.frame);
if (info->icount.parity)
ret += sprintf(buf+ret, " pe:%u", info->icount.parity);
if (info->icount.brk)
ret += sprintf(buf+ret, " brk:%u", info->icount.brk);
if (info->icount.overrun)
ret += sprintf(buf+ret, " oe:%u", info->icount.overrun);
/*
* Last thing is the RS-232 status lines.
*/
ret += sprintf(buf+ret, " %s\n", stat_buf + 1);
return ret;
}
int sab82532_read_proc(char *page, char **start, off_t off, int count,
int *eof, void *data)
{
struct sab82532 *info = sab82532_chain;
off_t begin = 0;
int len = 0;
len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version);
for (info = sab82532_chain; info && len < 4000; info = info->next) {
len += line_info(page + len, info);
if (len+begin > off+count)
goto done;
if (len+begin < off) {
begin += len;
len = 0;
}
}
*eof = 1;
done:
if (off >= len+begin)
return 0;
*start = page + (off-begin);
return ((count < begin+len-off) ? count : begin+len-off);
}
/*
* ---------------------------------------------------------------------
* sab82532_init() and friends
*
* sab82532_init() is called at boot-time to initialize the serial driver.
* ---------------------------------------------------------------------
*/
static int __init get_sab82532(unsigned long *memory_start)
static void __init sab_attach_callback(struct linux_ebus_device *edev, void *arg)
{
struct linux_ebus *ebus;
struct linux_ebus_device *edev = 0;
struct sab82532 *sab;
int *instance_p = arg;
struct uart_sunsab_port *up;
unsigned long regs, offset;
int i;
for_each_ebus(ebus) {
for_each_ebusdev(edev, ebus) {
if (!strcmp(edev->prom_name, "se"))
goto ebus_done;
if (!strcmp(edev->prom_name, "serial")) {
char compat[32];
int clen;
/* On RIO this can be an SE, check it. We could
* just check ebus->is_rio, but this is more portable.
*/
clen = prom_getproperty(edev->prom_node, "compatible",
compat, sizeof(compat));
if (clen > 0) {
if (strncmp(compat, "sab82532", 8) == 0) {
/* Yep. */
goto ebus_done;
}
}
}
}
}
ebus_done:
if (!edev)
return -ENODEV;
regs = edev->resource[0].start;
offset = sizeof(union sab82532_async_regs);
for (i = 0; i < 2; i++) {
if (memory_start) {
*memory_start = (*memory_start + 7) & ~(7);
sab = (struct sab82532 *)*memory_start;
*memory_start += sizeof(struct sab82532);
} else {
sab = (struct sab82532 *)kmalloc(sizeof(struct sab82532),
GFP_KERNEL);
if (!sab) {
printk("sab82532: can't alloc sab struct\n");
break;
}
}
memset(sab, 0, sizeof(struct sab82532));
sab->regs = ioremap(regs + offset, sizeof(union sab82532_async_regs));
sab->irq = edev->irqs[0];
sab->line = 1 - i;
sab->xmit_fifo_size = 32;
sab->recv_fifo_size = 32;
up = &sunsab_ports[(*instance_p * 2) + i];
writeb(SAB82532_IPC_IC_ACT_LOW, &sab->regs->w.ipc);
memset(up, 0, sizeof(*up));
up->regs = ioremap(regs + offset, sizeof(union sab82532_async_regs));
up->irq = edev->irqs[0];
up->sab_line = 1 - i;
up->xmit_fifo_size = 32;
up->recv_fifo_size = 32;
sab->next = sab82532_chain;
sab82532_chain = sab;
writeb(SAB82532_IPC_IC_ACT_LOW, &up->regs->w.ipc);
offset -= sizeof(union sab82532_async_regs);
}
return 0;
}
#ifndef MODULE
static void __init sab82532_kgdb_hook(int line)
static int __init probe_for_sabs(void)
{
prom_printf("sab82532: kgdb support is not implemented, yet\n");
prom_halt();
}
#endif
static inline void __init show_serial_version(void)
{
char *revision = "$Revision: 1.66 $";
char *version, *p;
version = strchr(revision, ' ');
strcpy(serial_version, ++version);
p = strchr(serial_version, ' ');
*p = '\0';
printk("SAB82532 serial driver version %s\n", serial_version);
}
extern int su_num_ports;
int this_sab = 0;
/*
* The serial driver boot-time initialization code!
*/
int __init sab82532_init(void)
{
struct sab82532 *info;
int i;
if (!sab82532_chain)
get_sab82532(0);
if (!sab82532_chain)
/* Find device instances. */
for_each_sab_edev(&sab_count_callback, &this_sab);
if (!this_sab)
return -ENODEV;
init_bh(SERIAL_BH, do_serial_bh);
show_serial_version();
/* Initialize the tty_driver structure */
memset(&serial_driver, 0, sizeof(struct tty_driver));
serial_driver.magic = TTY_DRIVER_MAGIC;
serial_driver.driver_name = "serial";
#ifdef CONFIG_DEVFS_FS
serial_driver.name = "tts/%d";
#else
serial_driver.name = "ttyS";
#endif
serial_driver.major = TTY_MAJOR;
serial_driver.minor_start = 64 + su_num_ports;
serial_driver.num = NR_PORTS;
serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
serial_driver.subtype = SERIAL_TYPE_NORMAL;
serial_driver.init_termios = tty_std_termios;
serial_driver.init_termios.c_cflag =
B9600 | CS8 | CREAD | HUPCL | CLOCAL;
serial_driver.flags = TTY_DRIVER_REAL_RAW;
serial_driver.refcount = &sab82532_refcount;
serial_driver.table = sab82532_table;
serial_driver.termios = sab82532_termios;
serial_driver.termios_locked = sab82532_termios_locked;
serial_driver.open = sab82532_open;
serial_driver.close = sab82532_close;
serial_driver.write = sab82532_write;
serial_driver.put_char = sab82532_put_char;
serial_driver.flush_chars = sab82532_flush_chars;
serial_driver.write_room = sab82532_write_room;
serial_driver.chars_in_buffer = sab82532_chars_in_buffer;
serial_driver.flush_buffer = sab82532_flush_buffer;
serial_driver.ioctl = sab82532_ioctl;
serial_driver.throttle = sab82532_throttle;
serial_driver.unthrottle = sab82532_unthrottle;
serial_driver.send_xchar = sab82532_send_xchar;
serial_driver.set_termios = sab82532_set_termios;
serial_driver.stop = sab82532_stop;
serial_driver.start = sab82532_start;
serial_driver.hangup = sab82532_hangup;
serial_driver.break_ctl = sab82532_break;
serial_driver.wait_until_sent = sab82532_wait_until_sent;
serial_driver.read_proc = sab82532_read_proc;
/*
* The callout device is just like normal device except for
* major number and the subtype code.
*/
callout_driver = serial_driver;
#ifdef CONFIG_DEVFS_FS
callout_driver.name = "cua/%d";
#else
callout_driver.name = "cua";
#endif
callout_driver.major = TTYAUX_MAJOR;
callout_driver.subtype = SERIAL_TYPE_CALLOUT;
callout_driver.read_proc = 0;
callout_driver.proc_entry = 0;
if (tty_register_driver(&serial_driver))
panic("Couldn't register serial driver\n");
if (tty_register_driver(&callout_driver))
panic("Couldn't register callout driver\n");
for (info = sab82532_chain, i = 0; info; info = info->next, i++) {
info->magic = SERIAL_MAGIC;
info->type = readb(&info->regs->r.vstr) & 0x0f;
writeb(~((1 << 1) | (1 << 2) | (1 << 4)), &info->regs->w.pcr);
writeb(0xff, &info->regs->w.pim);
if (info->line == 0) {
info->pvr_dsr_bit = (1 << 0);
info->pvr_dtr_bit = (1 << 1);
} else {
info->pvr_dsr_bit = (1 << 3);
info->pvr_dtr_bit = (1 << 2);
}
writeb((1 << 1) | (1 << 2) | (1 << 4), &info->regs->w.pvr);
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_FRTS, &info->regs->rw.mode);
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_RTS, &info->regs->rw.mode);
info->custom_divisor = 16;
info->close_delay = 5*HZ/10;
info->closing_wait = 30*HZ;
info->tec_timeout = SAB82532_MAX_TEC_TIMEOUT;
info->cec_timeout = SAB82532_MAX_CEC_TIMEOUT;
info->x_char = 0;
info->event = 0;
info->blocked_open = 0;
info->tqueue.routine = do_softint;
info->tqueue.data = info;
info->tqueue_hangup.routine = do_serial_hangup;
info->tqueue_hangup.data = info;
info->callout_termios = callout_driver.init_termios;
info->normal_termios = serial_driver.init_termios;
init_waitqueue_head(&info->open_wait);
init_waitqueue_head(&info->close_wait);
init_waitqueue_head(&info->delta_msr_wait);
info->icount.cts = info->icount.dsr =
info->icount.rng = info->icount.dcd = 0;
info->icount.rx = info->icount.tx = 0;
info->icount.frame = info->icount.parity = 0;
info->icount.overrun = info->icount.brk = 0;
if (!(info->line & 0x01)) {
if (request_irq(info->irq, sab82532_interrupt, SA_SHIRQ,
"serial(sab82532)", info)) {
printk("sab82532: can't get IRQ %x\n",
info->irq);
panic("sab82532 initialization failed");
}
}
/* Allocate tables. */
sunsab_ports = kmalloc(sizeof(struct uart_sunsab_port) * this_sab * 2,
GFP_KERNEL);
if (!sunsab_ports)
return -ENOMEM;
printk(KERN_INFO
"ttyS%02d at 0x%lx (irq = %s) is a SAB82532 %s\n",
info->line + su_num_ports, (unsigned long)info->regs,
__irq_itoa(info->irq), sab82532_version[info->type]);
}
num_channels = this_sab * 2;
#ifdef SERIAL_LOG_DEVICE
dprint_init(SERIAL_LOG_DEVICE);
#endif
this_sab = 0;
for_each_sab_edev(&sab_attach_callback, &this_sab);
return 0;
}
int __init sab82532_probe(void)
static void __init sunsab_init_hw(void)
{
int node, enode, snode;
char model[32];
int len;
node = prom_getchild(prom_root_node);
node = prom_searchsiblings(node, "pci");
/*
* Check for SUNW,sabre on Ultra 5/10/AXi.
*/
len = prom_getproperty(node, "model", model, sizeof(model));
if ((len > 0) && !strncmp(model, "SUNW,sabre", len)) {
node = prom_getchild(node);
node = prom_searchsiblings(node, "pci");
}
int i;
/*
* For each PCI bus...
*/
while (node) {
enode = prom_getchild(node);
enode = prom_searchsiblings(enode, "ebus");
for (i = 0; i < num_channels; i++) {
struct uart_sunsab_port *up = &sunsab_ports[i];
/*
* For each EBus on this PCI...
*/
while (enode) {
int child;
up->port.line = i;
up->port.ops = &sunsab_pops;
up->port.type = PORT_SUNSAB;
up->port.uartclk = SAB_BASE_BAUD;
child = prom_getchild(enode);
snode = prom_searchsiblings(child, "se");
if (snode)
goto found;
up->type = readb(&up->regs->r.vstr) & 0x0f;
writeb(~((1 << 1) | (1 << 2) | (1 << 4)), &up->regs->w.pcr);
writeb(0xff, &up->regs->w.pim);
if (up->sab_line == 0) {
up->pvr_dsr_bit = (1 << 0);
up->pvr_dtr_bit = (1 << 1);
} else {
up->pvr_dsr_bit = (1 << 3);
up->pvr_dtr_bit = (1 << 2);
}
writeb((1 << 1) | (1 << 2) | (1 << 4), &up->regs->w.pvr);
writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_FRTS,
&up->regs->rw.mode);
writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_RTS,
&up->regs->rw.mode);
snode = prom_searchsiblings(child, "serial");
if (snode) {
char compat[32];
int clen;
up->tec_timeout = SAB82532_MAX_TEC_TIMEOUT;
up->cec_timeout = SAB82532_MAX_CEC_TIMEOUT;
clen = prom_getproperty(snode, "compatible",
compat, sizeof(compat));
if (clen > 0) {
if (strncmp(compat, "sab82532", 8) == 0)
goto found;
if (!(up->sab_line & 0x01)) {
if (request_irq(up->irq, sunsab_interrupt, SA_SHIRQ,
"serial(sab82532)", up)) {
printk("sunsab%d: can't get IRQ %x\n",
i, up->irq);
continue;
}
}
enode = prom_getsibling(enode);
enode = prom_searchsiblings(enode, "ebus");
}
node = prom_getsibling(node);
node = prom_searchsiblings(node, "pci");
printk(KERN_INFO
"sunsab%d at 0x%lx (irq = %s) is a SAB82532 %s\n",
i, (unsigned long)up->regs,
__irq_itoa(up->irq), sab82532_version[up->type]);
}
return -ENODEV;
found:
#ifdef CONFIG_SERIAL_CONSOLE
sunserial_setinitfunc(sab82532_console_init);
#endif
#ifndef MODULE
sunserial_setinitfunc(sab82532_init);
rs_ops.rs_kgdb_hook = sab82532_kgdb_hook;
#endif
return 0;
}
#ifdef MODULE
MODULE_LICENSE("GPL");
int init_module(void)
static int __init sunsab_init(void)
{
if (get_sab82532(0))
return -ENODEV;
int ret = probe_for_sabs();
int i;
return sab82532_init();
}
if (ret < 0)
return ret;
void cleanup_module(void)
{
struct sab82532 *sab;
unsigned long flags;
int e1, e2;
/* printk("Unloading %s: version %s\n", serial_name, serial_version); */
save_flags(flags);
cli();
remove_bh(SERIAL_BH);
if ((e1 = tty_unregister_driver(&serial_driver)))
printk("SERIAL: failed to unregister serial driver (%d)\n",
e1);
if ((e2 = tty_unregister_driver(&callout_driver)))
printk("SERIAL: failed to unregister callout driver (%d)\n",
e2);
restore_flags(flags);
if (tmp_buf) {
free_page((unsigned long) tmp_buf);
tmp_buf = NULL;
}
for (sab = sab82532_chain; sab; sab = sab->next) {
if (!(sab->line & 0x01))
free_irq(sab->irq, sab);
iounmap(sab->regs);
}
}
#endif /* MODULE */
sunsab_init_hw();
#ifdef CONFIG_SERIAL_CONSOLE
static __inline__ void
sab82532_console_putchar(struct sab82532 *info, char c)
{
unsigned long flags;
sunsab_reg.minor = sunserial_current_minor;
sunserial_current_minor += num_channels;
save_flags(flags); cli();
sab82532_tec_wait(info);
writeb(c, &info->regs->w.tic);
restore_flags(flags);
}
sunsab_reg.nr = num_channels;
sunsab_reg.cons = &sunsab_console;
static void
sab82532_console_write(struct console *con, const char *s, unsigned n)
{
struct sab82532 *info;
ret = uart_register_driver(&sunsab_reg);
if (ret < 0) {
int i;
info = sab82532_chain;
for (i = con->index; i; i--) {
info = info->next;
if (!info)
return;
}
for (i = 0; i < num_channels; i++) {
struct uart_sunsab_port *up = &sunsab_ports[i];
for (i = 0; i < n; i++) {
if (*s == '\n')
sab82532_console_putchar(info, '\r');
sab82532_console_putchar(info, *s++);
if (!(up->sab_line & 0x01))
free_irq(up->irq, up);
iounmap(up->regs);
}
sab82532_tec_wait(info);
}
static kdev_t
sab82532_console_device(struct console *con)
{
return mk_kdev(TTY_MAJOR, 64 + con->index);
}
static int
sab82532_console_setup(struct console *con, char *options)
{
static struct tty_struct c_tty;
static struct termios c_termios;
struct sab82532 *info;
tcflag_t cflag;
int i;
kfree(sunsab_ports);
sunsab_ports = NULL;
info = sab82532_chain;
for (i = con->index; i; i--) {
info = info->next;
if (!info)
return -ENODEV;
return ret;
}
info->is_console = 1;
/*
* Initialize the hardware
*/
sab82532_init_line(info);
/*
* Finally, enable interrupts
*/
info->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
SAB82532_IMR0_PLLA | SAB82532_IMR0_CDSC;
writeb(info->interrupt_mask0, &info->regs->w.imr0);
info->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS |
SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN |
SAB82532_IMR1_CSC | SAB82532_IMR1_XON |
SAB82532_IMR1_XPR;
writeb(info->interrupt_mask1, &info->regs->w.imr1);
printk("Console: ttyS%d (SAB82532)\n", info->line);
sunserial_console_termios(con);
cflag = con->cflag;
for (i = 0; i < num_channels; i++) {
struct uart_sunsab_port *up = &sunsab_ports[i];
/*
* Fake up the tty and tty->termios structures so we can use
* change_speed (and eliminate a lot of duplicate code).
*/
if (!info->tty) {
memset(&c_tty, 0, sizeof(c_tty));
info->tty = &c_tty;
}
if (!info->tty->termios) {
memset(&c_termios, 0, sizeof(c_termios));
info->tty->termios = &c_termios;
uart_add_one_port(&sunsab_reg, &up->port);
}
info->tty->termios->c_cflag = con->cflag;
change_speed(info);
/* Now take out the pointers to static structures if necessary */
if (info->tty->termios == &c_termios)
info->tty->termios = 0;
if (info->tty == &c_tty)
info->tty = 0;
sunsab_console_init();
return 0;
}
static struct console sab82532_console = {
.name = "ttyS",
.write = sab82532_console_write,
.device = sab82532_console_device,
.setup = sab82532_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
};
int __init sab82532_console_init(void)
static void __exit sunsab_exit(void)
{
extern int su_console_registered;
int i;
if (con_is_present() || su_console_registered)
return 0;
for (i = 0; i < num_channels; i++) {
struct uart_sunsab_port *up = &sunsab_ports[i];
if (!sab82532_chain) {
prom_printf("sab82532_console_setup: can't get SAB82532 chain");
prom_halt();
}
uart_remove_one_port(&sunsab_reg, &up->port);
sab82532_console.index = serial_console - 1;
register_console(&sab82532_console);
return 0;
}
#ifdef SERIAL_LOG_DEVICE
if (!(up->sab_line & 0x01))
free_irq(up->irq, up);
iounmap(up->regs);
}
static int serial_log_device = 0;
uart_unregister_driver(&sunsab_reg);
static void
dprint_init(int tty)
{
serial_console = tty + 1;
sab82532_console.index = tty;
sab82532_console_setup(&sab82532_console, "");
serial_console = 0;
serial_log_device = tty + 1;
kfree(sunsab_ports);
sunsab_ports = NULL;
}
int
dprintf(const char *fmt, ...)
{
static char buffer[4096];
va_list args;
int i;
if (!serial_log_device)
return 0;
module_init(sunsab_init);
module_exit(sunsab_exit);
va_start(args, fmt);
i = vsprintf(buffer, fmt, args);
va_end(args);
sab82532_console.write(&sab82532_console, buffer, i);
return i;
}
#endif /* SERIAL_LOG_DEVICE */
#endif /* CONFIG_SERIAL_CONSOLE */
MODULE_AUTHOR("Eddie C. Dost and David S. Miller");
MODULE_DESCRIPTION("Sun SAB82532 serial port driver");
MODULE_LICENSE("GPL");
/* $Id: sab82532.h,v 1.7 2001/05/23 23:09:10 ecd Exp $
* sab82532.h: Register Definitions for the Siemens SAB82532 DUSCC
/* sunsab.h: Register Definitions for the Siemens SAB82532 DUSCC
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
*/
#ifndef _SPARC64_SAB82532_H
#define _SPARC64_SAB82532_H
#include <linux/types.h>
#include <linux/serial.h>
#include <linux/circ_buf.h>
#ifndef _SUNSAB_H
#define _SUNSAB_H
struct sab82532_async_rd_regs {
u8 rfifo[0x20]; /* Receive FIFO */
......@@ -120,8 +115,6 @@ union sab82532_async_regs {
__volatile__ struct sab82532_async_rw_regs rw;
};
#define NR_PORTS 2
union sab82532_irq_status {
unsigned short stat;
struct {
......@@ -130,62 +123,10 @@ union sab82532_irq_status {
} sreg;
};
struct sab82532 {
int magic;
int baud_base;
union sab82532_async_regs *regs;
int irq;
int flags; /* defined in tty.h */
int type; /* SAB82532 version */
struct tty_struct *tty;
int read_status_mask;
int ignore_status_mask;
int timeout;
int xmit_fifo_size;
int recv_fifo_size;
int custom_divisor;
int baud;
unsigned int cec_timeout;
unsigned int tec_timeout;
int x_char;
int close_delay;
unsigned short closing_wait;
unsigned short closing_wait2;
unsigned long irqflags;
int is_console;
unsigned char interrupt_mask0;
unsigned char interrupt_mask1;
unsigned char pvr_dtr_bit;
unsigned char pvr_dsr_bit;
unsigned char dcd;
unsigned char cts;
unsigned char dsr;
unsigned long event;
unsigned long last_active;
int line;
int count;
int blocked_open;
long session;
long pgrp;
struct circ_buf xmit;
struct tq_struct tqueue;
struct tq_struct tqueue_hangup;
struct async_icount icount;
struct termios normal_termios;
struct termios callout_termios;
wait_queue_head_t open_wait;
wait_queue_head_t close_wait;
wait_queue_head_t delta_msr_wait;
struct sab82532 *next;
struct sab82532 *prev;
};
/* irqflags bits */
#define SAB82532_ALLS 0x00000001
#define SAB82532_XPR 0x00000002
/* RFIFO Status Byte */
#define SAB82532_RSTAT_PE 0x80
#define SAB82532_RSTAT_FE 0x40
......@@ -377,4 +318,4 @@ struct sab82532 {
#define SAB82532_CCR4_ICD 0x10
#endif /* !(_SPARC64_SAB82532_H) */
#endif /* !(_SUNSAB_H) */
......@@ -1362,7 +1362,7 @@ static void sunsu_console_write(struct console *co, const char *s,
static kdev_t sunsu_console_device(struct console *co)
{
return mk_kdev(TTY_MAJOR, 64 + co->index);
return mk_kdev(sunsu_reg.major, sunsu_reg.minor + co->index);
}
/*
......@@ -1379,6 +1379,9 @@ static int __init sunsu_console_setup(struct console *co, char *options)
int parity = 'n';
int flow = 'n';
printk("Console: ttyS%d (SU)\n",
(sunsu_reg.minor - 64) + co->index);
/*
* Check whether an invalid uart number has been specified, and
* if so, search for the first available port that does have
......@@ -1414,15 +1417,23 @@ static struct console sunsu_cons = {
static int __init sunsu_serial_console_init(void)
{
int index;
int i;
if (con_is_present())
return 0;
index = serial_console - 1;
if (sunsu_ports[index].port_node == 0)
for (i = 0; i < UART_NR; i++) {
int this_minor = sunsu_reg.minor + i;
if ((this_minor - 64) == (serial_console - 1))
break;
}
if (i == UART_NR)
return 0;
sunsu_cons.index = index;
if (sunsu_ports[i].port_node == 0)
return 0;
sunsu_cons.index = i;
register_console(&sunsu_cons);
return 0;
}
......
......@@ -1013,7 +1013,6 @@ static struct uart_driver sunzilog_reg = {
.dev_name = "ttyS",
#endif
.major = TTY_MAJOR,
.minor = 64,
};
static void * __init alloc_one_table(unsigned long size)
......@@ -1386,7 +1385,7 @@ sunzilog_console_write(struct console *con, const char *s, unsigned int count)
static kdev_t sunzilog_console_device(struct console *con)
{
return mk_kdev(TTY_MAJOR, 64 + con->index);
return mk_kdev(sunzilog_reg.major, sunzilog_reg.minor + con->index);
}
static int __init sunzilog_console_setup(struct console *con, char *options)
......@@ -1395,7 +1394,8 @@ static int __init sunzilog_console_setup(struct console *con, char *options)
unsigned long flags;
int baud, brg;
printk("Console: ttyS%d (Zilog8530)\n", con->index / 2);
printk("Console: ttyS%d (Zilog8530)\n",
(sunzilog_reg.minor - 64) + con->index);
/* Get firmware console settings. */
sunserial_console_termios(con);
......@@ -1447,10 +1447,21 @@ static struct console sunzilog_console = {
static int __init sunzilog_console_init(void)
{
int i;
if (con_is_present())
return 0;
sunzilog_console.index = serial_console - 1;
for (i = 0; i < NUM_CHANNELS; i++) {
int this_minor = sunzilog_reg.minor + i;
if ((this_minor - 64) == (serial_console - 1))
break;
}
if (i == NUM_CHANNELS)
return 0;
sunzilog_console.index = i;
register_console(&sunzilog_console);
return 0;
}
......@@ -1480,6 +1491,7 @@ static void __init sunzilog_prepare(void)
up[(chip * 2) + 0].port.uartclk = ZS_CLOCK;
up[(chip * 2) + 0].port.fifosize = 1;
up[(chip * 2) + 0].port.ops = &sunzilog_pops;
up[(chip * 2) + 0].port.type = PORT_SUNZILOG;
up[(chip * 2) + 0].port.flags = 0;
up[(chip * 2) + 0].port.line = (chip * 2) + 0;
up[(chip * 2) + 0].flags |= SUNZILOG_FLAG_IS_CHANNEL_A;
......@@ -1490,6 +1502,7 @@ static void __init sunzilog_prepare(void)
up[(chip * 2) + 1].port.uartclk = ZS_CLOCK;
up[(chip * 2) + 1].port.fifosize = 1;
up[(chip * 2) + 1].port.ops = &sunzilog_pops;
up[(chip * 2) + 1].port.type = PORT_SUNZILOG;
up[(chip * 2) + 1].port.flags = 0;
up[(chip * 2) + 1].port.line = (chip * 2) + 1;
up[(chip * 2) + 1].flags |= 0;
......@@ -1607,12 +1620,10 @@ static int __init sunzilog_ports_init(void)
* in the system.
*/
sunzilog_reg.nr = NUM_CHANNELS;
#ifdef CONFIG_SERIAL_CONSOLE
sunzilog_reg.cons = &sunzilog_console;
#else
sunzilog_reg.cons = NULL;
#endif
sunzilog_reg.minor = sunserial_current_minor;
sunserial_current_minor += NUM_CHANNELS;
ret = uart_register_driver(&sunzilog_reg);
if (ret == 0) {
......
......@@ -78,9 +78,13 @@
#ifdef __KERNEL__
#include <linux/spinlock.h>
#include <asm/system.h>
#include <asm/io.h>
extern spinlock_t ns87303_lock;
static __inline__ int ns87303_modify(unsigned long port, unsigned int index,
unsigned char clr, unsigned char set)
{
......@@ -96,14 +100,16 @@ static __inline__ int ns87303_modify(unsigned long port, unsigned int index,
if (index > 0x0d)
return -EINVAL;
save_flags(flags); cli();
spin_lock_irqsave(&ns87303_lock, flags);
outb(index, port);
value = inb(port + 1);
value &= ~(reserved[index] | clr);
value |= set;
outb(value, port + 1);
outb(value, port + 1);
restore_flags(flags);
spin_unlock_irqrestore(&ns87303_lock, flags);
return 0;
}
......
......@@ -51,6 +51,10 @@
#define PORT_UART00 35
#define PORT_21285 37
/* Sparc type numbers. */
#define PORT_SUNZILOG 38
#define PORT_SUNSAB 39
#ifdef __KERNEL__
#include <linux/config.h>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment