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