Commit 130ca59d authored by Linus Torvalds's avatar Linus Torvalds

Merge http://gkernel.bkbits.net/misc-2.5

into home.transmeta.com:/home/torvalds/v2.5/linux
parents c3b26786 0b520647
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/spinlock.h>
#include <asm/page.h> #include <asm/page.h>
#include <asm/oplib.h> #include <asm/oplib.h>
...@@ -17,6 +18,11 @@ ...@@ -17,6 +18,11 @@
#include <asm/smp.h> #include <asm/smp.h>
#include <asm/spitfire.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 } }; struct prom_cpuinfo linux_cpus[NR_CPUS] __initdata = { { 0 } };
unsigned prom_cpu_nodes[NR_CPUS]; unsigned prom_cpu_nodes[NR_CPUS];
int linux_num_cpus = 0; int linux_num_cpus = 0;
......
...@@ -45,7 +45,6 @@ cpuinfo_sparc cpu_data[NR_CPUS]; ...@@ -45,7 +45,6 @@ cpuinfo_sparc cpu_data[NR_CPUS];
/* Please don't make this stuff initdata!!! --DaveM */ /* Please don't make this stuff initdata!!! --DaveM */
static unsigned char boot_cpu_id; static unsigned char boot_cpu_id;
static int smp_activated;
/* Kernel spinlock */ /* Kernel spinlock */
spinlock_t kernel_flag __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; spinlock_t kernel_flag __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
...@@ -223,85 +222,46 @@ extern unsigned long sparc64_cpu_startup; ...@@ -223,85 +222,46 @@ extern unsigned long sparc64_cpu_startup;
*/ */
static struct thread_info *cpu_new_thread = NULL; 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 entry =
(unsigned long)(&sparc64_cpu_startup); (unsigned long)(&sparc64_cpu_startup);
unsigned long cookie = unsigned long cookie =
(unsigned long)(&cpu_new_thread); (unsigned long)(&cpu_new_thread);
struct task_struct *p; struct task_struct *p;
int timeout; int timeout, no, ret;
int no;
prom_printf("Starting CPU %d... ", i);
kernel_thread(NULL, NULL, CLONE_IDLETASK); kernel_thread(NULL, NULL, CLONE_IDLETASK);
cpucount++;
p = prev_task(&init_task); p = prev_task(&init_task);
init_idle(p, i); init_idle(p, cpu);
unhash_process(p); unhash_process(p);
callin_flag = 0; callin_flag = 0;
for (no = 0; no < linux_num_cpus; no++) for (no = 0; no < linux_num_cpus; no++)
if (linux_cpus[no].mid == i) if (linux_cpus[no].mid == cpu)
break; break;
cpu_new_thread = p->thread_info; cpu_new_thread = p->thread_info;
set_bit(i, &cpu_callout_map); set_bit(cpu, &cpu_callout_map);
prom_startcpu(linux_cpus[no].prom_node, prom_startcpu(linux_cpus[no].prom_node, entry, cookie);
entry, cookie);
for (timeout = 0; timeout < 5000000; timeout++) { for (timeout = 0; timeout < 5000000; timeout++) {
if (callin_flag) if (callin_flag)
break; break;
udelay(100); udelay(100);
} }
if (callin_flag) { if (callin_flag) {
prom_cpu_nodes[i] = linux_cpus[no].prom_node; prom_cpu_nodes[cpu] = linux_cpus[no].prom_node;
prom_printf("OK\n"); ret = 0;
} else { } else {
cpucount--; printk("Processor %d is stuck.\n", cpu);
printk("Processor %d is stuck.\n", i); clear_bit(cpu, &cpu_callout_map);
prom_printf("FAILED\n"); ret = -ENODEV;
clear_bit(i, &cpu_callout_map);
}
ignorecpu:
}
} }
cpu_new_thread = NULL; 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++) { return ret;
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;
}
} }
static void spitfire_xcall_helper(u64 data0, u64 data1, u64 data2, u64 pstate, unsigned long cpu) 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) ...@@ -1119,8 +1079,6 @@ static void __init smp_setup_percpu_timer(void)
void __init smp_tick_init(void) void __init smp_tick_init(void)
{ {
int i;
boot_cpu_id = hard_smp_processor_id(); boot_cpu_id = hard_smp_processor_id();
current_tick_offset = timer_tick_offset; current_tick_offset = timer_tick_offset;
...@@ -1129,19 +1087,10 @@ void __init smp_tick_init(void) ...@@ -1129,19 +1087,10 @@ void __init smp_tick_init(void)
prom_halt(); prom_halt();
} }
atomic_set(&sparc64_num_cpus_online, 1); atomic_inc(&sparc64_num_cpus_online);
memset(&cpu_online_map, 0, sizeof(cpu_online_map));
set_bit(boot_cpu_id, &cpu_online_map); set_bit(boot_cpu_id, &cpu_online_map);
prom_cpu_nodes[boot_cpu_id] = linux_cpus[0].prom_node; prom_cpu_nodes[boot_cpu_id] = linux_cpus[0].prom_node;
prof_counter(boot_cpu_id) = prof_multiplier(boot_cpu_id) = 1; 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; cycles_t cacheflush_time;
...@@ -1272,19 +1221,59 @@ int setup_profiling_timer(unsigned int multiplier) ...@@ -1272,19 +1221,59 @@ int setup_profiling_timer(unsigned int multiplier)
void __init smp_prepare_cpus(unsigned int max_cpus) 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 __devinit __cpu_up(unsigned int cpu)
{ {
int ret = smp_boot_one_cpu(cpu);
if (!ret) {
set_bit(cpu, &smp_commenced_mask); set_bit(cpu, &smp_commenced_mask);
while (!test_bit(cpu, &cpu_online_map)) while (!test_bit(cpu, &cpu_online_map))
mb(); mb();
return 0; if (!test_bit(cpu, &cpu_online_map))
ret = -ENODEV;
}
return ret;
} }
void __init smp_cpus_done(unsigned int max_cpus) 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 /* We want to run this with all the other cpus spinning
* in the kernel. * in the kernel.
*/ */
......
...@@ -52,6 +52,7 @@ ...@@ -52,6 +52,7 @@
#include <asm/isa.h> #include <asm/isa.h>
#endif #endif
#include <asm/a.out.h> #include <asm/a.out.h>
#include <asm/ns87303.h>
struct poll { struct poll {
int fd; int fd;
...@@ -373,3 +374,6 @@ EXPORT_SYMBOL(kbd_pt_regs); ...@@ -373,3 +374,6 @@ EXPORT_SYMBOL(kbd_pt_regs);
#ifdef CONFIG_DEBUG_BUGVERBOSE #ifdef CONFIG_DEBUG_BUGVERBOSE
EXPORT_SYMBOL(do_BUG); EXPORT_SYMBOL(do_BUG);
#endif #endif
/* for ns8703 */
EXPORT_SYMBOL(ns87303_lock);
...@@ -250,8 +250,8 @@ solaris_sys_table: ...@@ -250,8 +250,8 @@ solaris_sys_table:
.word solaris_fstatvfs64 /* fstatvfs64 dP 219 */ .word solaris_fstatvfs64 /* fstatvfs64 dP 219 */
.word solaris_setrlimit64 /* setrlimit64 dP 220 */ .word solaris_setrlimit64 /* setrlimit64 dP 220 */
.word solaris_getrlimit64 /* getrlimit64 dP 221 */ .word solaris_getrlimit64 /* getrlimit64 dP 221 */
.word CHAIN(pread) /* pread64 dpdD 222 */ .word CHAIN(pread64) /* pread64 dpdD 222 */
.word CHAIN(pwrite) /* pwrite64 dpdD 223 */ .word CHAIN(pwrite64) /* pwrite64 dpdD 223 */
.word CHAIN(creat) /* creat64 so 224 */ .word CHAIN(creat) /* creat64 so 224 */
.word solaris_open /* open64 soo 225 */ .word solaris_open /* open64 soo 225 */
.word solaris_unimplemented /* 226 */ .word solaris_unimplemented /* 226 */
......
...@@ -546,8 +546,11 @@ static void myri_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -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; struct myri_eth *mp = (struct myri_eth *) dev->priv;
unsigned long lregs = mp->lregs; unsigned long lregs = mp->lregs;
struct myri_channel *chan = &mp->shmem->channel; struct myri_channel *chan = &mp->shmem->channel;
unsigned long flags;
u32 status; u32 status;
spin_lock_irqsave(&mp->irq_lock, flags);
status = sbus_readl(lregs + LANAI_ISTAT); status = sbus_readl(lregs + LANAI_ISTAT);
DIRQ(("myri_interrupt: status[%08x] ", status)); DIRQ(("myri_interrupt: status[%08x] ", status));
if (status & ISTAT_HOST) { if (status & ISTAT_HOST) {
...@@ -569,6 +572,8 @@ static void myri_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -569,6 +572,8 @@ static void myri_interrupt(int irq, void *dev_id, struct pt_regs *regs)
myri_enable_irq(lregs, mp->cregs); myri_enable_irq(lregs, mp->cregs);
} }
DIRQ(("\n")); DIRQ(("\n"));
spin_unlock_irqrestore(&mp->irq_lock, flags);
} }
static int myri_open(struct net_device *dev) 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) ...@@ -622,7 +627,7 @@ static int myri_start_xmit(struct sk_buff *skb, struct net_device *dev)
return 1; return 1;
} }
save_and_cli(flags); spin_lock_irqsave(&mp->irq_lock, flags);
DHDR(("xmit[skbdata(%p)]\n", skb->data)); DHDR(("xmit[skbdata(%p)]\n", skb->data));
#ifdef DEBUG_HEADER #ifdef DEBUG_HEADER
...@@ -669,7 +674,7 @@ static int myri_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -669,7 +674,7 @@ static int myri_start_xmit(struct sk_buff *skb, struct net_device *dev)
DTX(("tbusy=0, returning 0\n")); DTX(("tbusy=0, returning 0\n"));
netif_start_queue(dev); netif_start_queue(dev);
restore_flags(flags); spin_unlock_irqrestore(&mp->irq_lock, flags);
return 0; return 0;
} }
...@@ -900,6 +905,7 @@ static int __init myri_ether_init(struct net_device *dev, struct sbus_dev *sdev, ...@@ -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); printk("%s: MyriCOM MyriNET Ethernet ", dev->name);
mp = (struct myri_eth *) dev->priv; mp = (struct myri_eth *) dev->priv;
spin_lock_init(&mp->irq_lock);
mp->myri_sdev = sdev; mp->myri_sdev = sdev;
/* Clean out skb arrays. */ /* Clean out skb arrays. */
......
...@@ -269,6 +269,7 @@ struct myri_eth { ...@@ -269,6 +269,7 @@ struct myri_eth {
/* These are frequently accessed, keep together /* These are frequently accessed, keep together
* to obtain good cache hit rates. * to obtain good cache hit rates.
*/ */
spinlock_t irq_lock;
struct myri_shmem *shmem; /* Shared data structures. */ struct myri_shmem *shmem; /* Shared data structures. */
unsigned long cregs; /* Control register space. */ unsigned long cregs; /* Control register space. */
struct recvq *rqack; /* Where we ack rx's. */ struct recvq *rqack; /* Where we ack rx's. */
......
...@@ -147,7 +147,6 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file, ...@@ -147,7 +147,6 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file,
DATA *data = (DATA *) file->private_data; DATA *data = (DATA *) file->private_data;
char buffer[OPROMMAXPARAM+1], *buf; char buffer[OPROMMAXPARAM+1], *buf;
struct openpromio *opp; struct openpromio *opp;
unsigned long flags;
int bufsize, len, error = 0; int bufsize, len, error = 0;
extern char saved_command_line[]; extern char saved_command_line[];
static int cnt; static int cnt;
...@@ -163,18 +162,14 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file, ...@@ -163,18 +162,14 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file,
switch (cmd) { switch (cmd) {
case OPROMGETOPT: case OPROMGETOPT:
case OPROMGETPROP: case OPROMGETPROP:
save_and_cli(flags);
len = prom_getproplen(node, opp->oprom_array); len = prom_getproplen(node, opp->oprom_array);
restore_flags(flags);
if (len <= 0 || len > bufsize) { if (len <= 0 || len > bufsize) {
error = copyout((void *)arg, opp, sizeof(int)); error = copyout((void *)arg, opp, sizeof(int));
break; break;
} }
save_and_cli(flags);
len = prom_getproperty(node, opp->oprom_array, buffer, bufsize); len = prom_getproperty(node, opp->oprom_array, buffer, bufsize);
restore_flags(flags);
memcpy(opp->oprom_array, buffer, len); memcpy(opp->oprom_array, buffer, len);
opp->oprom_array[len] = '\0'; opp->oprom_array[len] = '\0';
...@@ -185,9 +180,7 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file, ...@@ -185,9 +180,7 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file,
case OPROMNXTOPT: case OPROMNXTOPT:
case OPROMNXTPROP: case OPROMNXTPROP:
save_and_cli(flags);
buf = prom_nextprop(node, opp->oprom_array, buffer); buf = prom_nextprop(node, opp->oprom_array, buffer);
restore_flags(flags);
len = strlen(buf); len = strlen(buf);
if (len == 0 || len + 1 > bufsize) { if (len == 0 || len + 1 > bufsize) {
...@@ -207,10 +200,8 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file, ...@@ -207,10 +200,8 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file,
buf = opp->oprom_array + strlen(opp->oprom_array) + 1; buf = opp->oprom_array + strlen(opp->oprom_array) + 1;
len = opp->oprom_array + bufsize - buf; len = opp->oprom_array + bufsize - buf;
save_and_cli(flags);
error = prom_setprop(options_node, opp->oprom_array, error = prom_setprop(options_node, opp->oprom_array,
buf, len); buf, len);
restore_flags(flags);
if (error < 0) if (error < 0)
error = -EINVAL; error = -EINVAL;
...@@ -226,13 +217,11 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file, ...@@ -226,13 +217,11 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file,
node = *((int *) opp->oprom_array); node = *((int *) opp->oprom_array);
save_and_cli(flags);
switch (cmd) { switch (cmd) {
case OPROMNEXT: node = __prom_getsibling(node); break; case OPROMNEXT: node = __prom_getsibling(node); break;
case OPROMCHILD: node = __prom_getchild(node); break; case OPROMCHILD: node = __prom_getchild(node); break;
case OPROMSETCUR: break; case OPROMSETCUR: break;
} }
restore_flags(flags);
data->current_node = node; data->current_node = node;
*((int *)opp->oprom_array) = node; *((int *)opp->oprom_array) = node;
...@@ -264,9 +253,7 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file, ...@@ -264,9 +253,7 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file,
break; break;
case OPROMPATH2NODE: case OPROMPATH2NODE:
save_and_cli(flags);
node = prom_finddevice(opp->oprom_array); node = prom_finddevice(opp->oprom_array);
restore_flags(flags);
data->current_node = node; data->current_node = node;
*((int *)opp->oprom_array) = node; *((int *)opp->oprom_array) = node;
opp->oprom_size = sizeof(int); opp->oprom_size = sizeof(int);
...@@ -361,7 +348,6 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file, ...@@ -361,7 +348,6 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file,
{ {
DATA *data = (DATA *) file->private_data; DATA *data = (DATA *) file->private_data;
struct opiocdesc op; struct opiocdesc op;
unsigned long flags;
int error, node, len; int error, node, len;
char *str, *tmp; char *str, *tmp;
char buffer[64]; char buffer[64];
...@@ -379,9 +365,7 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file, ...@@ -379,9 +365,7 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file,
if (error) if (error)
return error; return error;
save_and_cli(flags);
len = prom_getproplen(op.op_nodeid,str); len = prom_getproplen(op.op_nodeid,str);
restore_flags(flags);
if (len > op.op_buflen) { if (len > op.op_buflen) {
kfree(str); kfree(str);
...@@ -405,9 +389,7 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file, ...@@ -405,9 +389,7 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file,
return -ENOMEM; return -ENOMEM;
} }
save_and_cli(flags);
prom_getproperty(op.op_nodeid, str, tmp, len); prom_getproperty(op.op_nodeid, str, tmp, len);
restore_flags(flags);
tmp[len] = '\0'; tmp[len] = '\0';
...@@ -431,9 +413,7 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file, ...@@ -431,9 +413,7 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file,
if (error) if (error)
return error; return error;
save_and_cli(flags);
tmp = prom_nextprop(op.op_nodeid,str,buffer); tmp = prom_nextprop(op.op_nodeid,str,buffer);
restore_flags(flags);
if (tmp) { if (tmp) {
len = strlen(tmp); len = strlen(tmp);
...@@ -481,9 +461,7 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file, ...@@ -481,9 +461,7 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file,
return error; return error;
} }
save_and_cli(flags);
len = prom_setprop(op.op_nodeid,str,tmp,op.op_buflen+1); len = prom_setprop(op.op_nodeid,str,tmp,op.op_buflen+1);
restore_flags(flags);
if (len != op.op_buflen) if (len != op.op_buflen)
return -EINVAL; return -EINVAL;
...@@ -503,12 +481,10 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file, ...@@ -503,12 +481,10 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file,
if (copy_from_user(&node, (void *)arg, sizeof(int))) if (copy_from_user(&node, (void *)arg, sizeof(int)))
return -EFAULT; return -EFAULT;
save_and_cli(flags);
if (cmd == OPIOCGETNEXT) if (cmd == OPIOCGETNEXT)
node = __prom_getsibling(node); node = __prom_getsibling(node);
else else
node = __prom_getchild(node); node = __prom_getchild(node);
restore_flags(flags);
if (__copy_to_user((void *)arg, &node, sizeof(int))) if (__copy_to_user((void *)arg, &node, sizeof(int)))
return -EFAULT; return -EFAULT;
...@@ -624,7 +600,6 @@ static struct miscdevice openprom_dev = { ...@@ -624,7 +600,6 @@ static struct miscdevice openprom_dev = {
static int __init openprom_init(void) static int __init openprom_init(void)
{ {
unsigned long flags;
int error; int error;
error = misc_register(&openprom_dev); error = misc_register(&openprom_dev);
...@@ -633,10 +608,8 @@ static int __init openprom_init(void) ...@@ -633,10 +608,8 @@ static int __init openprom_init(void)
return error; return error;
} }
save_and_cli(flags);
options_node = prom_getchild(prom_root_node); options_node = prom_getchild(prom_root_node);
options_node = prom_searchsiblings(options_node,"options"); options_node = prom_searchsiblings(options_node,"options");
restore_flags(flags);
if (options_node == 0 || options_node == -1) { if (options_node == 0 || options_node == -1) {
printk(KERN_ERR "openprom: unable to find options node\n"); printk(KERN_ERR "openprom: unable to find options node\n");
......
...@@ -318,7 +318,7 @@ static int qlogicpti_reset_hardware(struct Scsi_Host *host) ...@@ -318,7 +318,7 @@ static int qlogicpti_reset_hardware(struct Scsi_Host *host)
risc_code_addr = 0x1000; /* all load addresses are at 0x1000 */ 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); sbus_writew(HCCTRL_PAUSE, qpti->qregs + HCCTRL);
...@@ -366,7 +366,7 @@ static int qlogicpti_reset_hardware(struct Scsi_Host *host) ...@@ -366,7 +366,7 @@ static int qlogicpti_reset_hardware(struct Scsi_Host *host)
if (qlogicpti_mbox_command(qpti, param, 1)) { if (qlogicpti_mbox_command(qpti, param, 1)) {
printk(KERN_EMERG "qlogicpti%d: Cannot execute ISP firmware.\n", printk(KERN_EMERG "qlogicpti%d: Cannot execute ISP firmware.\n",
qpti->qpti_id); qpti->qpti_id);
restore_flags(flags); spin_unlock_irqrestore(&qpti->lock, flags);
return 1; return 1;
} }
...@@ -377,7 +377,7 @@ static int qlogicpti_reset_hardware(struct Scsi_Host *host) ...@@ -377,7 +377,7 @@ static int qlogicpti_reset_hardware(struct Scsi_Host *host)
(param[0] != MBOX_COMMAND_COMPLETE)) { (param[0] != MBOX_COMMAND_COMPLETE)) {
printk(KERN_EMERG "qlogicpti%d: Cannot set initiator SCSI ID.\n", printk(KERN_EMERG "qlogicpti%d: Cannot set initiator SCSI ID.\n",
qpti->qpti_id); qpti->qpti_id);
restore_flags(flags); spin_unlock_irqrestore(&qpti->lock, flags);
return 1; return 1;
} }
...@@ -392,7 +392,7 @@ static int qlogicpti_reset_hardware(struct Scsi_Host *host) ...@@ -392,7 +392,7 @@ static int qlogicpti_reset_hardware(struct Scsi_Host *host)
if (qlogicpti_mbox_command(qpti, param, 1)) { if (qlogicpti_mbox_command(qpti, param, 1)) {
printk(KERN_EMERG "qlogicpti%d: Cannot init response queue.\n", printk(KERN_EMERG "qlogicpti%d: Cannot init response queue.\n",
qpti->qpti_id); qpti->qpti_id);
restore_flags(flags); spin_unlock_irqrestore(&qpti->lock, flags);
return 1; return 1;
} }
...@@ -404,7 +404,7 @@ static int qlogicpti_reset_hardware(struct Scsi_Host *host) ...@@ -404,7 +404,7 @@ static int qlogicpti_reset_hardware(struct Scsi_Host *host)
if (qlogicpti_mbox_command(qpti, param, 1)) { if (qlogicpti_mbox_command(qpti, param, 1)) {
printk(KERN_EMERG "qlogicpti%d: Cannot init request queue.\n", printk(KERN_EMERG "qlogicpti%d: Cannot init request queue.\n",
qpti->qpti_id); qpti->qpti_id);
restore_flags(flags); spin_unlock_irqrestore(&qpti->lock, flags);
return 1; return 1;
} }
...@@ -450,7 +450,7 @@ static int qlogicpti_reset_hardware(struct Scsi_Host *host) ...@@ -450,7 +450,7 @@ static int qlogicpti_reset_hardware(struct Scsi_Host *host)
qlogicpti_mbox_command(qpti, param, 0); qlogicpti_mbox_command(qpti, param, 0);
qpti->send_marker = 1; qpti->send_marker = 1;
restore_flags(flags); spin_unlock_irqrestore(&qpti->lock, flags);
return 0; return 0;
} }
...@@ -468,7 +468,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti) ...@@ -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_addr = 0x1000; /* all f/w modules load at 0x1000 */
risc_code_length = sbus_risc_code_length01; 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 /* Verify the checksum twice, one before loading it, and once
* afterwards via the mailbox commands. * afterwards via the mailbox commands.
...@@ -476,7 +476,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti) ...@@ -476,7 +476,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti)
for (i = 0; i < risc_code_length; i++) for (i = 0; i < risc_code_length; i++)
csum += risc_code[i]; csum += risc_code[i];
if (csum) { if (csum) {
restore_flags(flags); spin_unlock_irqrestore(&qpti->lock, flags);
printk(KERN_EMERG "qlogicpti%d: Aieee, firmware checksum failed!", printk(KERN_EMERG "qlogicpti%d: Aieee, firmware checksum failed!",
qpti->qpti_id); qpti->qpti_id);
return 1; return 1;
...@@ -488,7 +488,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti) ...@@ -488,7 +488,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti)
while (--timeout && (sbus_readw(qpti->qregs + SBUS_CTRL) & SBUS_CTRL_RESET)) while (--timeout && (sbus_readw(qpti->qregs + SBUS_CTRL) & SBUS_CTRL_RESET))
udelay(20); udelay(20);
if (!timeout) { if (!timeout) {
restore_flags(flags); spin_unlock_irqrestore(&qpti->lock, flags);
printk(KERN_EMERG "qlogicpti%d: Cannot reset the ISP.", qpti->qpti_id); printk(KERN_EMERG "qlogicpti%d: Cannot reset the ISP.", qpti->qpti_id);
return 1; return 1;
} }
...@@ -528,7 +528,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti) ...@@ -528,7 +528,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti)
if (qlogicpti_mbox_command(qpti, param, 1)) { if (qlogicpti_mbox_command(qpti, param, 1)) {
printk(KERN_EMERG "qlogicpti%d: Cannot stop firmware for reload.\n", printk(KERN_EMERG "qlogicpti%d: Cannot stop firmware for reload.\n",
qpti->qpti_id); qpti->qpti_id);
restore_flags(flags); spin_unlock_irqrestore(&qpti->lock, flags);
return 1; return 1;
} }
...@@ -541,7 +541,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti) ...@@ -541,7 +541,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti)
param[0] != MBOX_COMMAND_COMPLETE) { param[0] != MBOX_COMMAND_COMPLETE) {
printk("qlogicpti%d: Firmware dload failed, I'm bolixed!\n", printk("qlogicpti%d: Firmware dload failed, I'm bolixed!\n",
qpti->qpti_id); qpti->qpti_id);
restore_flags(flags); spin_unlock_irqrestore(&qpti->lock, flags);
return 1; return 1;
} }
} }
...@@ -561,7 +561,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti) ...@@ -561,7 +561,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti)
(param[0] != MBOX_COMMAND_COMPLETE)) { (param[0] != MBOX_COMMAND_COMPLETE)) {
printk(KERN_EMERG "qlogicpti%d: New firmware csum failure!\n", printk(KERN_EMERG "qlogicpti%d: New firmware csum failure!\n",
qpti->qpti_id); qpti->qpti_id);
restore_flags(flags); spin_unlock_irqrestore(&qpti->lock, flags);
return 1; return 1;
} }
...@@ -575,7 +575,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti) ...@@ -575,7 +575,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti)
(param[0] != MBOX_COMMAND_COMPLETE)) { (param[0] != MBOX_COMMAND_COMPLETE)) {
printk(KERN_EMERG "qlogicpti%d: AboutFirmware cmd fails.\n", printk(KERN_EMERG "qlogicpti%d: AboutFirmware cmd fails.\n",
qpti->qpti_id); qpti->qpti_id);
restore_flags(flags); spin_unlock_irqrestore(&qpti->lock, flags);
return 1; return 1;
} }
...@@ -591,7 +591,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti) ...@@ -591,7 +591,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti)
(param[0] != MBOX_COMMAND_COMPLETE)) { (param[0] != MBOX_COMMAND_COMPLETE)) {
printk(KERN_EMERG "qlogicpti%d: could not set clock rate.\n", printk(KERN_EMERG "qlogicpti%d: could not set clock rate.\n",
qpti->qpti_id); qpti->qpti_id);
restore_flags(flags); spin_unlock_irqrestore(&qpti->lock, flags);
return 1; return 1;
} }
...@@ -608,7 +608,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti) ...@@ -608,7 +608,7 @@ static int __init qlogicpti_load_firmware(struct qlogicpti *qpti)
qlogicpti_mbox_command(qpti, param, 1); qlogicpti_mbox_command(qpti, param, 1);
} }
restore_flags(flags); spin_unlock_irqrestore(&qpti->lock, flags);
return 0; return 0;
} }
...@@ -1166,8 +1166,8 @@ static void ourdone(Scsi_Cmnd *Cmnd) ...@@ -1166,8 +1166,8 @@ static void ourdone(Scsi_Cmnd *Cmnd)
int qlogicpti_queuecommand_slow(Scsi_Cmnd *Cmnd, void (*done)(Scsi_Cmnd *)) int qlogicpti_queuecommand_slow(Scsi_Cmnd *Cmnd, void (*done)(Scsi_Cmnd *))
{ {
unsigned long flags;
struct qlogicpti *qpti = (struct qlogicpti *) Cmnd->host->hostdata; struct qlogicpti *qpti = (struct qlogicpti *) Cmnd->host->hostdata;
unsigned long flags;
/* /*
* done checking this host adapter? * done checking this host adapter?
...@@ -1178,12 +1178,13 @@ int qlogicpti_queuecommand_slow(Scsi_Cmnd *Cmnd, void (*done)(Scsi_Cmnd *)) ...@@ -1178,12 +1178,13 @@ int qlogicpti_queuecommand_slow(Scsi_Cmnd *Cmnd, void (*done)(Scsi_Cmnd *))
if (qpti->sbits && qpti->sbits != 0xffff) { if (qpti->sbits && qpti->sbits != 0xffff) {
/* See above about in ourdone this ugliness... */ /* See above about in ourdone this ugliness... */
Cmnd->SCp.Message = ((unsigned long)done) & 0xffffffff; Cmnd->SCp.Message = ((unsigned long)done) & 0xffffffff;
#ifdef __sparc_v9__ #ifdef CONFIG_SPARC64
Cmnd->SCp.Status = ((unsigned long)done >> 32UL) & 0xffffffff; Cmnd->SCp.Status = ((unsigned long)done >> 32UL) & 0xffffffff;
#endif #endif
return qlogicpti_queuecommand(Cmnd, ourdone); 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 * 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 *)) ...@@ -1226,7 +1227,8 @@ int qlogicpti_queuecommand_slow(Scsi_Cmnd *Cmnd, void (*done)(Scsi_Cmnd *))
if (qpti == NULL) if (qpti == NULL)
Cmnd->host->hostt->queuecommand = qlogicpti_queuecommand; Cmnd->host->hostt->queuecommand = qlogicpti_queuecommand;
restore_flags(flags); spin_unlock_irqrestore(&qpti->lock, flags);
return qlogicpti_queuecommand(Cmnd, done); return qlogicpti_queuecommand(Cmnd, done);
} }
......
/* $Id: sab82532.c,v 1.66 2002/01/08 16:00:16 davem Exp $ /* sunsab.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
* sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
* *
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) * 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. * Rewrote buffer handling to use CIRC(Circular Buffer) macros.
* Maxim Krasnyanskiy <maxk@qualcomm.com> * Maxim Krasnyanskiy <maxk@qualcomm.com>
...@@ -10,92 +10,61 @@ ...@@ -10,92 +10,61 @@
* rates to be programmed into the UART. Also eliminated a lot of * rates to be programmed into the UART. Also eliminated a lot of
* duplicated code in the console setup. * duplicated code in the console setup.
* Theodore Ts'o <tytso@mit.edu>, 2001-Oct-12 * 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/config.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/errno.h> #include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/timer.h> #include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/tty.h> #include <linux/tty.h>
#include <linux/tty_flip.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/major.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h> #include <linux/ptrace.h>
#include <linux/ioport.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/slab.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/delay.h>
#include <asm/sab82532.h> #include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/ebus.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/oplib.h>
#include <asm/ebus.h>
#include "sunserial.h" #include <linux/serial_core.h>
static DECLARE_TASK_QUEUE(tq_serial); #include "suncore.h"
#include "sunsab.h"
/* This is (one of many) a special gross hack to allow SU and
* SAB serials to co-exist on the same machine. -DaveM struct uart_sunsab_port {
*/ struct uart_port port; /* Generic UART port */
#undef SERIAL_BH union sab82532_async_regs *regs; /* Chip registers */
#define SERIAL_BH AURORA_BH unsigned long irqflags; /* IRQ state flags */
int xmit_fifo_size; /* TX fifo size */
static struct tty_driver serial_driver, callout_driver; int recv_fifo_size; /* RX fifo size */
static int sab82532_refcount; int dsr; /* Current DSR state */
unsigned int cec_timeout; /* Chip poll timeout... */
#undef SERIAL_PARANOIA_CHECK unsigned int tec_timeout; /* likewise */
#define SERIAL_DO_RESTART unsigned char interrupt_mask0;/* ISR0 masking */
unsigned char interrupt_mask1;/* ISR1 masking */
/* Set of debugging defines */ unsigned char pvr_dtr_bit; /* Which PVR bit is DTR */
#undef SERIAL_DEBUG_OPEN unsigned char pvr_dsr_bit; /* Which PVR bit is DSR */
#undef SERIAL_DEBUG_FLOW int type; /* SAB82532 version */
#undef SERIAL_DEBUG_MODEM int sab_line; /* Internal numbering */
#undef SERIAL_DEBUG_WAIT_UNTIL_SENT unsigned int irq; /* Device interrupt */
#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);
/* /*
* This assumes you have a 29.4912 MHz clock for your UART. * This assumes you have a 29.4912 MHz clock for your UART.
*/ */
#define BASE_BAUD ( 29491200 / 16 ) #define SAB_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
static char *sab82532_version[16] = { static char *sab82532_version[16] = {
"V1.0", "V2.0", "V3.2", "V(0x03)", "V1.0", "V2.0", "V3.2", "V(0x03)",
...@@ -103,841 +72,601 @@ static char *sab82532_version[16] = { ...@@ -103,841 +72,601 @@ static char *sab82532_version[16] = {
"V(0x08)", "V(0x09)", "V(0x0a)", "V(0x0b)", "V(0x08)", "V(0x09)", "V(0x0a)", "V(0x0b)",
"V(0x0c)", "V(0x0d)", "V(0x0e)", "V(0x0f)" "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_TEC_TIMEOUT 200000 /* 1 character time (at 50 baud) */
#define SAB82532_MAX_CEC_TIMEOUT 50000 /* 2.5 TX CLKs (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); 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); udelay(1);
} }
static __inline__ void sab82532_start_tx(struct sab82532 *info) static void receive_chars(struct uart_sunsab_port *up,
{ union sab82532_irq_status *stat,
unsigned long flags; struct pt_regs *regs)
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)
{ {
struct tty_struct *tty = info->tty; struct tty_struct *tty = up->port.info->tty;
unsigned char buf[32]; unsigned char buf[32];
unsigned char status; int saw_console_brk = 0;
int free_fifo = 0; int free_fifo = 0;
int i, count = 0; int count = 0;
int i;
/* Read number of BYTES (Character + Status) available. */ /* Read number of BYTES (Character + Status) available. */
if (stat->sreg.isr0 & SAB82532_ISR0_RPF) { if (stat->sreg.isr0 & SAB82532_ISR0_RPF) {
count = info->recv_fifo_size; count = up->recv_fifo_size;
free_fifo++; free_fifo++;
} }
if (stat->sreg.isr0 & SAB82532_ISR0_TCD) { 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++; free_fifo++;
} }
/* Issue a FIFO read command in case we where idle. */ /* Issue a FIFO read command in case we where idle. */
if (stat->sreg.isr0 & SAB82532_ISR0_TIME) { if (stat->sreg.isr0 & SAB82532_ISR0_TIME) {
sab82532_cec_wait(info); sunsab_cec_wait(up);
writeb(SAB82532_CMDR_RFRD, &info->regs->w.cmdr); writeb(SAB82532_CMDR_RFRD, &up->regs->w.cmdr);
return; return;
} }
if (stat->sreg.isr0 & SAB82532_ISR0_RFO) { if (stat->sreg.isr0 & SAB82532_ISR0_RFO)
#ifdef SERIAL_DEBUG_OVERFLOW
printk("sab82532: receive_chars: RFO");
#endif
free_fifo++; free_fifo++;
}
/* Read the FIFO. */ /* Read the FIFO. */
for (i = 0; i < count; i++) 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. */ /* Issue Receive Message Complete command. */
if (free_fifo) { if (free_fifo) {
sab82532_cec_wait(info); sunsab_cec_wait(up);
writeb(SAB82532_CMDR_RMC, &info->regs->w.cmdr); writeb(SAB82532_CMDR_RMC, &up->regs->w.cmdr);
} }
if (!tty) for (i = 0; i < count; i++) {
return; unsigned char ch = buf[i];
for (i = 0; i < count; ) { if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) {
if (tty->flip.count >= TTY_FLIPBUF_SIZE) { tty->flip.tqueue.routine((void *)tty);
#ifdef SERIAL_DEBUG_OVERFLOW if (tty->flip.count >= TTY_FLIPBUF_SIZE)
printk("sab82532: receive_chars: tty overrun\n"); return; // if TTY_DONT_FLIP is set
#endif
info->icount.buf_overrun++;
break;
} }
tty->flip.count++; *tty->flip.char_buf_ptr = ch;
*tty->flip.char_buf_ptr++ = buf[i++]; *tty->flip.flag_buf_ptr = TTY_NORMAL;
status = buf[i++]; up->port.icount.rx++;
info->icount.rx++;
#ifdef SERIAL_DEBUG_INTR if (unlikely(stat->sreg.isr0 & (SAB82532_ISR0_PERR |
printk("DR%02x:%02x...", (unsigned char)*(tty->flip.char_buf_ptr - 1), status); SAB82532_ISR0_FERR |
#endif 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; * Mask off conditions which should be ingored.
info->icount.parity++; */
} else if (status & SAB82532_RSTAT_FE) { stat->sreg.isr0 &= (up->port.read_status_mask & 0xff);
*tty->flip.flag_buf_ptr++ = TTY_FRAME; stat->sreg.isr1 &= ((up->port.read_status_mask >> 8) & 0xff);
info->icount.frame++;
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) union sab82532_irq_status *stat)
{ {
struct circ_buf *xmit = &up->port.info->xmit;
int i; int i;
if (stat->sreg.isr1 & SAB82532_ISR1_ALLS) { if (stat->sreg.isr1 & SAB82532_ISR1_ALLS) {
info->interrupt_mask1 |= SAB82532_IMR1_ALLS; up->interrupt_mask1 |= SAB82532_IMR1_ALLS;
writeb(info->interrupt_mask1, &info->regs->w.imr1); writeb(up->interrupt_mask1, &up->regs->w.imr1);
set_bit(SAB82532_ALLS, &info->irqflags); set_bit(SAB82532_ALLS, &up->irqflags);
} }
if (!(stat->sreg.isr1 & SAB82532_ISR1_XPR)) if (!(stat->sreg.isr1 & SAB82532_ISR1_XPR))
return; return;
if (!(readb(&info->regs->r.star) & SAB82532_STAR_XFW)) { if (!(readb(&up->regs->r.star) & SAB82532_STAR_XFW))
#ifdef SERIAL_DEBUG_FIFO
printk("%s: XPR, but no XFW (?)\n", __FUNCTION__);
#endif
return; return;
}
set_bit(SAB82532_XPR, &info->irqflags);
if (!info->tty) { set_bit(SAB82532_XPR, &up->irqflags);
info->interrupt_mask1 |= SAB82532_IMR1_XPR;
writeb(info->interrupt_mask1, &info->regs->w.imr1);
return;
}
if ((info->xmit.head == info->xmit.tail) || if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
info->tty->stopped || info->tty->hw_stopped) { up->interrupt_mask1 |= SAB82532_IMR1_XPR;
info->interrupt_mask1 |= SAB82532_IMR1_XPR; writeb(up->interrupt_mask1, &up->regs->w.imr1);
writeb(info->interrupt_mask1, &info->regs->w.imr1);
return; return;
} }
info->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS); up->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS);
writeb(info->interrupt_mask1, &info->regs->w.imr1); writeb(up->interrupt_mask1, &up->regs->w.imr1);
clear_bit(SAB82532_ALLS, &info->irqflags); clear_bit(SAB82532_ALLS, &up->irqflags);
/* Stuff 32 bytes into Transmit FIFO. */ /* Stuff 32 bytes into Transmit FIFO. */
clear_bit(SAB82532_XPR, &info->irqflags); clear_bit(SAB82532_XPR, &up->irqflags);
for (i = 0; i < info->xmit_fifo_size; i++) { for (i = 0; i < up->xmit_fifo_size; i++) {
writeb(info->xmit.buf[info->xmit.tail], writeb(xmit->buf[xmit->tail],
&info->regs->w.xfifo[i]); &up->regs->w.xfifo[i]);
info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
info->icount.tx++; up->port.icount.tx++;
if (info->xmit.head == info->xmit.tail) if (uart_circ_empty(xmit))
break; break;
} }
/* Issue a Transmit Frame command. */ /* Issue a Transmit Frame command. */
sab82532_cec_wait(info); sunsab_cec_wait(up);
writeb(SAB82532_CMDR_XF, &info->regs->w.cmdr); writeb(SAB82532_CMDR_XF, &up->regs->w.cmdr);
if (CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) < WAKEUP_CHARS) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
sab82532_sched_event(info, RS_EVENT_WRITE_WAKEUP); uart_event(&up->port, EVT_WRITE_WAKEUP);
#ifdef SERIAL_DEBUG_INTR if (uart_circ_empty(xmit))
printk("THRE..."); sunsab_stop_tx(&up->port, 0);
#endif
} }
static void check_status(struct sab82532 *info, static void check_status(struct uart_sunsab_port *up,
union sab82532_irq_status *stat) union sab82532_irq_status *stat)
{ {
struct tty_struct *tty = info->tty; if (stat->sreg.isr0 & SAB82532_ISR0_CDSC)
int modem_change = 0; uart_handle_dcd_change(&up->port,
!(readb(&up->regs->r.vstr) & SAB82532_VSTR_CD));
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 (info->flags & ASYNC_CTS_FLOW) { if (stat->sreg.isr1 & SAB82532_ISR1_CSC)
if (info->tty->hw_stopped) { uart_handle_cts_change(&up->port,
if (info->cts) { (readb(&up->regs->r.star) & SAB82532_STAR_CTS));
#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) if ((readb(&up->regs->r.pvr) & up->pvr_dsr_bit) ^ up->dsr) {
printk("CTS tx start..."); up->dsr = (readb(&up->regs->r.pvr) & up->pvr_dsr_bit) ? 0 : 1;
#endif up->port.icount.dsr++;
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);
} }
} else {
if (!(info->cts)) {
#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) wake_up_interruptible(&up->port.info->delta_msr_wait);
printk("CTS tx stop...");
#endif
info->tty->hw_stopped = 1;
}
}
}
} }
/* static void sunsab_interrupt(int irq, void *dev_id, struct pt_regs *regs)
* This is the serial driver's generic interrupt routine
*/
static void sab82532_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; union sab82532_irq_status status;
unsigned long flags;
#ifdef SERIAL_DEBUG_INTR spin_lock_irqsave(&up->port.lock, flags);
printk("sab82532_interrupt(%d)...", irq);
#endif
status.stat = 0; status.stat = 0;
if (readb(&info->regs->r.gis) & SAB82532_GIS_ISA0) if (readb(&up->regs->r.gis) & SAB82532_GIS_ISA0)
status.sreg.isr0 = readb(&info->regs->r.isr0); status.sreg.isr0 = readb(&up->regs->r.isr0);
if (readb(&info->regs->r.gis) & SAB82532_GIS_ISA1) if (readb(&up->regs->r.gis) & SAB82532_GIS_ISA1)
status.sreg.isr1 = readb(&info->regs->r.isr1); status.sreg.isr1 = readb(&up->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 (status.stat) {
if (status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME | if (status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
SAB82532_ISR0_RFO | SAB82532_ISR0_RPF)) SAB82532_ISR0_RFO | SAB82532_ISR0_RPF))
receive_chars(info, &status); receive_chars(up, &status, regs);
if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) || if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) ||
(status.sreg.isr1 & (SAB82532_ISR1_BRK | SAB82532_ISR1_CSC))) (status.sreg.isr1 & SAB82532_ISR1_CSC))
check_status(info, &status); check_status(up, &status);
if (status.sreg.isr1 & (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR)) if (status.sreg.isr1 & (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR))
transmit_chars(info, &status); transmit_chars(up, &status);
}
next: spin_unlock(&up->port.lock);
info = info->next;
status.stat = 0; up++;
if (readb(&info->regs->r.gis) & SAB82532_GIS_ISB0)
status.sreg.isr0 = readb(&info->regs->r.isr0); spin_lock(&up->port.lock);
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
if (!status.stat) status.stat = 0;
goto done; 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 | if (status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
SAB82532_ISR0_RFO | SAB82532_ISR0_RPF)) SAB82532_ISR0_RFO | SAB82532_ISR0_RPF))
receive_chars(info, &status); receive_chars(up, &status, regs);
if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) || if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) ||
(status.sreg.isr1 & (SAB82532_ISR1_BRK | SAB82532_ISR1_CSC))) (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)) if (status.sreg.isr1 & (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR))
transmit_chars(info, &status); transmit_chars(up, &status);
}
done: spin_unlock_irqrestore(&up->port.lock, flags);
;
#ifdef SERIAL_DEBUG_INTR
printk("end.\n");
#endif
} }
/* /* port->lock is not held. */
* ------------------------------------------------------------------- static unsigned int sunsab_tx_empty(struct uart_port *port)
* Here ends the serial interrupt routines. {
* ------------------------------------------------------------------- struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
*/ int ret;
/* /* Do not need a lock for a state test like this. */
* This routine is used to handle the "bottom half" processing for the if (test_bit(SAB82532_ALLS, &up->irqflags))
* serial driver, known also the "software interrupt" processing. ret = TIOCSER_TEMT;
* This processing is done at the kernel interrupt level, after the else
* sab82532_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This ret = 0;
* is where time-consuming activities which can not be done in the
* interrupt driver proper are done; the interrupt driver schedules return ret;
* them using sab82532_sched_event(), and they get done here. }
*/
static void do_serial_bh(void) /* 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 uart_sunsab_port *up = (struct uart_sunsab_port *) port;
struct tty_struct *tty; struct circ_buf *xmit = &up->port.info->xmit;
int i;
tty = info->tty; if (!test_bit(SAB82532_XPR, &up->irqflags))
if (!tty)
return; return;
if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { up->interrupt_mask1 &= ~SAB82532_IMR1_XPR;
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && writeb(up->interrupt_mask1, &up->regs->w.imr1);
tty->ldisc.write_wakeup) clear_bit(SAB82532_ALLS, &up->irqflags);
(tty->ldisc.write_wakeup)(tty); clear_bit(SAB82532_XPR, &up->irqflags);
wake_up_interruptible(&tty->write_wait);
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);
} }
/* /* port->lock is not held. */
* This routine is called from the scheduler tqueue when the interrupt static void sunsab_send_xchar(struct uart_port *port, char ch)
* 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_)
{ {
struct sab82532 *info = (struct sab82532 *) private_; struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
struct tty_struct *tty; unsigned long flags;
tty = info->tty; spin_lock_irqsave(&up->port.lock, flags);
if (tty)
tty_hangup(tty); sunsab_tec_wait(up);
MOD_DEC_USE_COUNT; writeb(ch, &up->regs->w.tic);
spin_unlock_irqrestore(&up->port.lock, flags);
} }
static void /* port->lock held by caller. */
sab82532_init_line(struct sab82532 *info) static void sunsab_stop_rx(struct uart_port *port)
{ {
unsigned char stat, tmp; struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
/* up->interrupt_mask0 |= SAB82532_ISR0_TCD;
* Wait for any commands or immediate characters writeb(up->interrupt_mask1, &up->regs->w.imr0);
*/ }
sab82532_cec_wait(info);
sab82532_tec_wait(info);
/* /* port->lock held by caller. */
* Clear the FIFO buffers. static void sunsab_enable_ms(struct uart_port *port)
*/ {
writeb(SAB82532_CMDR_RRES, &info->regs->w.cmdr); /* For now we always receive these interrupts. */
sab82532_cec_wait(info); }
writeb(SAB82532_CMDR_XRES, &info->regs->w.cmdr);
/* /* port->lock is not held. */
* Clear the interrupt registers. static void sunsab_break_ctl(struct uart_port *port, int break_state)
*/ {
stat = readb(&info->regs->r.isr0); struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
stat = readb(&info->regs->r.isr1); 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 * 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 | writeb(SAB82532_CCR0_MCE | SAB82532_CCR0_SC_NRZ |
SAB82532_CCR0_SM_ASYNC, &info->regs->w.ccr0); SAB82532_CCR0_SM_ASYNC, &up->regs->w.ccr0);
writeb(SAB82532_CCR1_ODS | SAB82532_CCR1_BCR | 7, &info->regs->w.ccr1); writeb(SAB82532_CCR1_ODS | SAB82532_CCR1_BCR | 7, &up->regs->w.ccr1);
writeb(SAB82532_CCR2_BDF | SAB82532_CCR2_SSEL | writeb(SAB82532_CCR2_BDF | SAB82532_CCR2_SSEL |
SAB82532_CCR2_TOE, &info->regs->w.ccr2); SAB82532_CCR2_TOE, &up->regs->w.ccr2);
writeb(0, &info->regs->w.ccr3); writeb(0, &up->regs->w.ccr3);
writeb(SAB82532_CCR4_MCK4 | SAB82532_CCR4_EBRG, &info->regs->w.ccr4); writeb(SAB82532_CCR4_MCK4 | SAB82532_CCR4_EBRG, &up->regs->w.ccr4);
writeb(SAB82532_MODE_RTS | SAB82532_MODE_FCTS | writeb(SAB82532_MODE_RTS | SAB82532_MODE_FCTS |
SAB82532_MODE_RAC, &info->regs->w.mode); SAB82532_MODE_RAC, &up->regs->w.mode);
writeb(SAB82532_RFC_DPS | SAB82532_RFC_RFDF, &info->regs->w.rfc); writeb(SAB82532_RFC_DPS | SAB82532_RFC_RFDF, &up->regs->w.rfc);
switch (info->recv_fifo_size) {
switch (up->recv_fifo_size) {
case 1: case 1:
tmp = readb(&info->regs->w.rfc); tmp = readb(&up->regs->w.rfc);
tmp |= SAB82532_RFC_RFTH_1; tmp |= SAB82532_RFC_RFTH_1;
writeb(tmp, &info->regs->w.rfc); writeb(tmp, &up->regs->w.rfc);
break; break;
case 4: case 4:
tmp = readb(&info->regs->w.rfc); tmp = readb(&up->regs->w.rfc);
tmp |= SAB82532_RFC_RFTH_4; tmp |= SAB82532_RFC_RFTH_4;
writeb(tmp, &info->regs->w.rfc); writeb(tmp, &up->regs->w.rfc);
break; break;
case 16: case 16:
tmp = readb(&info->regs->w.rfc); tmp = readb(&up->regs->w.rfc);
tmp |= SAB82532_RFC_RFTH_16; tmp |= SAB82532_RFC_RFTH_16;
writeb(tmp, &info->regs->w.rfc); writeb(tmp, &up->regs->w.rfc);
break; break;
default: default:
info->recv_fifo_size = 32; up->recv_fifo_size = 32;
/* fall through */ /* fall through */
case 32: case 32:
tmp = readb(&info->regs->w.rfc); tmp = readb(&up->regs->w.rfc);
tmp |= SAB82532_RFC_RFTH_32; tmp |= SAB82532_RFC_RFTH_32;
writeb(tmp, &info->regs->w.rfc); writeb(tmp, &up->regs->w.rfc);
break; 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 = readb(&up->regs->rw.ccr0);
tmp &= ~(SAB82532_MODE_FRTS); tmp |= SAB82532_CCR0_PU; /* power-up */
tmp |= SAB82532_MODE_RTS; writeb(tmp, &up->regs->rw.ccr0);
writeb(tmp, &info->regs->rw.mode);
tmp = readb(&info->regs->rw.pvr);
tmp &= ~(info->pvr_dtr_bit);
writeb(tmp, &info->regs->rw.pvr);
}
/* /*
* Finally, enable interrupts * Finally, enable interrupts
*/ */
info->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR | up->interrupt_mask0 = (SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
SAB82532_IMR0_PLLA; SAB82532_IMR0_PLLA);
writeb(info->interrupt_mask0, &info->regs->w.imr0); writeb(up->interrupt_mask0, &up->regs->w.imr0);
info->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS | up->interrupt_mask1 = (SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS |
SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN | SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN |
SAB82532_IMR1_CSC | SAB82532_IMR1_XON | SAB82532_IMR1_CSC | SAB82532_IMR1_XON |
SAB82532_IMR1_XPR; SAB82532_IMR1_XPR);
writeb(info->interrupt_mask1, &info->regs->w.imr1); writeb(up->interrupt_mask1, &up->regs->w.imr1);
set_bit(SAB82532_ALLS, &info->irqflags); set_bit(SAB82532_ALLS, &up->irqflags);
set_bit(SAB82532_XPR, &up->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);
/* spin_unlock_irqrestore(&up->port.lock, flags);
* and set the speed of the serial port
*/
change_speed(info);
info->flags |= ASYNC_INITIALIZED;
restore_flags(flags);
return 0; return 0;
errout:
restore_flags(flags);
return retval;
} }
/* /* port->lock is not held. */
* This routine will shutdown a serial port; interrupts are disabled, and static void sunsab_shutdown(struct uart_port *port)
* DTR is dropped if the hangup on close termio flag is on.
*/
static void shutdown(struct sab82532 *info)
{ {
struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
unsigned long flags; unsigned long flags;
u8 tmp; unsigned char 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;
}
if (info->is_console) { spin_lock_irqsave(&up->port.lock, flags);
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;
}
/* Disable Interrupts */ /* Disable Interrupts */
info->interrupt_mask0 = 0xff; up->interrupt_mask0 = 0xff;
writeb(info->interrupt_mask0, &info->regs->w.imr0); writeb(up->interrupt_mask0, &up->regs->w.imr0);
info->interrupt_mask1 = 0xff; up->interrupt_mask1 = 0xff;
writeb(info->interrupt_mask1, &info->regs->w.imr1); writeb(up->interrupt_mask1, &up->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);
}
/* Disable break condition */ /* Disable break condition */
tmp = readb(&info->regs->rw.dafo); tmp = readb(&up->regs->rw.dafo);
tmp &= ~(SAB82532_DAFO_XBRK); tmp &= ~SAB82532_DAFO_XBRK;
writeb(tmp, &info->regs->rw.dafo); writeb(tmp, &up->regs->rw.dafo);
/* Disable Receiver */ /* Disable Receiver */
tmp = readb(&info->regs->rw.mode); tmp = readb(&up->regs->rw.mode);
tmp &= ~(SAB82532_MODE_RAC); tmp &= ~SAB82532_MODE_RAC;
writeb(tmp, &info->regs->rw.mode); writeb(tmp, &up->regs->rw.mode);
/* Power Down */ /* Power Down */
tmp = readb(&info->regs->rw.ccr0); tmp = readb(&up->regs->rw.ccr0);
tmp &= ~(SAB82532_CCR0_PU); tmp &= ~SAB82532_CCR0_PU;
writeb(tmp, &info->regs->rw.ccr0); writeb(tmp, &up->regs->rw.ccr0);
if (info->tty) spin_unlock_irqrestore(&up->port.lock, flags);
set_bit(TTY_IO_ERROR, &info->tty->flags);
info->flags &= ~ASYNC_INITIALIZED;
restore_flags(flags);
} }
/* /*
* This routine is called to set the UART divisor registers to match * This is used to figure out the divisor speeds.
* the specified baud rate for a serial port. *
* 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; unsigned int ebrg;
tcflag_t cflag;
unsigned char dafo; unsigned char dafo;
int bits, n, m; int bits, n, m;
if (!info->tty || !info->tty->termios)
return;
cflag = info->tty->termios->c_cflag;
/* Byte size and parity */ /* Byte size and parity */
switch (cflag & CSIZE) { switch (cflag & CSIZE) {
case CS5: dafo = SAB82532_DAFO_CHL5; bits = 7; break; case CS5: dafo = SAB82532_DAFO_CHL5; bits = 7; break;
...@@ -959,1658 +688,483 @@ static void change_speed(struct sab82532 *info) ...@@ -959,1658 +688,483 @@ static void change_speed(struct sab82532 *info)
} }
if (cflag & PARODD) { if (cflag & PARODD) {
#ifdef CMSPAR
if (cflag & CMSPAR)
dafo |= SAB82532_DAFO_PAR_MARK;
else
#endif
dafo |= SAB82532_DAFO_PAR_ODD; dafo |= SAB82532_DAFO_PAR_ODD;
} else { } else {
#ifdef CMSPAR
if (cflag & CMSPAR)
dafo |= SAB82532_DAFO_PAR_SPACE;
else
#endif
dafo |= SAB82532_DAFO_PAR_EVEN; dafo |= SAB82532_DAFO_PAR_EVEN;
} }
/* Determine EBRG values based on baud rate */ calc_ebrg(baud, &n, &m);
info->baud = tty_get_baud_rate(info->tty);
calc_ebrg(info->baud, &n, &m);
ebrg = n | (m << 6); ebrg = n | (m << 6);
if (info->baud) { up->tec_timeout = (10 * 1000000) / baud;
info->timeout = (info->xmit_fifo_size * HZ * bits) / info->baud; up->cec_timeout = up->tec_timeout >> 2;
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 */
/* CTS flow control flags */ /* CTS flow control flags */
if (cflag & CRTSCTS) /* We encode read_status_mask and ignore_status_mask like so:
info->flags |= ASYNC_CTS_FLOW; *
else * ---------------------
info->flags &= ~(ASYNC_CTS_FLOW); * | ... | ISR1 | ISR0 |
* ---------------------
if (cflag & CLOCAL) * .. 15 8 7 0
info->flags &= ~(ASYNC_CHECK_CD); */
else
info->flags |= ASYNC_CHECK_CD; up->port.read_status_mask = (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
if (info->tty) SAB82532_ISR0_RFO | SAB82532_ISR0_RPF |
info->tty->hw_stopped = 0; 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 * Characteres to ignore
* XXX: not implemented, yet. */
*/ up->port.ignore_status_mask = 0;
#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) 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 * If we're ignoring parity and break indicators,
* XXX: not implemented, yet. * 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 * ignore all characters if CREAD is not set
* XXX: not implemented, yet.
*/ */
if ((cflag & CREAD) == 0) if ((cflag & CREAD) == 0)
info->ignore_status_mask |= SAB82532_ISR0_RPF | up->port.ignore_status_mask |= (SAB82532_ISR0_RPF |
SAB82532_ISR0_TCD | SAB82532_ISR0_TCD);
SAB82532_ISR0_TIME;
/* Now bang the new settings into the chip. */
save_flags(flags); cli(); sunsab_cec_wait(up);
sab82532_cec_wait(info); sunsab_tec_wait(up);
sab82532_tec_wait(info); writeb(dafo, &up->regs->w.dafo);
writeb(dafo, &info->regs->w.dafo); writeb(ebrg & 0xff, &up->regs->w.bgr);
writeb(ebrg & 0xff, &info->regs->w.bgr); writeb((readb(&up->regs->rw.ccr2) & ~0xc0) | ((ebrg >> 2) & 0xc0),
writeb(readb(&info->regs->rw.ccr2) & ~(0xc0), &info->regs->rw.ccr2); &up->regs->rw.ccr2);
writeb(readb(&info->regs->rw.ccr2) | ((ebrg >> 2) & 0xc0), &info->regs->rw.ccr2);
if (info->flags & ASYNC_CTS_FLOW) { if (cflag & CRTSCTS) {
writeb(readb(&info->regs->rw.mode) & ~(SAB82532_MODE_RTS), &info->regs->rw.mode); writeb(readb(&up->regs->rw.mode) & ~SAB82532_MODE_RTS,
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_FRTS, &info->regs->rw.mode); &up->regs->rw.mode);
writeb(readb(&info->regs->rw.mode) & ~(SAB82532_MODE_FCTS), &info->regs->rw.mode); writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_FRTS,
info->interrupt_mask1 &= ~(SAB82532_IMR1_CSC); &up->regs->rw.mode);
writeb(info->interrupt_mask1, &info->regs->w.imr1); 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 { } else {
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_RTS, &info->regs->rw.mode); writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_RTS,
writeb(readb(&info->regs->rw.mode) & ~(SAB82532_MODE_FRTS), &info->regs->rw.mode); &up->regs->rw.mode);
writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_FCTS, &info->regs->rw.mode); writeb(readb(&up->regs->rw.mode) & ~SAB82532_MODE_FRTS,
info->interrupt_mask1 |= SAB82532_IMR1_CSC; &up->regs->rw.mode);
writeb(info->interrupt_mask1, &info->regs->w.imr1); 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); writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_RAC, &up->regs->rw.mode);
restore_flags(flags);
} }
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; unsigned long flags;
int baud;
if (serial_paranoia_check(info, tty->device, "sab82532_put_char")) spin_lock_irqsave(&up->port.lock, flags);
return;
if (!tty || !info->xmit.buf) /* Undo what generic UART core did. */
return; baud = (SAB_BASE_BAUD / (quot * 16));
save_flags(flags); cli(); sunsab_convert_to_sab(up, cflag, iflag, baud);
if (!CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE)) {
restore_flags(flags);
return;
}
info->xmit.buf[info->xmit.head] = ch; spin_unlock_irqrestore(&up->port.lock, flags);
info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1);
restore_flags(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; return "SunSAB";
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);
} }
static int sab82532_write(struct tty_struct * tty, int from_user, static void sunsab_release_port(struct uart_port *port)
const unsigned char *buf, int count)
{ {
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; return 0;
}
if (!tty || !info->xmit.buf || !tmp_buf) static void sunsab_config_port(struct uart_port *port, int flags)
return 0; {
}
save_flags(flags); static int sunsab_verify_port(struct uart_port *port, struct serial_struct *ser)
if (from_user) { {
down(&tmp_buf_sem); return -EINVAL;
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;
c -= copy_from_user(tmp_buf, buf, c); static struct uart_ops sunsab_pops = {
if (!c) { .tx_empty = sunsab_tx_empty,
if (!ret) .set_mctrl = sunsab_set_mctrl,
ret = -EFAULT; .get_mctrl = sunsab_get_mctrl,
break; .stop_tx = sunsab_stop_tx,
} .start_tx = sunsab_start_tx,
cli(); .send_xchar = sunsab_send_xchar,
c1 = CIRC_SPACE_TO_END(info->xmit.head, .stop_rx = sunsab_stop_rx,
info->xmit.tail, .enable_ms = sunsab_enable_ms,
SERIAL_XMIT_SIZE); .break_ctl = sunsab_break_ctl,
if (c1 < c) .startup = sunsab_startup,
c = c1; .shutdown = sunsab_shutdown,
memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c); .change_speed = sunsab_change_speed,
info->xmit.head = (info->xmit.head + c) & (SERIAL_XMIT_SIZE-1); .type = sunsab_type,
restore_flags(flags); .release_port = sunsab_release_port,
buf += c; .request_port = sunsab_request_port,
count -= c; .config_port = sunsab_config_port,
ret += c; .verify_port = sunsab_verify_port,
} };
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);
}
if ((info->xmit.head != info->xmit.tail) && static struct uart_driver sunsab_reg = {
!tty->stopped && !tty->hw_stopped) { .owner = THIS_MODULE,
info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR); .driver_name = "serial",
writeb(info->interrupt_mask1, &info->regs->w.imr1); #ifdef CONFIG_DEVFS_FS
sab82532_start_tx(info); .dev_name = "tts/%d",
} #else
.dev_name = "ttyS",
#endif
.major = TTY_MAJOR,
};
restore_flags(flags); static struct uart_sunsab_port *sunsab_ports;
return ret; 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")) spin_lock_irqsave(&up->port.lock, flags);
return 0;
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")) for (i = 0; i < n; i++) {
return 0; 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; unsigned long flags;
int baud;
if (serial_paranoia_check(info, tty->device, "sab82532_flush_buffer")) printk("Console: ttyS%d (SAB82532)\n",
return; (sunsab_reg.minor - 64) + con->index);
save_flags(flags); cli(); sunserial_console_termios(con);
info->xmit.head = info->xmit.tail = 0;
restore_flags(flags);
wake_up_interruptible(&tty->write_wait); /* Firmware console speed is limited to 150-->38400 baud so
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && * this hackish cflag thing is OK.
tty->ldisc.write_wakeup) */
(tty->ldisc.write_wakeup)(tty); 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 * Temporary fix.
* the device
*/ */
static void sab82532_send_xchar(struct tty_struct *tty, char ch) spin_lock_init(&up->port.lock);
{
struct sab82532 *info = (struct sab82532 *)tty->driver_data;
unsigned long flags;
if (serial_paranoia_check(info, tty->device, "sab82532_send_xchar")) /*
return; * Initialize the hardware
*/
sunsab_startup(&up->port);
save_flags(flags); cli(); spin_lock_irqsave(&up->port.lock, flags);
sab82532_tec_wait(info);
writeb(ch, &info->regs->w.tic);
restore_flags(flags);
}
/* /*
* ------------------------------------------------------------ * Finally, enable interrupts
* sab82532_throttle()
*
* This routine is called by the upper-layer tty layer to signal that
* incoming characters should be throttled.
* ------------------------------------------------------------
*/ */
static void sab82532_throttle(struct tty_struct * tty) up->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
{ SAB82532_IMR0_PLLA | SAB82532_IMR0_CDSC;
struct sab82532 *info = (struct sab82532 *)tty->driver_data; writeb(up->interrupt_mask0, &up->regs->w.imr0);
#ifdef SERIAL_DEBUG_THROTTLE up->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS |
char buf[64]; SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN |
SAB82532_IMR1_CSC | SAB82532_IMR1_XON |
printk("throttle %s: %d....\n", _tty_name(tty, buf), SAB82532_IMR1_XPR;
tty->ldisc.chars_in_buffer(tty)); writeb(up->interrupt_mask1, &up->regs->w.imr1);
#endif
if (serial_paranoia_check(info, tty->device, "sab82532_throttle")) sunsab_convert_to_sab(up, con->cflag, 0, baud);
return;
if (I_IXOFF(tty)) spin_unlock_irqrestore(&up->port.lock, flags);
sab82532_send_xchar(tty, STOP_CHAR(tty));
if (tty->termios->c_cflag & CRTSCTS) { return 0;
u8 mode = readb(&info->regs->r.mode);
mode &= ~(SAB82532_MODE_FRTS | SAB82532_MODE_RTS);
writeb(mode, &info->regs->w.mode);
}
} }
static void sab82532_unthrottle(struct tty_struct * tty) static struct console sunsab_console = {
{ .name = "ttyS",
struct sab82532 *info = (struct sab82532 *)tty->driver_data; .write = sunsab_console_write,
#ifdef SERIAL_DEBUG_THROTTLE .device = sunsab_console_device,
char buf[64]; .setup = sunsab_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
};
printk("unthrottle %s: %d....\n", _tty_name(tty, buf), static void __init sunsab_console_init(void)
tty->ldisc.chars_in_buffer(tty)); {
#endif int i;
if (serial_paranoia_check(info, tty->device, "sab82532_unthrottle")) if (con_is_present())
return; return;
if (I_IXOFF(tty)) { for (i = 0; i < num_channels; i++) {
if (info->x_char) int this_minor = sunsab_reg.minor + i;
info->x_char = 0;
else
sab82532_send_xchar(tty, START_CHAR(tty));
}
if (tty->termios->c_cflag & CRTSCTS) { if ((this_minor - 64) == (serial_console - 1))
u8 mode = readb(&info->regs->r.mode); break;
mode &= ~(SAB82532_MODE_RTS);
mode |= SAB82532_MODE_FRTS;
writeb(mode, &info->regs->w.mode);
} }
} if (i == num_channels)
return;
/*
* ------------------------------------------------------------
* sab82532_ioctl() and friends
* ------------------------------------------------------------
*/
static int get_serial_info(struct sab82532 *info, sunsab_console.index = i;
struct serial_struct *retinfo) register_console(&sunsab_console);
{
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;
} }
static int set_serial_info(struct sab82532 *info, static void __init for_each_sab_edev(void (*callback)(struct linux_ebus_device *, void *), void *arg)
struct serial_struct *new_info)
{ {
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;
/* /* On RIO this can be an SE, check it. We could
* get_lsr_info - get line status register info * just check ebus->is_rio, but this is more portable.
*
* 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.
*/ */
static int get_lsr_info(struct sab82532 * info, unsigned int *value) clen = prom_getproperty(edev->prom_node, "compatible",
{ compat, sizeof(compat));
unsigned int result; if (clen > 0) {
if (strncmp(compat, "sab82532", 8) == 0) {
result = (!info->xmit.buf && test_bit(SAB82532_ALLS, &info->irqflags)) callback(edev, arg);
? TIOCSER_TEMT : 0; continue;
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);
} }
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;
} }
/* static void __init sab_count_callback(struct linux_ebus_device *edev, void *arg)
* This routine sends a break character out the serial port.
*/
static void sab82532_break(struct tty_struct *tty, int break_state)
{ {
struct sab82532 * info = (struct sab82532 *)tty->driver_data; int *count_p = arg;
unsigned long flags;
if (serial_paranoia_check(info, tty->device, "sab82532_break")) (*count_p)++;
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;
} }
/* static void __init sab_attach_callback(struct linux_ebus_device *edev, void *arg)
* /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)
{ {
struct linux_ebus *ebus; int *instance_p = arg;
struct linux_ebus_device *edev = 0; struct uart_sunsab_port *up;
struct sab82532 *sab;
unsigned long regs, offset; unsigned long regs, offset;
int i; 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; regs = edev->resource[0].start;
offset = sizeof(union sab82532_async_regs); offset = sizeof(union sab82532_async_regs);
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
if (memory_start) { up = &sunsab_ports[(*instance_p * 2) + i];
*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;
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; writeb(SAB82532_IPC_IC_ACT_LOW, &up->regs->w.ipc);
sab82532_chain = sab;
offset -= sizeof(union sab82532_async_regs); offset -= sizeof(union sab82532_async_regs);
} }
return 0;
} }
#ifndef MODULE static int __init probe_for_sabs(void)
static void __init sab82532_kgdb_hook(int line)
{ {
prom_printf("sab82532: kgdb support is not implemented, yet\n"); int this_sab = 0;
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;
/* /* Find device instances. */
* The serial driver boot-time initialization code! for_each_sab_edev(&sab_count_callback, &this_sab);
*/ if (!this_sab)
int __init sab82532_init(void)
{
struct sab82532 *info;
int i;
if (!sab82532_chain)
get_sab82532(0);
if (!sab82532_chain)
return -ENODEV; return -ENODEV;
init_bh(SERIAL_BH, do_serial_bh); /* Allocate tables. */
sunsab_ports = kmalloc(sizeof(struct uart_sunsab_port) * this_sab * 2,
show_serial_version(); GFP_KERNEL);
if (!sunsab_ports)
/* Initialize the tty_driver structure */ return -ENOMEM;
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");
}
}
printk(KERN_INFO num_channels = this_sab * 2;
"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]);
}
#ifdef SERIAL_LOG_DEVICE this_sab = 0;
dprint_init(SERIAL_LOG_DEVICE); for_each_sab_edev(&sab_attach_callback, &this_sab);
#endif
return 0; return 0;
} }
int __init sab82532_probe(void) static void __init sunsab_init_hw(void)
{ {
int node, enode, snode; int i;
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");
}
/* for (i = 0; i < num_channels; i++) {
* For each PCI bus... struct uart_sunsab_port *up = &sunsab_ports[i];
*/
while (node) {
enode = prom_getchild(node);
enode = prom_searchsiblings(enode, "ebus");
/* up->port.line = i;
* For each EBus on this PCI... up->port.ops = &sunsab_pops;
*/ up->port.type = PORT_SUNSAB;
while (enode) { up->port.uartclk = SAB_BASE_BAUD;
int child;
child = prom_getchild(enode); up->type = readb(&up->regs->r.vstr) & 0x0f;
snode = prom_searchsiblings(child, "se"); writeb(~((1 << 1) | (1 << 2) | (1 << 4)), &up->regs->w.pcr);
if (snode) writeb(0xff, &up->regs->w.pim);
goto found; 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"); up->tec_timeout = SAB82532_MAX_TEC_TIMEOUT;
if (snode) { up->cec_timeout = SAB82532_MAX_CEC_TIMEOUT;
char compat[32];
int clen;
clen = prom_getproperty(snode, "compatible", if (!(up->sab_line & 0x01)) {
compat, sizeof(compat)); if (request_irq(up->irq, sunsab_interrupt, SA_SHIRQ,
if (clen > 0) { "serial(sab82532)", up)) {
if (strncmp(compat, "sab82532", 8) == 0) printk("sunsab%d: can't get IRQ %x\n",
goto found; i, up->irq);
continue;
} }
} }
enode = prom_getsibling(enode); printk(KERN_INFO
enode = prom_searchsiblings(enode, "ebus"); "sunsab%d at 0x%lx (irq = %s) is a SAB82532 %s\n",
} i, (unsigned long)up->regs,
node = prom_getsibling(node); __irq_itoa(up->irq), sab82532_version[up->type]);
node = prom_searchsiblings(node, "pci");
} }
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 static int __init sunsab_init(void)
MODULE_LICENSE("GPL");
int init_module(void)
{ {
if (get_sab82532(0)) int ret = probe_for_sabs();
return -ENODEV; int i;
return sab82532_init(); if (ret < 0)
} return ret;
void cleanup_module(void) sunsab_init_hw();
{
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 */
#ifdef CONFIG_SERIAL_CONSOLE sunsab_reg.minor = sunserial_current_minor;
static __inline__ void sunserial_current_minor += num_channels;
sab82532_console_putchar(struct sab82532 *info, char c)
{
unsigned long flags;
save_flags(flags); cli(); sunsab_reg.nr = num_channels;
sab82532_tec_wait(info); sunsab_reg.cons = &sunsab_console;
writeb(c, &info->regs->w.tic);
restore_flags(flags);
}
static void ret = uart_register_driver(&sunsab_reg);
sab82532_console_write(struct console *con, const char *s, unsigned n) if (ret < 0) {
{
struct sab82532 *info;
int i; int i;
info = sab82532_chain; for (i = 0; i < num_channels; i++) {
for (i = con->index; i; i--) { struct uart_sunsab_port *up = &sunsab_ports[i];
info = info->next;
if (!info)
return;
}
for (i = 0; i < n; i++) { if (!(up->sab_line & 0x01))
if (*s == '\n') free_irq(up->irq, up);
sab82532_console_putchar(info, '\r'); iounmap(up->regs);
sab82532_console_putchar(info, *s++);
} }
sab82532_tec_wait(info); kfree(sunsab_ports);
} sunsab_ports = NULL;
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;
info = sab82532_chain; return ret;
for (i = con->index; i; i--) {
info = info->next;
if (!info)
return -ENODEV;
} }
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); for (i = 0; i < num_channels; i++) {
cflag = con->cflag; struct uart_sunsab_port *up = &sunsab_ports[i];
/* uart_add_one_port(&sunsab_reg, &up->port);
* 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;
} }
info->tty->termios->c_cflag = con->cflag;
change_speed(info); sunsab_console_init();
/* 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;
return 0; return 0;
} }
static struct console sab82532_console = { static void __exit sunsab_exit(void)
.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)
{ {
extern int su_console_registered; int i;
if (con_is_present() || su_console_registered) for (i = 0; i < num_channels; i++) {
return 0; struct uart_sunsab_port *up = &sunsab_ports[i];
if (!sab82532_chain) { uart_remove_one_port(&sunsab_reg, &up->port);
prom_printf("sab82532_console_setup: can't get SAB82532 chain");
prom_halt();
}
sab82532_console.index = serial_console - 1; if (!(up->sab_line & 0x01))
register_console(&sab82532_console); free_irq(up->irq, up);
return 0; iounmap(up->regs);
} }
#ifdef SERIAL_LOG_DEVICE
static int serial_log_device = 0; uart_unregister_driver(&sunsab_reg);
static void kfree(sunsab_ports);
dprint_init(int tty) sunsab_ports = NULL;
{
serial_console = tty + 1;
sab82532_console.index = tty;
sab82532_console_setup(&sab82532_console, "");
serial_console = 0;
serial_log_device = tty + 1;
} }
int module_init(sunsab_init);
dprintf(const char *fmt, ...) module_exit(sunsab_exit);
{
static char buffer[4096];
va_list args;
int i;
if (!serial_log_device)
return 0;
va_start(args, fmt); MODULE_AUTHOR("Eddie C. Dost and David S. Miller");
i = vsprintf(buffer, fmt, args); MODULE_DESCRIPTION("Sun SAB82532 serial port driver");
va_end(args); MODULE_LICENSE("GPL");
sab82532_console.write(&sab82532_console, buffer, i);
return i;
}
#endif /* SERIAL_LOG_DEVICE */
#endif /* CONFIG_SERIAL_CONSOLE */
/* $Id: sab82532.h,v 1.7 2001/05/23 23:09:10 ecd Exp $ /* sunsab.h: Register Definitions for the Siemens SAB82532 DUSCC
* sab82532.h: Register Definitions for the Siemens SAB82532 DUSCC
* *
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
*/ */
#ifndef _SPARC64_SAB82532_H #ifndef _SUNSAB_H
#define _SPARC64_SAB82532_H #define _SUNSAB_H
#include <linux/types.h>
#include <linux/serial.h>
#include <linux/circ_buf.h>
struct sab82532_async_rd_regs { struct sab82532_async_rd_regs {
u8 rfifo[0x20]; /* Receive FIFO */ u8 rfifo[0x20]; /* Receive FIFO */
...@@ -120,8 +115,6 @@ union sab82532_async_regs { ...@@ -120,8 +115,6 @@ union sab82532_async_regs {
__volatile__ struct sab82532_async_rw_regs rw; __volatile__ struct sab82532_async_rw_regs rw;
}; };
#define NR_PORTS 2
union sab82532_irq_status { union sab82532_irq_status {
unsigned short stat; unsigned short stat;
struct { struct {
...@@ -130,62 +123,10 @@ union sab82532_irq_status { ...@@ -130,62 +123,10 @@ union sab82532_irq_status {
} sreg; } 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 */ /* irqflags bits */
#define SAB82532_ALLS 0x00000001 #define SAB82532_ALLS 0x00000001
#define SAB82532_XPR 0x00000002 #define SAB82532_XPR 0x00000002
/* RFIFO Status Byte */ /* RFIFO Status Byte */
#define SAB82532_RSTAT_PE 0x80 #define SAB82532_RSTAT_PE 0x80
#define SAB82532_RSTAT_FE 0x40 #define SAB82532_RSTAT_FE 0x40
...@@ -377,4 +318,4 @@ struct sab82532 { ...@@ -377,4 +318,4 @@ struct sab82532 {
#define SAB82532_CCR4_ICD 0x10 #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, ...@@ -1362,7 +1362,7 @@ static void sunsu_console_write(struct console *co, const char *s,
static kdev_t sunsu_console_device(struct console *co) 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) ...@@ -1379,6 +1379,9 @@ static int __init sunsu_console_setup(struct console *co, char *options)
int parity = 'n'; int parity = 'n';
int flow = '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 * Check whether an invalid uart number has been specified, and
* if so, search for the first available port that does have * if so, search for the first available port that does have
...@@ -1414,15 +1417,23 @@ static struct console sunsu_cons = { ...@@ -1414,15 +1417,23 @@ static struct console sunsu_cons = {
static int __init sunsu_serial_console_init(void) static int __init sunsu_serial_console_init(void)
{ {
int index; int i;
if (con_is_present()) if (con_is_present())
return 0; return 0;
index = serial_console - 1; for (i = 0; i < UART_NR; i++) {
if (sunsu_ports[index].port_node == 0) int this_minor = sunsu_reg.minor + i;
if ((this_minor - 64) == (serial_console - 1))
break;
}
if (i == UART_NR)
return 0; return 0;
sunsu_cons.index = index; if (sunsu_ports[i].port_node == 0)
return 0;
sunsu_cons.index = i;
register_console(&sunsu_cons); register_console(&sunsu_cons);
return 0; return 0;
} }
......
...@@ -1013,7 +1013,6 @@ static struct uart_driver sunzilog_reg = { ...@@ -1013,7 +1013,6 @@ static struct uart_driver sunzilog_reg = {
.dev_name = "ttyS", .dev_name = "ttyS",
#endif #endif
.major = TTY_MAJOR, .major = TTY_MAJOR,
.minor = 64,
}; };
static void * __init alloc_one_table(unsigned long size) 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) ...@@ -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) 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) 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) ...@@ -1395,7 +1394,8 @@ static int __init sunzilog_console_setup(struct console *con, char *options)
unsigned long flags; unsigned long flags;
int baud, brg; 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. */ /* Get firmware console settings. */
sunserial_console_termios(con); sunserial_console_termios(con);
...@@ -1447,10 +1447,21 @@ static struct console sunzilog_console = { ...@@ -1447,10 +1447,21 @@ static struct console sunzilog_console = {
static int __init sunzilog_console_init(void) static int __init sunzilog_console_init(void)
{ {
int i;
if (con_is_present()) if (con_is_present())
return 0; 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); register_console(&sunzilog_console);
return 0; return 0;
} }
...@@ -1480,6 +1491,7 @@ static void __init sunzilog_prepare(void) ...@@ -1480,6 +1491,7 @@ static void __init sunzilog_prepare(void)
up[(chip * 2) + 0].port.uartclk = ZS_CLOCK; up[(chip * 2) + 0].port.uartclk = ZS_CLOCK;
up[(chip * 2) + 0].port.fifosize = 1; up[(chip * 2) + 0].port.fifosize = 1;
up[(chip * 2) + 0].port.ops = &sunzilog_pops; 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.flags = 0;
up[(chip * 2) + 0].port.line = (chip * 2) + 0; up[(chip * 2) + 0].port.line = (chip * 2) + 0;
up[(chip * 2) + 0].flags |= SUNZILOG_FLAG_IS_CHANNEL_A; up[(chip * 2) + 0].flags |= SUNZILOG_FLAG_IS_CHANNEL_A;
...@@ -1490,6 +1502,7 @@ static void __init sunzilog_prepare(void) ...@@ -1490,6 +1502,7 @@ static void __init sunzilog_prepare(void)
up[(chip * 2) + 1].port.uartclk = ZS_CLOCK; up[(chip * 2) + 1].port.uartclk = ZS_CLOCK;
up[(chip * 2) + 1].port.fifosize = 1; up[(chip * 2) + 1].port.fifosize = 1;
up[(chip * 2) + 1].port.ops = &sunzilog_pops; 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.flags = 0;
up[(chip * 2) + 1].port.line = (chip * 2) + 1; up[(chip * 2) + 1].port.line = (chip * 2) + 1;
up[(chip * 2) + 1].flags |= 0; up[(chip * 2) + 1].flags |= 0;
...@@ -1607,12 +1620,10 @@ static int __init sunzilog_ports_init(void) ...@@ -1607,12 +1620,10 @@ static int __init sunzilog_ports_init(void)
* in the system. * in the system.
*/ */
sunzilog_reg.nr = NUM_CHANNELS; sunzilog_reg.nr = NUM_CHANNELS;
#ifdef CONFIG_SERIAL_CONSOLE
sunzilog_reg.cons = &sunzilog_console; sunzilog_reg.cons = &sunzilog_console;
#else
sunzilog_reg.cons = NULL; sunzilog_reg.minor = sunserial_current_minor;
#endif sunserial_current_minor += NUM_CHANNELS;
ret = uart_register_driver(&sunzilog_reg); ret = uart_register_driver(&sunzilog_reg);
if (ret == 0) { if (ret == 0) {
......
...@@ -78,9 +78,13 @@ ...@@ -78,9 +78,13 @@
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <linux/spinlock.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/io.h> #include <asm/io.h>
extern spinlock_t ns87303_lock;
static __inline__ int ns87303_modify(unsigned long port, unsigned int index, static __inline__ int ns87303_modify(unsigned long port, unsigned int index,
unsigned char clr, unsigned char set) unsigned char clr, unsigned char set)
{ {
...@@ -96,14 +100,16 @@ static __inline__ int ns87303_modify(unsigned long port, unsigned int index, ...@@ -96,14 +100,16 @@ static __inline__ int ns87303_modify(unsigned long port, unsigned int index,
if (index > 0x0d) if (index > 0x0d)
return -EINVAL; return -EINVAL;
save_flags(flags); cli(); spin_lock_irqsave(&ns87303_lock, flags);
outb(index, port); outb(index, port);
value = inb(port + 1); value = inb(port + 1);
value &= ~(reserved[index] | clr); value &= ~(reserved[index] | clr);
value |= set; value |= set;
outb(value, port + 1); outb(value, port + 1);
outb(value, port + 1); outb(value, port + 1);
restore_flags(flags);
spin_unlock_irqrestore(&ns87303_lock, flags);
return 0; return 0;
} }
......
...@@ -51,6 +51,10 @@ ...@@ -51,6 +51,10 @@
#define PORT_UART00 35 #define PORT_UART00 35
#define PORT_21285 37 #define PORT_21285 37
/* Sparc type numbers. */
#define PORT_SUNZILOG 38
#define PORT_SUNSAB 39
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <linux/config.h> #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