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
b86c69e1
Commit
b86c69e1
authored
Jul 25, 2002
by
Russell King
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[SERIAL] Remove drivers/char/serial_{21285,amba}.c
These drivers are now part of drivers/serial
parent
62263c4e
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
0 additions
and
2511 deletions
+0
-2511
drivers/char/serial_21285.c
drivers/char/serial_21285.c
+0
-497
drivers/char/serial_amba.c
drivers/char/serial_amba.c
+0
-2014
No files found.
drivers/char/serial_21285.c
deleted
100644 → 0
View file @
62263c4e
/*
* linux/drivers/char/serial_21285.c
*
* Driver for the serial port on the 21285 StrongArm-110 core logic chip.
*
* Based on drivers/char/serial.c
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/major.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/console.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/dec21285.h>
#include <asm/hardware.h>
#define BAUD_BASE (mem_fclk_21285/64)
#define SERIAL_21285_NAME "ttyFB"
#define SERIAL_21285_MAJOR 204
#define SERIAL_21285_MINOR 4
#define SERIAL_21285_AUXNAME "cuafb"
#define SERIAL_21285_AUXMAJOR 205
#define SERIAL_21285_AUXMINOR 4
static
struct
tty_driver
rs285_driver
,
callout_driver
;
static
int
rs285_refcount
;
static
struct
tty_struct
*
rs285_table
[
1
];
static
struct
termios
*
rs285_termios
[
1
];
static
struct
termios
*
rs285_termios_locked
[
1
];
static
char
wbuf
[
1000
],
*
putp
=
wbuf
,
*
getp
=
wbuf
,
x_char
;
static
struct
tty_struct
*
rs285_tty
;
static
int
rs285_use_count
;
static
int
rs285_write_room
(
struct
tty_struct
*
tty
)
{
return
putp
>=
getp
?
(
sizeof
(
wbuf
)
-
(
long
)
putp
+
(
long
)
getp
)
:
((
long
)
getp
-
(
long
)
putp
-
1
);
}
static
void
rs285_rx_int
(
int
irq
,
void
*
dev_id
,
struct
pt_regs
*
regs
)
{
if
(
!
rs285_tty
)
{
disable_irq
(
IRQ_CONRX
);
return
;
}
while
(
!
(
*
CSR_UARTFLG
&
0x10
))
{
int
ch
,
flag
;
ch
=
*
CSR_UARTDR
;
flag
=
*
CSR_RXSTAT
;
if
(
flag
&
4
)
tty_insert_flip_char
(
rs285_tty
,
0
,
TTY_OVERRUN
);
if
(
flag
&
2
)
flag
=
TTY_PARITY
;
else
if
(
flag
&
1
)
flag
=
TTY_FRAME
;
tty_insert_flip_char
(
rs285_tty
,
ch
,
flag
);
}
tty_flip_buffer_push
(
rs285_tty
);
}
static
void
rs285_send_xchar
(
struct
tty_struct
*
tty
,
char
ch
)
{
x_char
=
ch
;
enable_irq
(
IRQ_CONTX
);
}
static
void
rs285_throttle
(
struct
tty_struct
*
tty
)
{
if
(
I_IXOFF
(
tty
))
rs285_send_xchar
(
tty
,
STOP_CHAR
(
tty
));
}
static
void
rs285_unthrottle
(
struct
tty_struct
*
tty
)
{
if
(
I_IXOFF
(
tty
))
{
if
(
x_char
)
x_char
=
0
;
else
rs285_send_xchar
(
tty
,
START_CHAR
(
tty
));
}
}
static
void
rs285_tx_int
(
int
irq
,
void
*
dev_id
,
struct
pt_regs
*
regs
)
{
while
(
!
(
*
CSR_UARTFLG
&
0x20
))
{
if
(
x_char
)
{
*
CSR_UARTDR
=
x_char
;
x_char
=
0
;
continue
;
}
if
(
putp
==
getp
)
{
disable_irq
(
IRQ_CONTX
);
break
;
}
*
CSR_UARTDR
=
*
getp
;
if
(
++
getp
>=
wbuf
+
sizeof
(
wbuf
))
getp
=
wbuf
;
}
if
(
rs285_tty
)
wake_up_interruptible
(
&
rs285_tty
->
write_wait
);
}
static
inline
int
rs285_xmit
(
int
ch
)
{
if
(
putp
+
1
==
getp
||
(
putp
+
1
==
wbuf
+
sizeof
(
wbuf
)
&&
getp
==
wbuf
))
return
0
;
*
putp
=
ch
;
if
(
++
putp
>=
wbuf
+
sizeof
(
wbuf
))
putp
=
wbuf
;
enable_irq
(
IRQ_CONTX
);
return
1
;
}
static
int
rs285_write
(
struct
tty_struct
*
tty
,
int
from_user
,
const
u_char
*
buf
,
int
count
)
{
int
i
;
if
(
from_user
&&
verify_area
(
VERIFY_READ
,
buf
,
count
))
return
-
EINVAL
;
for
(
i
=
0
;
i
<
count
;
i
++
)
{
char
ch
;
if
(
from_user
)
__get_user
(
ch
,
buf
+
i
);
else
ch
=
buf
[
i
];
if
(
!
rs285_xmit
(
ch
))
break
;
}
return
i
;
}
static
void
rs285_put_char
(
struct
tty_struct
*
tty
,
u_char
ch
)
{
rs285_xmit
(
ch
);
}
static
int
rs285_chars_in_buffer
(
struct
tty_struct
*
tty
)
{
return
sizeof
(
wbuf
)
-
rs285_write_room
(
tty
);
}
static
void
rs285_flush_buffer
(
struct
tty_struct
*
tty
)
{
disable_irq
(
IRQ_CONTX
);
putp
=
getp
=
wbuf
;
if
(
x_char
)
enable_irq
(
IRQ_CONTX
);
}
static
inline
void
rs285_set_cflag
(
int
cflag
)
{
int
h_lcr
,
baud
,
quot
;
switch
(
cflag
&
CSIZE
)
{
case
CS5
:
h_lcr
=
0x10
;
break
;
case
CS6
:
h_lcr
=
0x30
;
break
;
case
CS7
:
h_lcr
=
0x50
;
break
;
default:
/* CS8 */
h_lcr
=
0x70
;
break
;
}
if
(
cflag
&
CSTOPB
)
h_lcr
|=
0x08
;
if
(
cflag
&
PARENB
)
h_lcr
|=
0x02
;
if
(
!
(
cflag
&
PARODD
))
h_lcr
|=
0x04
;
switch
(
cflag
&
CBAUD
)
{
case
B200
:
baud
=
200
;
break
;
case
B300
:
baud
=
300
;
break
;
case
B1200
:
baud
=
1200
;
break
;
case
B1800
:
baud
=
1800
;
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
;
case
B57600
:
baud
=
57600
;
break
;
case
B115200
:
baud
=
115200
;
break
;
}
/*
* The documented expression for selecting the divisor is:
* BAUD_BASE / baud - 1
* However, typically BAUD_BASE is not divisible by baud, so
* we want to select the divisor that gives us the minimum
* error. Therefore, we want:
* int(BAUD_BASE / baud - 0.5) ->
* int(BAUD_BASE / baud - (baud >> 1) / baud) ->
* int((BAUD_BASE - (baud >> 1)) / baud)
*/
quot
=
(
BAUD_BASE
-
(
baud
>>
1
))
/
baud
;
*
CSR_UARTCON
=
0
;
*
CSR_L_UBRLCR
=
quot
&
0xff
;
*
CSR_M_UBRLCR
=
(
quot
>>
8
)
&
0x0f
;
*
CSR_H_UBRLCR
=
h_lcr
;
*
CSR_UARTCON
=
1
;
}
static
void
rs285_set_termios
(
struct
tty_struct
*
tty
,
struct
termios
*
old
)
{
if
(
old
&&
tty
->
termios
->
c_cflag
==
old
->
c_cflag
)
return
;
rs285_set_cflag
(
tty
->
termios
->
c_cflag
);
}
static
void
rs285_stop
(
struct
tty_struct
*
tty
)
{
disable_irq
(
IRQ_CONTX
);
}
static
void
rs285_start
(
struct
tty_struct
*
tty
)
{
enable_irq
(
IRQ_CONTX
);
}
static
void
rs285_wait_until_sent
(
struct
tty_struct
*
tty
,
int
timeout
)
{
int
orig_jiffies
=
jiffies
;
while
(
*
CSR_UARTFLG
&
8
)
{
current
->
state
=
TASK_INTERRUPTIBLE
;
schedule_timeout
(
1
);
if
(
signal_pending
(
current
))
break
;
if
(
timeout
&&
time_after
(
jiffies
,
orig_jiffies
+
timeout
))
break
;
}
current
->
state
=
TASK_RUNNING
;
}
static
int
rs285_open
(
struct
tty_struct
*
tty
,
struct
file
*
filp
)
{
int
line
;
MOD_INC_USE_COUNT
;
line
=
minor
(
tty
->
device
)
-
tty
->
driver
.
minor_start
;
if
(
line
)
{
MOD_DEC_USE_COUNT
;
return
-
ENODEV
;
}
tty
->
driver_data
=
NULL
;
if
(
!
rs285_tty
)
rs285_tty
=
tty
;
enable_irq
(
IRQ_CONRX
);
rs285_use_count
++
;
return
0
;
}
static
void
rs285_close
(
struct
tty_struct
*
tty
,
struct
file
*
filp
)
{
if
(
!--
rs285_use_count
)
{
rs285_wait_until_sent
(
tty
,
0
);
disable_irq
(
IRQ_CONRX
);
disable_irq
(
IRQ_CONTX
);
rs285_tty
=
NULL
;
}
MOD_DEC_USE_COUNT
;
}
static
int
__init
rs285_init
(
void
)
{
int
baud
=
B9600
;
if
(
machine_is_personal_server
())
baud
=
B57600
;
rs285_driver
.
magic
=
TTY_DRIVER_MAGIC
;
rs285_driver
.
driver_name
=
"serial_21285"
;
rs285_driver
.
name
=
SERIAL_21285_NAME
;
rs285_driver
.
major
=
SERIAL_21285_MAJOR
;
rs285_driver
.
minor_start
=
SERIAL_21285_MINOR
;
rs285_driver
.
num
=
1
;
rs285_driver
.
type
=
TTY_DRIVER_TYPE_SERIAL
;
rs285_driver
.
subtype
=
SERIAL_TYPE_NORMAL
;
rs285_driver
.
init_termios
=
tty_std_termios
;
rs285_driver
.
init_termios
.
c_cflag
=
baud
|
CS8
|
CREAD
|
HUPCL
|
CLOCAL
;
rs285_driver
.
flags
=
TTY_DRIVER_REAL_RAW
;
rs285_driver
.
refcount
=
&
rs285_refcount
;
rs285_driver
.
table
=
rs285_table
;
rs285_driver
.
termios
=
rs285_termios
;
rs285_driver
.
termios_locked
=
rs285_termios_locked
;
rs285_driver
.
open
=
rs285_open
;
rs285_driver
.
close
=
rs285_close
;
rs285_driver
.
write
=
rs285_write
;
rs285_driver
.
put_char
=
rs285_put_char
;
rs285_driver
.
write_room
=
rs285_write_room
;
rs285_driver
.
chars_in_buffer
=
rs285_chars_in_buffer
;
rs285_driver
.
flush_buffer
=
rs285_flush_buffer
;
rs285_driver
.
throttle
=
rs285_throttle
;
rs285_driver
.
unthrottle
=
rs285_unthrottle
;
rs285_driver
.
send_xchar
=
rs285_send_xchar
;
rs285_driver
.
set_termios
=
rs285_set_termios
;
rs285_driver
.
stop
=
rs285_stop
;
rs285_driver
.
start
=
rs285_start
;
rs285_driver
.
wait_until_sent
=
rs285_wait_until_sent
;
callout_driver
=
rs285_driver
;
callout_driver
.
name
=
SERIAL_21285_AUXNAME
;
callout_driver
.
major
=
SERIAL_21285_AUXMAJOR
;
callout_driver
.
subtype
=
SERIAL_TYPE_CALLOUT
;
if
(
request_irq
(
IRQ_CONRX
,
rs285_rx_int
,
0
,
"rs285"
,
NULL
))
panic
(
"Couldn't get rx irq for rs285"
);
if
(
request_irq
(
IRQ_CONTX
,
rs285_tx_int
,
0
,
"rs285"
,
NULL
))
panic
(
"Couldn't get tx irq for rs285"
);
if
(
tty_register_driver
(
&
rs285_driver
))
printk
(
KERN_ERR
"Couldn't register 21285 serial driver
\n
"
);
if
(
tty_register_driver
(
&
callout_driver
))
printk
(
KERN_ERR
"Couldn't register 21285 callout driver
\n
"
);
return
0
;
}
static
void
__exit
rs285_fini
(
void
)
{
unsigned
long
flags
;
int
ret
;
save_flags
(
flags
);
cli
();
ret
=
tty_unregister_driver
(
&
callout_driver
);
if
(
ret
)
printk
(
KERN_ERR
"Unable to unregister 21285 callout driver "
"(%d)
\n
"
,
ret
);
ret
=
tty_unregister_driver
(
&
rs285_driver
);
if
(
ret
)
printk
(
KERN_ERR
"Unable to unregister 21285 driver (%d)
\n
"
,
ret
);
free_irq
(
IRQ_CONTX
,
NULL
);
free_irq
(
IRQ_CONRX
,
NULL
);
restore_flags
(
flags
);
}
module_init
(
rs285_init
);
module_exit
(
rs285_fini
);
#ifdef CONFIG_SERIAL_21285_CONSOLE
/************** console driver *****************/
static
void
rs285_console_write
(
struct
console
*
co
,
const
char
*
s
,
u_int
count
)
{
int
i
;
disable_irq
(
IRQ_CONTX
);
for
(
i
=
0
;
i
<
count
;
i
++
)
{
while
(
*
CSR_UARTFLG
&
0x20
);
*
CSR_UARTDR
=
s
[
i
];
if
(
s
[
i
]
==
'\n'
)
{
while
(
*
CSR_UARTFLG
&
0x20
);
*
CSR_UARTDR
=
'\r'
;
}
}
enable_irq
(
IRQ_CONTX
);
}
static
kdev_t
rs285_console_device
(
struct
console
*
c
)
{
return
mk_kdev
(
SERIAL_21285_MAJOR
,
SERIAL_21285_MINOR
);
}
static
int
__init
rs285_console_setup
(
struct
console
*
co
,
char
*
options
)
{
int
baud
=
9600
;
int
bits
=
8
;
int
parity
=
'n'
;
int
cflag
=
CREAD
|
HUPCL
|
CLOCAL
;
if
(
machine_is_personal_server
())
baud
=
57600
;
if
(
options
)
{
char
*
s
=
options
;
baud
=
simple_strtoul
(
options
,
NULL
,
10
);
while
(
*
s
>=
'0'
&&
*
s
<=
'9'
)
s
++
;
if
(
*
s
)
parity
=
*
s
++
;
if
(
*
s
)
bits
=
*
s
-
'0'
;
}
/*
* Now construct a cflag setting.
*/
switch
(
baud
)
{
case
1200
:
cflag
|=
B1200
;
break
;
case
2400
:
cflag
|=
B2400
;
break
;
case
4800
:
cflag
|=
B4800
;
break
;
case
9600
:
cflag
|=
B9600
;
break
;
case
19200
:
cflag
|=
B19200
;
break
;
case
38400
:
cflag
|=
B38400
;
break
;
case
57600
:
cflag
|=
B57600
;
break
;
case
115200
:
cflag
|=
B115200
;
break
;
default:
cflag
|=
B9600
;
break
;
}
switch
(
bits
)
{
case
7
:
cflag
|=
CS7
;
break
;
default:
cflag
|=
CS8
;
break
;
}
switch
(
parity
)
{
case
'o'
:
case
'O'
:
cflag
|=
PARODD
;
break
;
case
'e'
:
case
'E'
:
cflag
|=
PARENB
;
break
;
}
co
->
cflag
=
cflag
;
rs285_set_cflag
(
cflag
);
rs285_console_write
(
NULL
,
"\e[2J\e[Hboot "
,
12
);
if
(
options
)
rs285_console_write
(
NULL
,
options
,
strlen
(
options
));
else
rs285_console_write
(
NULL
,
"no options"
,
10
);
rs285_console_write
(
NULL
,
"
\n
"
,
1
);
return
0
;
}
static
struct
console
rs285_cons
=
{
name:
SERIAL_21285_NAME
,
write:
rs285_console_write
,
device:
rs285_console_device
,
setup:
rs285_console_setup
,
flags:
CON_PRINTBUFFER
,
index:
-
1
,
};
void
__init
rs285_console_init
(
void
)
{
register_console
(
&
rs285_cons
);
}
#endif
/* CONFIG_SERIAL_21285_CONSOLE */
MODULE_LICENSE
(
"GPL"
);
drivers/char/serial_amba.c
deleted
100644 → 0
View file @
62263c4e
/*
* linux/drivers/char/serial_amba.c
*
* Driver for AMBA serial ports
*
* Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
*
* Copyright 1999 ARM Limited
* Copyright (C) 2000 Deep Blue Solutions Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
* This is a generic driver for ARM AMBA-type serial ports. They
* have a lot of 16550-like features, but are not register compatable.
* Note that although they do have CTS, DCD and DSR inputs, they do
* not have an RI input, nor do they have DTR or RTS outputs. If
* required, these have to be supplied via some other means (eg, GPIO)
* and hooked into this driver.
*
* This could very easily become a generic serial driver for dumb UARTs
* (eg, {82,16x}50, 21285, SA1100).
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.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/slab.h>
#include <linux/init.h>
#include <linux/circ_buf.h>
#include <linux/serial.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>
#include <asm/hardware/serial_amba.h>
#define SERIAL_AMBA_NAME "ttyAM"
#define SERIAL_AMBA_MAJOR 204
#define SERIAL_AMBA_MINOR 16
#define SERIAL_AMBA_NR 2
#define CALLOUT_AMBA_NAME "cuaam"
#define CALLOUT_AMBA_MAJOR 205
#define CALLOUT_AMBA_MINOR 16
#define CALLOUT_AMBA_NR SERIAL_AMBA_NR
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#define DEBUG 0
#define DEBUG_LEDS 0
#if DEBUG_LEDS
extern
int
get_leds
(
void
);
extern
int
set_leds
(
int
);
#endif
/*
* Access routines for the AMBA UARTs
*/
#define UART_GET_INT_STATUS(p) IO_READ((p)->uart_base + AMBA_UARTIIR)
#define UART_GET_FR(p) IO_READ((p)->uart_base + AMBA_UARTFR)
#define UART_GET_CHAR(p) IO_READ((p)->uart_base + AMBA_UARTDR)
#define UART_PUT_CHAR(p, c) IO_WRITE((p)->uart_base + AMBA_UARTDR, (c))
#define UART_GET_RSR(p) IO_READ((p)->uart_base + AMBA_UARTRSR)
#define UART_GET_CR(p) IO_READ((p)->uart_base + AMBA_UARTCR)
#define UART_PUT_CR(p,c) IO_WRITE((p)->uart_base + AMBA_UARTCR, (c))
#define UART_GET_LCRL(p) IO_READ((p)->uart_base + AMBA_UARTLCR_L)
#define UART_PUT_LCRL(p,c) IO_WRITE((p)->uart_base + AMBA_UARTLCR_L, (c))
#define UART_GET_LCRM(p) IO_READ((p)->uart_base + AMBA_UARTLCR_M)
#define UART_PUT_LCRM(p,c) IO_WRITE((p)->uart_base + AMBA_UARTLCR_M, (c))
#define UART_GET_LCRH(p) IO_READ((p)->uart_base + AMBA_UARTLCR_H)
#define UART_PUT_LCRH(p,c) IO_WRITE((p)->uart_base + AMBA_UARTLCR_H, (c))
#define UART_RX_DATA(s) (((s) & AMBA_UARTFR_RXFE) == 0)
#define UART_TX_READY(s) (((s) & AMBA_UARTFR_TXFF) == 0)
#define UART_TX_EMPTY(p) ((UART_GET_FR(p) & AMBA_UARTFR_TMSK) == 0)
#define AMBA_UARTRSR_ANY (AMBA_UARTRSR_OE|AMBA_UARTRSR_BE|AMBA_UARTRSR_PE|AMBA_UARTRSR_FE)
#define AMBA_UARTFR_MODEM_ANY (AMBA_UARTFR_DCD|AMBA_UARTFR_DSR|AMBA_UARTFR_CTS)
/*
* Things needed by tty driver
*/
static
struct
tty_driver
ambanormal_driver
,
ambacallout_driver
;
static
int
ambauart_refcount
;
static
struct
tty_struct
*
ambauart_table
[
SERIAL_AMBA_NR
];
static
struct
termios
*
ambauart_termios
[
SERIAL_AMBA_NR
];
static
struct
termios
*
ambauart_termios_locked
[
SERIAL_AMBA_NR
];
#if defined(CONFIG_SERIAL_AMBA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
/*
* Things needed internally to this driver
*/
/*
* tmp_buf is used as a temporary buffer by serial_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
u_char
*
tmp_buf
;
static
DECLARE_MUTEX
(
tmp_buf_sem
);
#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8)
/* number of characters left in xmit buffer before we ask for more */
#define WAKEUP_CHARS 256
#define AMBA_ISR_PASS_LIMIT 256
#define EVT_WRITE_WAKEUP 0
struct
amba_icount
{
__u32
cts
;
__u32
dsr
;
__u32
rng
;
__u32
dcd
;
__u32
rx
;
__u32
tx
;
__u32
frame
;
__u32
overrun
;
__u32
parity
;
__u32
brk
;
__u32
buf_overrun
;
};
/*
* Static information about the port
*/
struct
amba_port
{
unsigned
int
uart_base
;
unsigned
int
irq
;
unsigned
int
uartclk
;
unsigned
int
fifosize
;
unsigned
int
tiocm_support
;
void
(
*
set_mctrl
)(
struct
amba_port
*
,
u_int
mctrl
);
};
/*
* This is the state information which is persistent across opens
*/
struct
amba_state
{
struct
amba_icount
icount
;
unsigned
int
line
;
unsigned
int
close_delay
;
unsigned
int
closing_wait
;
unsigned
int
custom_divisor
;
unsigned
int
flags
;
struct
termios
normal_termios
;
struct
termios
callout_termios
;
int
count
;
struct
amba_info
*
info
;
};
#define AMBA_XMIT_SIZE 1024
/*
* This is the state information which is only valid when the port is open.
*/
struct
amba_info
{
struct
amba_port
*
port
;
struct
amba_state
*
state
;
struct
tty_struct
*
tty
;
unsigned
char
x_char
;
unsigned
char
old_status
;
unsigned
char
read_status_mask
;
unsigned
char
ignore_status_mask
;
struct
circ_buf
xmit
;
unsigned
int
flags
;
#ifdef SUPPORT_SYSRQ
unsigned
long
sysrq
;
#endif
unsigned
int
event
;
unsigned
int
timeout
;
unsigned
int
lcr_h
;
unsigned
int
mctrl
;
int
blocked_open
;
pid_t
session
;
pid_t
pgrp
;
struct
tasklet_struct
tlet
;
wait_queue_head_t
open_wait
;
wait_queue_head_t
close_wait
;
wait_queue_head_t
delta_msr_wait
;
};
#ifdef CONFIG_SERIAL_AMBA_CONSOLE
static
struct
console
ambauart_cons
;
#endif
static
void
ambauart_change_speed
(
struct
amba_info
*
info
,
struct
termios
*
old_termios
);
static
void
ambauart_wait_until_sent
(
struct
tty_struct
*
tty
,
int
timeout
);
#if 1 //def CONFIG_SERIAL_INTEGRATOR
static
void
amba_set_mctrl_null
(
struct
amba_port
*
port
,
u_int
mctrl
)
{
}
static
struct
amba_port
amba_ports
[
SERIAL_AMBA_NR
]
=
{
{
uart_base:
IO_ADDRESS
(
INTEGRATOR_UART0_BASE
),
irq:
IRQ_UARTINT0
,
uartclk:
14745600
,
fifosize:
8
,
set_mctrl:
amba_set_mctrl_null
,
},
{
uart_base:
IO_ADDRESS
(
INTEGRATOR_UART1_BASE
),
irq:
IRQ_UARTINT1
,
uartclk:
14745600
,
fifosize:
8
,
set_mctrl:
amba_set_mctrl_null
,
}
};
#endif
static
struct
amba_state
amba_state
[
SERIAL_AMBA_NR
];
static
void
ambauart_enable_rx_interrupt
(
struct
amba_info
*
info
)
{
unsigned
int
cr
;
cr
=
UART_GET_CR
(
info
->
port
);
cr
|=
AMBA_UARTCR_RIE
|
AMBA_UARTCR_RTIE
;
UART_PUT_CR
(
info
->
port
,
cr
);
}
static
void
ambauart_disable_rx_interrupt
(
struct
amba_info
*
info
)
{
unsigned
int
cr
;
cr
=
UART_GET_CR
(
info
->
port
);
cr
&=
~
(
AMBA_UARTCR_RIE
|
AMBA_UARTCR_RTIE
);
UART_PUT_CR
(
info
->
port
,
cr
);
}
static
void
ambauart_enable_tx_interrupt
(
struct
amba_info
*
info
)
{
unsigned
int
cr
;
cr
=
UART_GET_CR
(
info
->
port
);
cr
|=
AMBA_UARTCR_TIE
;
UART_PUT_CR
(
info
->
port
,
cr
);
}
static
void
ambauart_disable_tx_interrupt
(
struct
amba_info
*
info
)
{
unsigned
int
cr
;
cr
=
UART_GET_CR
(
info
->
port
);
cr
&=
~
AMBA_UARTCR_TIE
;
UART_PUT_CR
(
info
->
port
,
cr
);
}
static
void
ambauart_stop
(
struct
tty_struct
*
tty
)
{
struct
amba_info
*
info
=
tty
->
driver_data
;
unsigned
long
flags
;
save_flags
(
flags
);
cli
();
ambauart_disable_tx_interrupt
(
info
);
restore_flags
(
flags
);
}
static
void
ambauart_start
(
struct
tty_struct
*
tty
)
{
struct
amba_info
*
info
=
tty
->
driver_data
;
unsigned
long
flags
;
save_flags
(
flags
);
cli
();
if
(
info
->
xmit
.
head
!=
info
->
xmit
.
tail
&&
info
->
xmit
.
buf
)
ambauart_enable_tx_interrupt
(
info
);
restore_flags
(
flags
);
}
/*
* This routine is used by the interrupt handler to schedule
* processing in the software interrupt portion of the driver.
*/
static
void
ambauart_event
(
struct
amba_info
*
info
,
int
event
)
{
info
->
event
|=
1
<<
event
;
tasklet_schedule
(
&
info
->
tlet
);
}
static
void
#ifdef SUPPORT_SYSRQ
ambauart_rx_chars
(
struct
amba_info
*
info
,
struct
pt_regs
*
regs
)
#else
ambauart_rx_chars
(
struct
amba_info
*
info
)
#endif
{
struct
tty_struct
*
tty
=
info
->
tty
;
unsigned
int
status
,
ch
,
rsr
,
flg
,
ignored
=
0
;
struct
amba_icount
*
icount
=
&
info
->
state
->
icount
;
struct
amba_port
*
port
=
info
->
port
;
status
=
UART_GET_FR
(
port
);
while
(
UART_RX_DATA
(
status
))
{
ch
=
UART_GET_CHAR
(
port
);
if
(
tty
->
flip
.
count
>=
TTY_FLIPBUF_SIZE
)
goto
ignore_char
;
icount
->
rx
++
;
flg
=
TTY_NORMAL
;
/*
* Note that the error handling code is
* out of the main execution path
*/
rsr
=
UART_GET_RSR
(
port
);
if
(
rsr
&
AMBA_UARTRSR_ANY
)
goto
handle_error
;
#ifdef SUPPORT_SYSRQ
if
(
info
->
sysrq
)
{
if
(
ch
&&
time_before
(
jiffies
,
info
->
sysrq
))
{
handle_sysrq
(
ch
,
regs
,
NULL
,
NULL
);
info
->
sysrq
=
0
;
goto
ignore_char
;
}
info
->
sysrq
=
0
;
}
#endif
error_return:
*
tty
->
flip
.
flag_buf_ptr
++
=
flg
;
*
tty
->
flip
.
char_buf_ptr
++
=
ch
;
tty
->
flip
.
count
++
;
ignore_char:
status
=
UART_GET_FR
(
port
);
}
out:
tty_flip_buffer_push
(
tty
);
return
;
handle_error:
if
(
rsr
&
AMBA_UARTRSR_BE
)
{
rsr
&=
~
(
AMBA_UARTRSR_FE
|
AMBA_UARTRSR_PE
);
icount
->
brk
++
;
#ifdef SUPPORT_SYSRQ
if
(
info
->
state
->
line
==
ambauart_cons
.
index
)
{
if
(
!
info
->
sysrq
)
{
info
->
sysrq
=
jiffies
+
HZ
*
5
;
goto
ignore_char
;
}
}
#endif
}
else
if
(
rsr
&
AMBA_UARTRSR_PE
)
icount
->
parity
++
;
else
if
(
rsr
&
AMBA_UARTRSR_FE
)
icount
->
frame
++
;
if
(
rsr
&
AMBA_UARTRSR_OE
)
icount
->
overrun
++
;
if
(
rsr
&
info
->
ignore_status_mask
)
{
if
(
++
ignored
>
100
)
goto
out
;
goto
ignore_char
;
}
rsr
&=
info
->
read_status_mask
;
if
(
rsr
&
AMBA_UARTRSR_BE
)
flg
=
TTY_BREAK
;
else
if
(
rsr
&
AMBA_UARTRSR_PE
)
flg
=
TTY_PARITY
;
else
if
(
rsr
&
AMBA_UARTRSR_FE
)
flg
=
TTY_FRAME
;
if
(
rsr
&
AMBA_UARTRSR_OE
)
{
/*
* CHECK: does overrun affect the current character?
* ASSUMPTION: it does not.
*/
*
tty
->
flip
.
flag_buf_ptr
++
=
flg
;
*
tty
->
flip
.
char_buf_ptr
++
=
ch
;
tty
->
flip
.
count
++
;
if
(
tty
->
flip
.
count
>=
TTY_FLIPBUF_SIZE
)
goto
ignore_char
;
ch
=
0
;
flg
=
TTY_OVERRUN
;
}
#ifdef SUPPORT_SYSRQ
info
->
sysrq
=
0
;
#endif
goto
error_return
;
}
static
void
ambauart_tx_chars
(
struct
amba_info
*
info
)
{
struct
amba_port
*
port
=
info
->
port
;
int
count
;
if
(
info
->
x_char
)
{
UART_PUT_CHAR
(
port
,
info
->
x_char
);
info
->
state
->
icount
.
tx
++
;
info
->
x_char
=
0
;
return
;
}
if
(
info
->
xmit
.
head
==
info
->
xmit
.
tail
||
info
->
tty
->
stopped
||
info
->
tty
->
hw_stopped
)
{
ambauart_disable_tx_interrupt
(
info
);
return
;
}
count
=
port
->
fifosize
;
do
{
UART_PUT_CHAR
(
port
,
info
->
xmit
.
buf
[
info
->
xmit
.
tail
]);
info
->
xmit
.
tail
=
(
info
->
xmit
.
tail
+
1
)
&
(
AMBA_XMIT_SIZE
-
1
);
info
->
state
->
icount
.
tx
++
;
if
(
info
->
xmit
.
head
==
info
->
xmit
.
tail
)
break
;
}
while
(
--
count
>
0
);
if
(
CIRC_CNT
(
info
->
xmit
.
head
,
info
->
xmit
.
tail
,
AMBA_XMIT_SIZE
)
<
WAKEUP_CHARS
)
ambauart_event
(
info
,
EVT_WRITE_WAKEUP
);
if
(
info
->
xmit
.
head
==
info
->
xmit
.
tail
)
{
ambauart_disable_tx_interrupt
(
info
);
}
}
static
void
ambauart_modem_status
(
struct
amba_info
*
info
)
{
unsigned
int
status
,
delta
;
struct
amba_icount
*
icount
=
&
info
->
state
->
icount
;
status
=
UART_GET_FR
(
info
->
port
)
&
AMBA_UARTFR_MODEM_ANY
;
delta
=
status
^
info
->
old_status
;
info
->
old_status
=
status
;
if
(
!
delta
)
return
;
if
(
delta
&
AMBA_UARTFR_DCD
)
{
icount
->
dcd
++
;
#ifdef CONFIG_HARD_PPS
if
((
info
->
flags
&
ASYNC_HARDPPS_CD
)
&&
(
status
&
AMBA_UARTFR_DCD
)
hardpps
();
#endif
if
(
info
->
flags
&
ASYNC_CHECK_CD
)
{
if
(
status
&
AMBA_UARTFR_DCD
)
wake_up_interruptible
(
&
info
->
open_wait
);
else
if
(
!
((
info
->
flags
&
ASYNC_CALLOUT_ACTIVE
)
&&
(
info
->
flags
&
ASYNC_CALLOUT_NOHUP
)))
{
if
(
info
->
tty
)
tty_hangup
(
info
->
tty
);
}
}
}
if
(
delta
&
AMBA_UARTFR_DSR
)
icount
->
dsr
++
;
if
(
delta
&
AMBA_UARTFR_CTS
)
{
icount
->
cts
++
;
if
(
info
->
flags
&
ASYNC_CTS_FLOW
)
{
status
&=
AMBA_UARTFR_CTS
;
if
(
info
->
tty
->
hw_stopped
)
{
if
(
status
)
{
info
->
tty
->
hw_stopped
=
0
;
ambauart_enable_tx_interrupt
(
info
);
ambauart_event
(
info
,
EVT_WRITE_WAKEUP
);
}
}
else
{
if
(
!
status
)
{
info
->
tty
->
hw_stopped
=
1
;
ambauart_disable_tx_interrupt
(
info
);
}
}
}
}
wake_up_interruptible
(
&
info
->
delta_msr_wait
);
}
static
void
ambauart_int
(
int
irq
,
void
*
dev_id
,
struct
pt_regs
*
regs
)
{
struct
amba_info
*
info
=
dev_id
;
unsigned
int
status
,
pass_counter
=
0
;
#if DEBUG_LEDS
// tell the world
set_leds
(
get_leds
()
|
RED_LED
);
#endif
status
=
UART_GET_INT_STATUS
(
info
->
port
);
do
{
/*
* FIXME: what about clearing the interrupts?
*/
if
(
status
&
(
AMBA_UARTIIR_RTIS
|
AMBA_UARTIIR_RIS
))
#ifdef SUPPORT_SYSRQ
ambauart_rx_chars
(
info
,
regs
);
#else
ambauart_rx_chars
(
info
);
#endif
if
(
status
&
AMBA_UARTIIR_TIS
)
ambauart_tx_chars
(
info
);
if
(
status
&
AMBA_UARTIIR_MIS
)
ambauart_modem_status
(
info
);
if
(
pass_counter
++
>
AMBA_ISR_PASS_LIMIT
)
break
;
status
=
UART_GET_INT_STATUS
(
info
->
port
);
}
while
(
status
&
(
AMBA_UARTIIR_RTIS
|
AMBA_UARTIIR_RIS
|
AMBA_UARTIIR_TIS
));
#if DEBUG_LEDS
// tell the world
set_leds
(
get_leds
()
&
~
RED_LED
);
#endif
}
static
void
ambauart_tasklet_action
(
unsigned
long
data
)
{
struct
amba_info
*
info
=
(
struct
amba_info
*
)
data
;
struct
tty_struct
*
tty
;
tty
=
info
->
tty
;
if
(
!
tty
||
!
test_and_clear_bit
(
EVT_WRITE_WAKEUP
,
&
info
->
event
))
return
;
if
((
tty
->
flags
&
(
1
<<
TTY_DO_WRITE_WAKEUP
))
&&
tty
->
ldisc
.
write_wakeup
)
(
tty
->
ldisc
.
write_wakeup
)(
tty
);
wake_up_interruptible
(
&
tty
->
write_wait
);
}
static
int
ambauart_startup
(
struct
amba_info
*
info
)
{
unsigned
long
flags
;
unsigned
long
page
;
int
retval
=
0
;
page
=
get_zeroed_page
(
GFP_KERNEL
);
if
(
!
page
)
return
-
ENOMEM
;
save_flags
(
flags
);
cli
();
if
(
info
->
flags
&
ASYNC_INITIALIZED
)
{
free_page
(
page
);
goto
errout
;
}
if
(
info
->
xmit
.
buf
)
free_page
(
page
);
else
info
->
xmit
.
buf
=
(
unsigned
char
*
)
page
;
/*
* Allocate the IRQ
*/
retval
=
request_irq
(
info
->
port
->
irq
,
ambauart_int
,
0
,
"amba"
,
info
);
if
(
retval
)
{
if
(
capable
(
CAP_SYS_ADMIN
))
{
if
(
info
->
tty
)
set_bit
(
TTY_IO_ERROR
,
&
info
->
tty
->
flags
);
retval
=
0
;
}
goto
errout
;
}
info
->
mctrl
=
0
;
if
(
info
->
tty
->
termios
->
c_cflag
&
CBAUD
)
info
->
mctrl
=
TIOCM_RTS
|
TIOCM_DTR
;
info
->
port
->
set_mctrl
(
info
->
port
,
info
->
mctrl
);
/*
* initialise the old status of the modem signals
*/
info
->
old_status
=
UART_GET_FR
(
info
->
port
)
&
AMBA_UARTFR_MODEM_ANY
;
/*
* Finally, enable interrupts
*/
ambauart_enable_rx_interrupt
(
info
);
if
(
info
->
tty
)
clear_bit
(
TTY_IO_ERROR
,
&
info
->
tty
->
flags
);
info
->
xmit
.
head
=
info
->
xmit
.
tail
=
0
;
/*
* Set up the tty->alt_speed kludge
*/
if
(
info
->
tty
)
{
if
((
info
->
flags
&
ASYNC_SPD_MASK
)
==
ASYNC_SPD_HI
)
info
->
tty
->
alt_speed
=
57600
;
if
((
info
->
flags
&
ASYNC_SPD_MASK
)
==
ASYNC_SPD_VHI
)
info
->
tty
->
alt_speed
=
115200
;
if
((
info
->
flags
&
ASYNC_SPD_MASK
)
==
ASYNC_SPD_SHI
)
info
->
tty
->
alt_speed
=
230400
;
if
((
info
->
flags
&
ASYNC_SPD_MASK
)
==
ASYNC_SPD_WARP
)
info
->
tty
->
alt_speed
=
460800
;
}
/*
* and set the speed of the serial port
*/
ambauart_change_speed
(
info
,
0
);
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
ambauart_shutdown
(
struct
amba_info
*
info
)
{
unsigned
long
flags
;
if
(
!
(
info
->
flags
&
ASYNC_INITIALIZED
))
return
;
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 woken up
*/
wake_up_interruptible
(
&
info
->
delta_msr_wait
);
/*
* Free the IRQ
*/
free_irq
(
info
->
port
->
irq
,
info
);
if
(
info
->
xmit
.
buf
)
{
unsigned
long
pg
=
(
unsigned
long
)
info
->
xmit
.
buf
;
info
->
xmit
.
buf
=
NULL
;
free_page
(
pg
);
}
/*
* disable all interrupts, disable the port
*/
UART_PUT_CR
(
info
->
port
,
0
);
/* disable break condition and fifos */
UART_PUT_LCRH
(
info
->
port
,
UART_GET_LCRH
(
info
->
port
)
&
~
(
AMBA_UARTLCR_H_BRK
|
AMBA_UARTLCR_H_FEN
));
if
(
!
info
->
tty
||
(
info
->
tty
->
termios
->
c_cflag
&
HUPCL
))
info
->
mctrl
&=
~
(
TIOCM_DTR
|
TIOCM_RTS
);
info
->
port
->
set_mctrl
(
info
->
port
,
info
->
mctrl
);
/* kill off our tasklet */
tasklet_kill
(
&
info
->
tlet
);
if
(
info
->
tty
)
set_bit
(
TTY_IO_ERROR
,
&
info
->
tty
->
flags
);
info
->
flags
&=
~
ASYNC_INITIALIZED
;
restore_flags
(
flags
);
}
static
void
ambauart_change_speed
(
struct
amba_info
*
info
,
struct
termios
*
old_termios
)
{
unsigned
int
lcr_h
,
baud
,
quot
,
cflag
,
old_cr
,
bits
;
unsigned
long
flags
;
if
(
!
info
->
tty
||
!
info
->
tty
->
termios
)
return
;
cflag
=
info
->
tty
->
termios
->
c_cflag
;
#if DEBUG
printk
(
"ambauart_set_cflag(0x%x) called
\n
"
,
cflag
);
#endif
/* byte size and parity */
switch
(
cflag
&
CSIZE
)
{
case
CS5
:
lcr_h
=
AMBA_UARTLCR_H_WLEN_5
;
bits
=
7
;
break
;
case
CS6
:
lcr_h
=
AMBA_UARTLCR_H_WLEN_6
;
bits
=
8
;
break
;
case
CS7
:
lcr_h
=
AMBA_UARTLCR_H_WLEN_7
;
bits
=
9
;
break
;
default:
lcr_h
=
AMBA_UARTLCR_H_WLEN_8
;
bits
=
10
;
break
;
// CS8
}
if
(
cflag
&
CSTOPB
)
{
lcr_h
|=
AMBA_UARTLCR_H_STP2
;
bits
++
;
}
if
(
cflag
&
PARENB
)
{
lcr_h
|=
AMBA_UARTLCR_H_PEN
;
bits
++
;
if
(
!
(
cflag
&
PARODD
))
lcr_h
|=
AMBA_UARTLCR_H_EPS
;
}
if
(
info
->
port
->
fifosize
>
1
)
lcr_h
|=
AMBA_UARTLCR_H_FEN
;
do
{
/* Determine divisor based on baud rate */
baud
=
tty_get_baud_rate
(
info
->
tty
);
if
(
!
baud
)
baud
=
9600
;
if
(
baud
==
38400
&&
((
info
->
flags
&
ASYNC_SPD_MASK
)
==
ASYNC_SPD_CUST
))
quot
=
info
->
state
->
custom_divisor
;
else
quot
=
(
info
->
port
->
uartclk
/
(
16
*
baud
))
-
1
;
if
(
!
quot
&&
old_termios
)
{
info
->
tty
->
termios
->
c_cflag
&=
~
CBAUD
;
info
->
tty
->
termios
->
c_cflag
|=
(
old_termios
->
c_cflag
&
CBAUD
);
old_termios
=
NULL
;
}
}
while
(
quot
==
0
&&
old_termios
);
/* As a last resort, if the quotient is zero, default to 9600 bps */
if
(
!
quot
)
quot
=
(
info
->
port
->
uartclk
/
(
16
*
9600
))
-
1
;
info
->
timeout
=
(
info
->
port
->
fifosize
*
HZ
*
bits
*
quot
)
/
(
info
->
port
->
uartclk
/
16
);
info
->
timeout
+=
HZ
/
50
;
/* Add .02 seconds of slop */
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
;
/*
* Set up parity check flag
*/
#define RELEVENT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
info
->
read_status_mask
=
AMBA_UARTRSR_OE
;
if
(
I_INPCK
(
info
->
tty
))
info
->
read_status_mask
|=
AMBA_UARTRSR_FE
|
AMBA_UARTRSR_PE
;
if
(
I_BRKINT
(
info
->
tty
)
||
I_PARMRK
(
info
->
tty
))
info
->
read_status_mask
|=
AMBA_UARTRSR_BE
;
/*
* Characters to ignore
*/
info
->
ignore_status_mask
=
0
;
if
(
I_IGNPAR
(
info
->
tty
))
info
->
ignore_status_mask
|=
AMBA_UARTRSR_FE
|
AMBA_UARTRSR_PE
;
if
(
I_IGNBRK
(
info
->
tty
))
{
info
->
ignore_status_mask
|=
AMBA_UARTRSR_BE
;
/*
* If we're ignoring parity and break indicators,
* ignore overruns to (for real raw support).
*/
if
(
I_IGNPAR
(
info
->
tty
))
info
->
ignore_status_mask
|=
AMBA_UARTRSR_OE
;
}
/* first, disable everything */
save_flags
(
flags
);
cli
();
old_cr
=
UART_GET_CR
(
info
->
port
)
&=
~
AMBA_UARTCR_MSIE
;
if
((
info
->
flags
&
ASYNC_HARDPPS_CD
)
||
(
cflag
&
CRTSCTS
)
||
!
(
cflag
&
CLOCAL
))
old_cr
|=
AMBA_UARTCR_MSIE
;
UART_PUT_CR
(
info
->
port
,
0
);
restore_flags
(
flags
);
/* Set baud rate */
UART_PUT_LCRM
(
info
->
port
,
((
quot
&
0xf00
)
>>
8
));
UART_PUT_LCRL
(
info
->
port
,
(
quot
&
0xff
));
/*
* ----------v----------v----------v----------v-----
* NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L
* ----------^----------^----------^----------^-----
*/
UART_PUT_LCRH
(
info
->
port
,
lcr_h
);
UART_PUT_CR
(
info
->
port
,
old_cr
);
}
static
void
ambauart_put_char
(
struct
tty_struct
*
tty
,
u_char
ch
)
{
struct
amba_info
*
info
=
tty
->
driver_data
;
unsigned
long
flags
;
if
(
!
tty
||
!
info
->
xmit
.
buf
)
return
;
save_flags
(
flags
);
cli
();
if
(
CIRC_SPACE
(
info
->
xmit
.
head
,
info
->
xmit
.
tail
,
AMBA_XMIT_SIZE
)
!=
0
)
{
info
->
xmit
.
buf
[
info
->
xmit
.
head
]
=
ch
;
info
->
xmit
.
head
=
(
info
->
xmit
.
head
+
1
)
&
(
AMBA_XMIT_SIZE
-
1
);
}
restore_flags
(
flags
);
}
static
void
ambauart_flush_chars
(
struct
tty_struct
*
tty
)
{
struct
amba_info
*
info
=
tty
->
driver_data
;
unsigned
long
flags
;
if
(
info
->
xmit
.
head
==
info
->
xmit
.
tail
||
tty
->
stopped
||
tty
->
hw_stopped
||
!
info
->
xmit
.
buf
)
return
;
save_flags
(
flags
);
cli
();
ambauart_enable_tx_interrupt
(
info
);
restore_flags
(
flags
);
}
static
int
ambauart_write
(
struct
tty_struct
*
tty
,
int
from_user
,
const
u_char
*
buf
,
int
count
)
{
struct
amba_info
*
info
=
tty
->
driver_data
;
unsigned
long
flags
;
int
c
,
ret
=
0
;
if
(
!
tty
||
!
info
->
xmit
.
buf
||
!
tmp_buf
)
return
0
;
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
,
AMBA_XMIT_SIZE
);
if
(
count
<
c
)
c
=
count
;
if
(
c
<=
0
)
break
;
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
,
AMBA_XMIT_SIZE
);
if
(
c1
<
c
)
c
=
c1
;
memcpy
(
info
->
xmit
.
buf
+
info
->
xmit
.
head
,
tmp_buf
,
c
);
info
->
xmit
.
head
=
(
info
->
xmit
.
head
+
c
)
&
(
AMBA_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
,
AMBA_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
)
&
(
AMBA_XMIT_SIZE
-
1
);
buf
+=
c
;
count
-=
c
;
ret
+=
c
;
}
restore_flags
(
flags
);
}
if
(
info
->
xmit
.
head
!=
info
->
xmit
.
tail
&&
!
tty
->
stopped
&&
!
tty
->
hw_stopped
)
ambauart_enable_tx_interrupt
(
info
);
return
ret
;
}
static
int
ambauart_write_room
(
struct
tty_struct
*
tty
)
{
struct
amba_info
*
info
=
tty
->
driver_data
;
return
CIRC_SPACE
(
info
->
xmit
.
head
,
info
->
xmit
.
tail
,
AMBA_XMIT_SIZE
);
}
static
int
ambauart_chars_in_buffer
(
struct
tty_struct
*
tty
)
{
struct
amba_info
*
info
=
tty
->
driver_data
;
return
CIRC_CNT
(
info
->
xmit
.
head
,
info
->
xmit
.
tail
,
AMBA_XMIT_SIZE
);
}
static
void
ambauart_flush_buffer
(
struct
tty_struct
*
tty
)
{
struct
amba_info
*
info
=
tty
->
driver_data
;
unsigned
long
flags
;
#if DEBUG
printk
(
"ambauart_flush_buffer(%d) called
\n
"
,
minor
(
tty
->
device
)
-
tty
->
driver
.
minor_start
);
#endif
save_flags
(
flags
);
cli
();
info
->
xmit
.
head
=
info
->
xmit
.
tail
=
0
;
restore_flags
(
flags
);
wake_up_interruptible
(
&
tty
->
write_wait
);
if
((
tty
->
flags
&
(
1
<<
TTY_DO_WRITE_WAKEUP
))
&&
tty
->
ldisc
.
write_wakeup
)
(
tty
->
ldisc
.
write_wakeup
)(
tty
);
}
/*
* This function is used to send a high-priority XON/XOFF character to
* the device
*/
static
void
ambauart_send_xchar
(
struct
tty_struct
*
tty
,
char
ch
)
{
struct
amba_info
*
info
=
tty
->
driver_data
;
info
->
x_char
=
ch
;
if
(
ch
)
ambauart_enable_tx_interrupt
(
info
);
}
static
void
ambauart_throttle
(
struct
tty_struct
*
tty
)
{
struct
amba_info
*
info
=
tty
->
driver_data
;
unsigned
long
flags
;
if
(
I_IXOFF
(
tty
))
ambauart_send_xchar
(
tty
,
STOP_CHAR
(
tty
));
if
(
tty
->
termios
->
c_cflag
&
CRTSCTS
)
{
save_flags
(
flags
);
cli
();
info
->
mctrl
&=
~
TIOCM_RTS
;
info
->
port
->
set_mctrl
(
info
->
port
,
info
->
mctrl
);
restore_flags
(
flags
);
}
}
static
void
ambauart_unthrottle
(
struct
tty_struct
*
tty
)
{
struct
amba_info
*
info
=
(
struct
amba_info
*
)
tty
->
driver_data
;
unsigned
long
flags
;
if
(
I_IXOFF
(
tty
))
{
if
(
info
->
x_char
)
info
->
x_char
=
0
;
else
ambauart_send_xchar
(
tty
,
START_CHAR
(
tty
));
}
if
(
tty
->
termios
->
c_cflag
&
CRTSCTS
)
{
save_flags
(
flags
);
cli
();
info
->
mctrl
|=
TIOCM_RTS
;
info
->
port
->
set_mctrl
(
info
->
port
,
info
->
mctrl
);
restore_flags
(
flags
);
}
}
static
int
get_serial_info
(
struct
amba_info
*
info
,
struct
serial_struct
*
retinfo
)
{
struct
amba_state
*
state
=
info
->
state
;
struct
amba_port
*
port
=
info
->
port
;
struct
serial_struct
tmp
;
memset
(
&
tmp
,
0
,
sizeof
(
tmp
));
tmp
.
type
=
0
;
tmp
.
line
=
state
->
line
;
tmp
.
port
=
port
->
uart_base
;
if
(
HIGH_BITS_OFFSET
)
tmp
.
port_high
=
port
->
uart_base
>>
HIGH_BITS_OFFSET
;
tmp
.
irq
=
port
->
irq
;
tmp
.
flags
=
0
;
tmp
.
xmit_fifo_size
=
port
->
fifosize
;
tmp
.
baud_base
=
port
->
uartclk
/
16
;
tmp
.
close_delay
=
state
->
close_delay
;
tmp
.
closing_wait
=
state
->
closing_wait
;
tmp
.
custom_divisor
=
state
->
custom_divisor
;
if
(
copy_to_user
(
retinfo
,
&
tmp
,
sizeof
(
*
retinfo
)))
return
-
EFAULT
;
return
0
;
}
static
int
set_serial_info
(
struct
amba_info
*
info
,
struct
serial_struct
*
newinfo
)
{
struct
serial_struct
new_serial
;
struct
amba_state
*
state
,
old_state
;
struct
amba_port
*
port
;
unsigned
long
new_port
;
unsigned
int
i
,
change_irq
,
change_port
;
int
retval
=
0
;
if
(
copy_from_user
(
&
new_serial
,
newinfo
,
sizeof
(
new_serial
)))
return
-
EFAULT
;
state
=
info
->
state
;
old_state
=
*
state
;
port
=
info
->
port
;
new_port
=
new_serial
.
port
;
if
(
HIGH_BITS_OFFSET
)
new_port
+=
(
unsigned
long
)
new_serial
.
port_high
<<
HIGH_BITS_OFFSET
;
change_irq
=
new_serial
.
irq
!=
port
->
irq
;
change_port
=
new_port
!=
port
->
uart_base
;
if
(
!
capable
(
CAP_SYS_ADMIN
))
{
if
(
change_irq
||
change_port
||
(
new_serial
.
baud_base
!=
port
->
uartclk
/
16
)
||
(
new_serial
.
close_delay
!=
state
->
close_delay
)
||
(
new_serial
.
xmit_fifo_size
!=
port
->
fifosize
)
||
((
new_serial
.
flags
&
~
ASYNC_USR_MASK
)
!=
(
state
->
flags
&
~
ASYNC_USR_MASK
)))
return
-
EPERM
;
state
->
flags
=
((
state
->
flags
&
~
ASYNC_USR_MASK
)
|
(
new_serial
.
flags
&
ASYNC_USR_MASK
));
info
->
flags
=
((
info
->
flags
&
~
ASYNC_USR_MASK
)
|
(
new_serial
.
flags
&
ASYNC_USR_MASK
));
state
->
custom_divisor
=
new_serial
.
custom_divisor
;
goto
check_and_exit
;
}
if
((
new_serial
.
irq
>=
NR_IRQS
)
||
(
new_serial
.
irq
<
0
)
||
(
new_serial
.
baud_base
<
9600
))
return
-
EINVAL
;
if
(
new_serial
.
type
&&
change_port
)
{
for
(
i
=
0
;
i
<
SERIAL_AMBA_NR
;
i
++
)
if
((
port
!=
amba_ports
+
i
)
&&
amba_ports
[
i
].
uart_base
!=
new_port
)
return
-
EADDRINUSE
;
}
if
((
change_port
||
change_irq
)
&&
(
state
->
count
>
1
))
return
-
EBUSY
;
/*
* OK, past this point, all the error checking has been done.
* At this point, we start making changes.....
*/
port
->
uartclk
=
new_serial
.
baud_base
*
16
;
state
->
flags
=
((
state
->
flags
&
~
ASYNC_FLAGS
)
|
(
new_serial
.
flags
&
ASYNC_FLAGS
));
info
->
flags
=
((
state
->
flags
&
~
ASYNC_INTERNAL_FLAGS
)
|
(
info
->
flags
&
ASYNC_INTERNAL_FLAGS
));
state
->
custom_divisor
=
new_serial
.
custom_divisor
;
state
->
close_delay
=
new_serial
.
close_delay
*
HZ
/
100
;
state
->
closing_wait
=
new_serial
.
closing_wait
*
HZ
/
100
;
info
->
tty
->
low_latency
=
(
info
->
flags
&
ASYNC_LOW_LATENCY
)
?
1
:
0
;
port
->
fifosize
=
new_serial
.
xmit_fifo_size
;
if
(
change_port
||
change_irq
)
{
/*
* We need to shutdown the serial port at the old
* port/irq combination.
*/
ambauart_shutdown
(
info
);
port
->
irq
=
new_serial
.
irq
;
port
->
uart_base
=
new_port
;
}
check_and_exit:
if
(
!
port
->
uart_base
)
return
0
;
if
(
info
->
flags
&
ASYNC_INITIALIZED
)
{
if
((
old_state
.
flags
&
ASYNC_SPD_MASK
)
!=
(
state
->
flags
&
ASYNC_SPD_MASK
)
||
(
old_state
.
custom_divisor
!=
state
->
custom_divisor
))
{
if
((
state
->
flags
&
ASYNC_SPD_MASK
)
==
ASYNC_SPD_HI
)
info
->
tty
->
alt_speed
=
57600
;
if
((
state
->
flags
&
ASYNC_SPD_MASK
)
==
ASYNC_SPD_VHI
)
info
->
tty
->
alt_speed
=
115200
;
if
((
state
->
flags
&
ASYNC_SPD_MASK
)
==
ASYNC_SPD_SHI
)
info
->
tty
->
alt_speed
=
230400
;
if
((
state
->
flags
&
ASYNC_SPD_MASK
)
==
ASYNC_SPD_WARP
)
info
->
tty
->
alt_speed
=
460800
;
ambauart_change_speed
(
info
,
NULL
);
}
}
else
retval
=
ambauart_startup
(
info
);
return
retval
;
}
/*
* get_lsr_info - get line status register info
*/
static
int
get_lsr_info
(
struct
amba_info
*
info
,
unsigned
int
*
value
)
{
unsigned
int
result
,
status
;
unsigned
long
flags
;
save_flags
(
flags
);
cli
();
status
=
UART_GET_FR
(
info
->
port
);
restore_flags
(
flags
);
result
=
status
&
AMBA_UARTFR_BUSY
?
TIOCSER_TEMT
:
0
;
/*
* If we're about to load something into the transmit
* register, we'll pretend the transmitter isn't empty to
* avoid a race condition (depending on when the transmit
* interrupt happens).
*/
if
(
info
->
x_char
||
((
CIRC_CNT
(
info
->
xmit
.
head
,
info
->
xmit
.
tail
,
AMBA_XMIT_SIZE
)
>
0
)
&&
!
info
->
tty
->
stopped
&&
!
info
->
tty
->
hw_stopped
))
result
&=
TIOCSER_TEMT
;
return
put_user
(
result
,
value
);
}
static
int
get_modem_info
(
struct
amba_info
*
info
,
unsigned
int
*
value
)
{
unsigned
int
result
=
info
->
mctrl
;
unsigned
int
status
;
status
=
UART_GET_FR
(
info
->
port
);
if
(
status
&
AMBA_UARTFR_DCD
)
result
|=
TIOCM_CAR
;
if
(
status
&
AMBA_UARTFR_DSR
)
result
|=
TIOCM_DSR
;
if
(
status
&
AMBA_UARTFR_CTS
)
result
|=
TIOCM_CTS
;
return
put_user
(
result
,
value
);
}
static
int
set_modem_info
(
struct
amba_info
*
info
,
unsigned
int
cmd
,
unsigned
int
*
value
)
{
unsigned
int
arg
,
old
;
unsigned
long
flags
;
if
(
get_user
(
arg
,
value
))
return
-
EFAULT
;
old
=
info
->
mctrl
;
switch
(
cmd
)
{
case
TIOCMBIS
:
info
->
mctrl
|=
arg
;
break
;
case
TIOCMBIC
:
info
->
mctrl
&=
~
arg
;
break
;
case
TIOCMSET
:
info
->
mctrl
=
arg
;
break
;
default:
return
-
EINVAL
;
}
save_flags
(
flags
);
cli
();
if
(
old
!=
info
->
mctrl
)
info
->
port
->
set_mctrl
(
info
->
port
,
info
->
mctrl
);
restore_flags
(
flags
);
return
0
;
}
static
void
ambauart_break_ctl
(
struct
tty_struct
*
tty
,
int
break_state
)
{
struct
amba_info
*
info
=
tty
->
driver_data
;
unsigned
long
flags
;
unsigned
int
lcr_h
;
save_flags
(
flags
);
cli
();
lcr_h
=
UART_GET_LCRH
(
info
->
port
);
if
(
break_state
==
-
1
)
lcr_h
|=
AMBA_UARTLCR_H_BRK
;
else
lcr_h
&=
~
AMBA_UARTLCR_H_BRK
;
UART_PUT_LCRH
(
info
->
port
,
lcr_h
);
restore_flags
(
flags
);
}
static
int
ambauart_ioctl
(
struct
tty_struct
*
tty
,
struct
file
*
file
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
struct
amba_info
*
info
=
tty
->
driver_data
;
struct
amba_icount
cprev
,
cnow
;
struct
serial_icounter_struct
icount
;
unsigned
long
flags
;
if
((
cmd
!=
TIOCGSERIAL
)
&&
(
cmd
!=
TIOCSSERIAL
)
&&
(
cmd
!=
TIOCSERCONFIG
)
&&
(
cmd
!=
TIOCSERGSTRUCT
)
&&
(
cmd
!=
TIOCMIWAIT
)
&&
(
cmd
!=
TIOCGICOUNT
))
{
if
(
tty
->
flags
&
(
1
<<
TTY_IO_ERROR
))
return
-
EIO
;
}
switch
(
cmd
)
{
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
);
/*
* 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
:
save_flags
(
flags
);
cli
();
/* note the counters on entry */
cprev
=
info
->
state
->
icount
;
/* Force modem status interrupts on */
UART_PUT_CR
(
info
->
port
,
UART_GET_CR
(
info
->
port
)
|
AMBA_UARTCR_MSIE
);
restore_flags
(
flags
);
while
(
1
)
{
interruptible_sleep_on
(
&
info
->
delta_msr_wait
);
/* see if a signal did it */
if
(
signal_pending
(
current
))
return
-
ERESTARTSYS
;
save_flags
(
flags
);
cli
();
cnow
=
info
->
state
->
icount
;
/* atomic copy */
restore_flags
(
flags
);
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
:
save_flags
(
flags
);
cli
();
cnow
=
info
->
state
->
icount
;
restore_flags
(
flags
);
icount
.
cts
=
cnow
.
cts
;
icount
.
dsr
=
cnow
.
dsr
;
icount
.
rng
=
cnow
.
rng
;
icount
.
dcd
=
cnow
.
dcd
;
icount
.
rx
=
cnow
.
rx
;
icount
.
tx
=
cnow
.
tx
;
icount
.
frame
=
cnow
.
frame
;
icount
.
overrun
=
cnow
.
overrun
;
icount
.
parity
=
cnow
.
parity
;
icount
.
brk
=
cnow
.
brk
;
icount
.
buf_overrun
=
cnow
.
buf_overrun
;
return
copy_to_user
((
void
*
)
arg
,
&
icount
,
sizeof
(
icount
))
?
-
EFAULT
:
0
;
default:
return
-
ENOIOCTLCMD
;
}
return
0
;
}
static
void
ambauart_set_termios
(
struct
tty_struct
*
tty
,
struct
termios
*
old_termios
)
{
struct
amba_info
*
info
=
tty
->
driver_data
;
unsigned
long
flags
;
unsigned
int
cflag
=
tty
->
termios
->
c_cflag
;
if
((
cflag
^
old_termios
->
c_cflag
)
==
0
&&
RELEVENT_IFLAG
(
tty
->
termios
->
c_iflag
^
old_termios
->
c_iflag
)
==
0
)
return
;
ambauart_change_speed
(
info
,
old_termios
);
/* Handle transition to B0 status */
if
((
old_termios
->
c_cflag
&
CBAUD
)
&&
!
(
cflag
&
CBAUD
))
{
save_flags
(
flags
);
cli
();
info
->
mctrl
&=
~
(
TIOCM_RTS
|
TIOCM_DTR
);
info
->
port
->
set_mctrl
(
info
->
port
,
info
->
mctrl
);
restore_flags
(
flags
);
}
/* Handle transition away from B0 status */
if
(
!
(
old_termios
->
c_cflag
&
CBAUD
)
&&
(
cflag
&
CBAUD
))
{
save_flags
(
flags
);
cli
();
info
->
mctrl
|=
TIOCM_DTR
;
if
(
!
(
cflag
&
CRTSCTS
)
||
!
test_bit
(
TTY_THROTTLED
,
&
tty
->
flags
))
info
->
mctrl
|=
TIOCM_RTS
;
info
->
port
->
set_mctrl
(
info
->
port
,
info
->
mctrl
);
restore_flags
(
flags
);
}
/* Handle turning off CRTSCTS */
if
((
old_termios
->
c_cflag
&
CRTSCTS
)
&&
!
(
cflag
&
CRTSCTS
))
{
tty
->
hw_stopped
=
0
;
ambauart_start
(
tty
);
}
#if 0
/*
* No need to wake up processes in open wait, since they
* sample the CLOCAL flag once, and don't recheck it.
* XXX It's not clear whether the current behavior is correct
* or not. Hence, this may change.....
*/
if (!(old_termios->c_cflag & CLOCAL) &&
(tty->termios->c_cflag & CLOCAL))
wake_up_interruptible(&info->open_wait);
#endif
}
static
void
ambauart_close
(
struct
tty_struct
*
tty
,
struct
file
*
filp
)
{
struct
amba_info
*
info
=
tty
->
driver_data
;
struct
amba_state
*
state
;
unsigned
long
flags
;
if
(
!
info
)
return
;
state
=
info
->
state
;
#if DEBUG
printk
(
"ambauart_close() called
\n
"
);
#endif
save_flags
(
flags
);
cli
();
if
(
tty_hung_up_p
(
filp
))
{
MOD_DEC_USE_COUNT
;
restore_flags
(
flags
);
return
;
}
if
((
tty
->
count
==
1
)
&&
(
state
->
count
!=
1
))
{
/*
* Uh, oh. tty->count is 1, which means that the tty
* structure will be freed. state->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
(
"ambauart_close: bad serial port count; tty->count is 1, "
"state->count is %d
\n
"
,
state
->
count
);
state
->
count
=
1
;
}
if
(
--
state
->
count
<
0
)
{
printk
(
"rs_close: bad serial port count for %s%d: %d
\n
"
,
tty
->
driver
.
name
,
info
->
state
->
line
,
state
->
count
);
state
->
count
=
0
;
}
if
(
state
->
count
)
{
MOD_DEC_USE_COUNT
;
restore_flags
(
flags
);
return
;
}
info
->
flags
|=
ASYNC_CLOSING
;
restore_flags
(
flags
);
/*
* Save the termios structure, since this port may have
* separate termios for callout and dialin.
*/
if
(
info
->
flags
&
ASYNC_NORMAL_ACTIVE
)
info
->
state
->
normal_termios
=
*
tty
->
termios
;
if
(
info
->
flags
&
ASYNC_CALLOUT_ACTIVE
)
info
->
state
->
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
->
state
->
closing_wait
!=
ASYNC_CLOSING_WAIT_NONE
)
tty_wait_until_sent
(
tty
,
info
->
state
->
closing_wait
);
/*
* At this point, we stop accepting input. To do this, we
* disable the receive line status interrupts.
*/
if
(
info
->
flags
&
ASYNC_INITIALIZED
)
{
ambauart_disable_rx_interrupt
(
info
);
/*
* Before we drop DTR, make sure the UART transmitter
* has completely drained; this is especially
* important if there is a transmit FIFO!
*/
ambauart_wait_until_sent
(
tty
,
info
->
timeout
);
}
ambauart_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
=
NULL
;
if
(
info
->
blocked_open
)
{
if
(
info
->
state
->
close_delay
)
{
set_current_state
(
TASK_INTERRUPTIBLE
);
schedule_timeout
(
info
->
state
->
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
;
}
static
void
ambauart_wait_until_sent
(
struct
tty_struct
*
tty
,
int
timeout
)
{
struct
amba_info
*
info
=
(
struct
amba_info
*
)
tty
->
driver_data
;
unsigned
long
char_time
,
expire
;
unsigned
int
status
;
if
(
info
->
port
->
fifosize
==
0
)
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
->
port
->
fifosize
;
char_time
=
char_time
/
5
;
if
(
char_time
==
0
)
char_time
=
1
;
if
(
timeout
&&
timeout
<
char_time
)
char_time
=
timeout
;
/*
* If the transmitter hasn't cleared in twice the approximate
* amount of time to send the entire FIFO, it probably won't
* ever clear. This assumes the UART isn't doing flow
* control, which is currently the case. Hence, if it ever
* takes longer than info->timeout, this is probably due to a
* UART bug of some kind. So, we clamp the timeout parameter at
* 2*info->timeout.
*/
if
(
!
timeout
||
timeout
>
2
*
info
->
timeout
)
timeout
=
2
*
info
->
timeout
;
expire
=
jiffies
+
timeout
;
#if DEBUG
printk
(
"ambauart_wait_until_sent(%d), jiff=%lu, expire=%lu...
\n
"
,
minor
(
tty
->
device
)
-
tty
->
driver
.
minor_start
,
jiffies
,
expire
);
#endif
while
(
UART_GET_FR
(
info
->
port
)
&
AMBA_UARTFR_BUSY
)
{
set_current_state
(
TASK_INTERRUPTIBLE
);
schedule_timeout
(
char_time
);
if
(
signal_pending
(
current
))
break
;
if
(
timeout
&&
time_after
(
jiffies
,
expire
))
break
;
status
=
UART_GET_FR
(
info
->
port
);
}
set_current_state
(
TASK_RUNNING
);
}
static
void
ambauart_hangup
(
struct
tty_struct
*
tty
)
{
struct
amba_info
*
info
=
tty
->
driver_data
;
struct
amba_state
*
state
=
info
->
state
;
ambauart_flush_buffer
(
tty
);
if
(
info
->
flags
&
ASYNC_CLOSING
)
return
;
ambauart_shutdown
(
info
);
info
->
event
=
0
;
state
->
count
=
0
;
info
->
flags
&=
~
(
ASYNC_NORMAL_ACTIVE
|
ASYNC_CALLOUT_ACTIVE
);
info
->
tty
=
NULL
;
wake_up_interruptible
(
&
info
->
open_wait
);
}
static
int
block_til_ready
(
struct
tty_struct
*
tty
,
struct
file
*
filp
,
struct
amba_info
*
info
)
{
DECLARE_WAITQUEUE
(
wait
,
current
);
struct
amba_state
*
state
=
info
->
state
;
unsigned
long
flags
;
int
do_clocal
=
0
,
extra_count
=
0
,
retval
;
/*
* 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
);
return
(
info
->
flags
&
ASYNC_HUP_NOTIFY
)
?
-
EAGAIN
:
-
ERESTARTSYS
;
}
/*
* 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
(
state
->
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, state->count is dropped by one, so that
* rs_close() knows when to free things. We restore it upon
* exit, either normal or abnormal.
*/
retval
=
0
;
add_wait_queue
(
&
info
->
open_wait
,
&
wait
);
save_flags
(
flags
);
cli
();
if
(
!
tty_hung_up_p
(
filp
))
{
extra_count
=
1
;
state
->
count
--
;
}
restore_flags
(
flags
);
info
->
blocked_open
++
;
while
(
1
)
{
save_flags
(
flags
);
cli
();
if
(
!
(
info
->
flags
&
ASYNC_CALLOUT_ACTIVE
)
&&
(
tty
->
termios
->
c_cflag
&
CBAUD
))
{
info
->
mctrl
=
TIOCM_DTR
|
TIOCM_RTS
;
info
->
port
->
set_mctrl
(
info
->
port
,
info
->
mctrl
);
}
restore_flags
(
flags
);
set_current_state
(
TASK_INTERRUPTIBLE
);
if
(
tty_hung_up_p
(
filp
)
||
!
(
info
->
flags
&
ASYNC_INITIALIZED
))
{
if
(
info
->
flags
&
ASYNC_HUP_NOTIFY
)
retval
=
-
EAGAIN
;
else
retval
=
-
ERESTARTSYS
;
break
;
}
if
(
!
(
info
->
flags
&
ASYNC_CALLOUT_ACTIVE
)
&&
!
(
info
->
flags
&
ASYNC_CLOSING
)
&&
(
do_clocal
||
(
UART_GET_FR
(
info
->
port
)
&
AMBA_UARTFR_DCD
)))
break
;
if
(
signal_pending
(
current
))
{
retval
=
-
ERESTARTSYS
;
break
;
}
schedule
();
}
set_current_state
(
TASK_RUNNING
);
remove_wait_queue
(
&
info
->
open_wait
,
&
wait
);
if
(
extra_count
)
state
->
count
++
;
info
->
blocked_open
--
;
if
(
retval
)
return
retval
;
info
->
flags
|=
ASYNC_NORMAL_ACTIVE
;
return
0
;
}
static
struct
amba_info
*
ambauart_get
(
int
line
)
{
struct
amba_info
*
info
;
struct
amba_state
*
state
=
amba_state
+
line
;
state
->
count
++
;
if
(
state
->
info
)
return
state
->
info
;
info
=
kmalloc
(
sizeof
(
struct
amba_info
),
GFP_KERNEL
);
if
(
info
)
{
memset
(
info
,
0
,
sizeof
(
struct
amba_info
));
init_waitqueue_head
(
&
info
->
open_wait
);
init_waitqueue_head
(
&
info
->
close_wait
);
init_waitqueue_head
(
&
info
->
delta_msr_wait
);
info
->
flags
=
state
->
flags
;
info
->
state
=
state
;
info
->
port
=
amba_ports
+
line
;
tasklet_init
(
&
info
->
tlet
,
ambauart_tasklet_action
,
(
unsigned
long
)
info
);
}
if
(
state
->
info
)
{
kfree
(
info
);
return
state
->
info
;
}
state
->
info
=
info
;
return
info
;
}
static
int
ambauart_open
(
struct
tty_struct
*
tty
,
struct
file
*
filp
)
{
struct
amba_info
*
info
;
int
retval
,
line
=
minor
(
tty
->
device
)
-
tty
->
driver
.
minor_start
;
#if DEBUG
printk
(
"ambauart_open(%d) called
\n
"
,
line
);
#endif
// is this a line that we've got?
MOD_INC_USE_COUNT
;
if
(
line
>=
SERIAL_AMBA_NR
)
{
MOD_DEC_USE_COUNT
;
return
-
ENODEV
;
}
info
=
ambauart_get
(
line
);
if
(
!
info
)
return
-
ENOMEM
;
tty
->
driver_data
=
info
;
info
->
tty
=
tty
;
info
->
tty
->
low_latency
=
(
info
->
flags
&
ASYNC_LOW_LATENCY
)
?
1
:
0
;
/*
* Make sure we have the temporary buffer allocated
*/
if
(
!
tmp_buf
)
{
unsigned
long
page
=
get_zeroed_page
(
GFP_KERNEL
);
if
(
tmp_buf
)
free_page
(
page
);
else
if
(
!
page
)
{
MOD_DEC_USE_COUNT
;
return
-
ENOMEM
;
}
tmp_buf
=
(
u_char
*
)
page
;
}
/*
* 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
);
MOD_DEC_USE_COUNT
;
return
-
EAGAIN
;
}
/*
* Start up the serial port
*/
retval
=
ambauart_startup
(
info
);
if
(
retval
)
{
MOD_DEC_USE_COUNT
;
return
retval
;
}
retval
=
block_til_ready
(
tty
,
filp
,
info
);
if
(
retval
)
{
MOD_DEC_USE_COUNT
;
return
retval
;
}
if
((
info
->
state
->
count
==
1
)
&&
(
info
->
flags
&
ASYNC_SPLIT_TERMIOS
))
{
if
(
tty
->
driver
.
subtype
==
SERIAL_TYPE_NORMAL
)
*
tty
->
termios
=
info
->
state
->
normal_termios
;
else
*
tty
->
termios
=
info
->
state
->
callout_termios
;
}
#ifdef CONFIG_SERIAL_AMBA_CONSOLE
if
(
ambauart_cons
.
cflag
&&
ambauart_cons
.
index
==
line
)
{
tty
->
termios
->
c_cflag
=
ambauart_cons
.
cflag
;
ambauart_cons
.
cflag
=
0
;
}
#endif
ambauart_change_speed
(
info
,
NULL
);
info
->
session
=
current
->
session
;
info
->
pgrp
=
current
->
pgrp
;
return
0
;
}
int
__init
ambauart_init
(
void
)
{
int
i
;
ambanormal_driver
.
magic
=
TTY_DRIVER_MAGIC
;
ambanormal_driver
.
driver_name
=
"serial_amba"
;
ambanormal_driver
.
name
=
SERIAL_AMBA_NAME
;
ambanormal_driver
.
major
=
SERIAL_AMBA_MAJOR
;
ambanormal_driver
.
minor_start
=
SERIAL_AMBA_MINOR
;
ambanormal_driver
.
num
=
SERIAL_AMBA_NR
;
ambanormal_driver
.
type
=
TTY_DRIVER_TYPE_SERIAL
;
ambanormal_driver
.
subtype
=
SERIAL_TYPE_NORMAL
;
ambanormal_driver
.
init_termios
=
tty_std_termios
;
ambanormal_driver
.
init_termios
.
c_cflag
=
B38400
|
CS8
|
CREAD
|
HUPCL
|
CLOCAL
;
ambanormal_driver
.
flags
=
TTY_DRIVER_REAL_RAW
|
TTY_DRIVER_NO_DEVFS
;
ambanormal_driver
.
refcount
=
&
ambauart_refcount
;
ambanormal_driver
.
table
=
ambauart_table
;
ambanormal_driver
.
termios
=
ambauart_termios
;
ambanormal_driver
.
termios_locked
=
ambauart_termios_locked
;
ambanormal_driver
.
open
=
ambauart_open
;
ambanormal_driver
.
close
=
ambauart_close
;
ambanormal_driver
.
write
=
ambauart_write
;
ambanormal_driver
.
put_char
=
ambauart_put_char
;
ambanormal_driver
.
flush_chars
=
ambauart_flush_chars
;
ambanormal_driver
.
write_room
=
ambauart_write_room
;
ambanormal_driver
.
chars_in_buffer
=
ambauart_chars_in_buffer
;
ambanormal_driver
.
flush_buffer
=
ambauart_flush_buffer
;
ambanormal_driver
.
ioctl
=
ambauart_ioctl
;
ambanormal_driver
.
throttle
=
ambauart_throttle
;
ambanormal_driver
.
unthrottle
=
ambauart_unthrottle
;
ambanormal_driver
.
send_xchar
=
ambauart_send_xchar
;
ambanormal_driver
.
set_termios
=
ambauart_set_termios
;
ambanormal_driver
.
stop
=
ambauart_stop
;
ambanormal_driver
.
start
=
ambauart_start
;
ambanormal_driver
.
hangup
=
ambauart_hangup
;
ambanormal_driver
.
break_ctl
=
ambauart_break_ctl
;
ambanormal_driver
.
wait_until_sent
=
ambauart_wait_until_sent
;
ambanormal_driver
.
read_proc
=
NULL
;
/*
* The callout device is just like the normal device except for
* the major number and the subtype code.
*/
ambacallout_driver
=
ambanormal_driver
;
ambacallout_driver
.
name
=
CALLOUT_AMBA_NAME
;
ambacallout_driver
.
major
=
CALLOUT_AMBA_MAJOR
;
ambacallout_driver
.
subtype
=
SERIAL_TYPE_CALLOUT
;
ambacallout_driver
.
read_proc
=
NULL
;
ambacallout_driver
.
proc_entry
=
NULL
;
if
(
tty_register_driver
(
&
ambanormal_driver
))
panic
(
"Couldn't register AMBA serial driver
\n
"
);
if
(
tty_register_driver
(
&
ambacallout_driver
))
panic
(
"Couldn't register AMBA callout driver
\n
"
);
for
(
i
=
0
;
i
<
SERIAL_AMBA_NR
;
i
++
)
{
struct
amba_state
*
state
=
amba_state
+
i
;
state
->
line
=
i
;
state
->
close_delay
=
5
*
HZ
/
10
;
state
->
closing_wait
=
30
*
HZ
;
state
->
callout_termios
=
ambacallout_driver
.
init_termios
;
state
->
normal_termios
=
ambanormal_driver
.
init_termios
;
}
return
0
;
}
__initcall
(
ambauart_init
);
#ifdef CONFIG_SERIAL_AMBA_CONSOLE
/************** console driver *****************/
/*
* This code is currently never used; console->read is never called.
* Therefore, although we have an implementation, we don't use it.
* FIXME: the "const char *s" should be fixed to "char *s" some day.
* (when the definition in include/linux/console.h is also fixed)
*/
#ifdef used_and_not_const_char_pointer
static
int
ambauart_console_read
(
struct
console
*
co
,
const
char
*
s
,
u_int
count
)
{
struct
amba_port
*
port
=
&
amba_ports
[
co
->
index
];
unsigned
int
status
;
char
*
w
;
int
c
;
#if DEBUG
printk
(
"ambauart_console_read() called
\n
"
);
#endif
c
=
0
;
w
=
s
;
while
(
c
<
count
)
{
status
=
UART_GET_FR
(
port
);
if
(
UART_RX_DATA
(
status
))
{
*
w
++
=
UART_GET_CHAR
(
port
);
c
++
;
}
else
{
// nothing more to get, return
return
c
;
}
}
// return the count
return
c
;
}
#endif
/*
* Print a string to the serial port trying not to disturb
* any possible real use of the port...
*
* The console must be locked when we get here.
*/
static
void
ambauart_console_write
(
struct
console
*
co
,
const
char
*
s
,
u_int
count
)
{
struct
amba_port
*
port
=
&
amba_ports
[
co
->
index
];
unsigned
int
status
,
old_cr
;
int
i
;
/*
* First save the CR then disable the interrupts
*/
old_cr
=
UART_GET_CR
(
port
);
UART_PUT_CR
(
port
,
AMBA_UARTCR_UARTEN
);
/*
* Now, do each character
*/
for
(
i
=
0
;
i
<
count
;
i
++
)
{
do
{
status
=
UART_GET_FR
(
port
);
}
while
(
!
UART_TX_READY
(
status
));
UART_PUT_CHAR
(
port
,
s
[
i
]);
if
(
s
[
i
]
==
'\n'
)
{
do
{
status
=
UART_GET_FR
(
port
);
}
while
(
!
UART_TX_READY
(
status
));
UART_PUT_CHAR
(
port
,
'\r'
);
}
}
/*
* Finally, wait for transmitter to become empty
* and restore the TCR
*/
do
{
status
=
UART_GET_FR
(
port
);
}
while
(
status
&
AMBA_UARTFR_BUSY
);
UART_PUT_CR
(
port
,
old_cr
);
}
static
kdev_t
ambauart_console_device
(
struct
console
*
c
)
{
return
mk_kdev
(
SERIAL_AMBA_MAJOR
,
SERIAL_AMBA_MINOR
+
c
->
index
);
}
static
int
__init
ambauart_console_setup
(
struct
console
*
co
,
char
*
options
)
{
struct
amba_port
*
port
;
int
baud
=
38400
;
int
bits
=
8
;
int
parity
=
'n'
;
u_int
cflag
=
CREAD
|
HUPCL
|
CLOCAL
;
u_int
lcr_h
,
quot
;
if
(
co
->
index
>=
SERIAL_AMBA_NR
)
co
->
index
=
0
;
port
=
&
amba_ports
[
co
->
index
];
if
(
options
)
{
char
*
s
=
options
;
baud
=
simple_strtoul
(
s
,
NULL
,
10
);
while
(
*
s
>=
'0'
&&
*
s
<=
'9'
)
s
++
;
if
(
*
s
)
parity
=
*
s
++
;
if
(
*
s
)
bits
=
*
s
-
'0'
;
}
/*
* Now construct a cflag setting.
*/
switch
(
baud
)
{
case
1200
:
cflag
|=
B1200
;
break
;
case
2400
:
cflag
|=
B2400
;
break
;
case
4800
:
cflag
|=
B4800
;
break
;
default:
cflag
|=
B9600
;
baud
=
9600
;
break
;
case
19200
:
cflag
|=
B19200
;
break
;
case
38400
:
cflag
|=
B38400
;
break
;
case
57600
:
cflag
|=
B57600
;
break
;
case
115200
:
cflag
|=
B115200
;
break
;
}
switch
(
bits
)
{
case
7
:
cflag
|=
CS7
;
lcr_h
=
AMBA_UARTLCR_H_WLEN_7
;
break
;
default:
cflag
|=
CS8
;
lcr_h
=
AMBA_UARTLCR_H_WLEN_8
;
break
;
}
switch
(
parity
)
{
case
'o'
:
case
'O'
:
cflag
|=
PARODD
;
lcr_h
|=
AMBA_UARTLCR_H_PEN
;
break
;
case
'e'
:
case
'E'
:
cflag
|=
PARENB
;
lcr_h
|=
AMBA_UARTLCR_H_PEN
|
AMBA_UARTLCR_H_EPS
;
break
;
}
co
->
cflag
=
cflag
;
if
(
port
->
fifosize
>
1
)
lcr_h
|=
AMBA_UARTLCR_H_FEN
;
quot
=
(
port
->
uartclk
/
(
16
*
baud
))
-
1
;
UART_PUT_LCRL
(
port
,
(
quot
&
0xff
));
UART_PUT_LCRM
(
port
,
(
quot
>>
8
));
UART_PUT_LCRH
(
port
,
lcr_h
);
/* we will enable the port as we need it */
UART_PUT_CR
(
port
,
0
);
return
0
;
}
static
struct
console
ambauart_cons
=
{
name:
SERIAL_AMBA_NAME
,
write:
ambauart_console_write
,
#ifdef used_and_not_const_char_pointer
read:
ambauart_console_read
,
#endif
device:
ambauart_console_device
,
setup:
ambauart_console_setup
,
flags:
CON_PRINTBUFFER
,
index:
-
1
,
};
void
__init
ambauart_console_init
(
void
)
{
register_console
(
&
ambauart_cons
);
}
#endif
/* CONFIG_SERIAL_AMBA_CONSOLE */
MODULE_LICENSE
(
"GPL"
);
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