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
Kirill Smelkov
linux
Commits
70ca2c43
Commit
70ca2c43
authored
Nov 14, 2004
by
Linus Torvalds
Browse files
Options
Browse Files
Download
Plain Diff
Merge
bk://bk.arm.linux.org.uk/linux-2.6-serial
into ppc970.osdl.org:/home/torvalds/v2.6/linux
parents
f52ef599
3f6a3bd4
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
1168 additions
and
351 deletions
+1168
-351
drivers/serial/s3c2410.c
drivers/serial/s3c2410.c
+1106
-323
include/asm-arm/arch-s3c2410/regs-serial.h
include/asm-arm/arch-s3c2410/regs-serial.h
+62
-28
No files found.
drivers/serial/s3c2410.c
View file @
70ca2c43
/*
/*
* linux/drivers/
char
/s3c2410.c
* linux/drivers/
serial
/s3c2410.c
*
*
* Driver for onboard UARTs on the Samsung S3C24
10
* Driver for onboard UARTs on the Samsung S3C24
XX
*
*
* Based on drivers/char/serial.c and drivers/char/21285.c
* Based on drivers/char/serial.c and drivers/char/21285.c
*
*
...
@@ -9,55 +9,152 @@
...
@@ -9,55 +9,152 @@
*
*
* Changelog:
* Changelog:
*
*
*/
* 22-Jul-2004 BJD Finished off device rewrite
*
* 21-Jul-2004 BJD Thanks to <herbet@13thfloor.at> for pointing out
* problems with baud rate and loss of IR settings. Update
* to add configuration via platform_device structure
*
* 28-Sep-2004 BJD Re-write for the following items
* - S3C2410 and S3C2440 serial support
* - Power Management support
* - Fix console via IrDA devices
* - SysReq (Herbert Ptzl)
* - Break character handling (Herbert Ptzl)
* - spin-lock initialisation (Dimitry Andric)
* - added clock control
* - updated init code to use platform_device info
*/
/* Hote on 2410 error handling
*
* The s3c2410 manual has a love/hate affair with the contents of the
* UERSTAT register in the UART blocks, and keeps marking some of the
* error bits as reserved. Having checked with the s3c2410x01,
* it copes with BREAKs properly, so I am happy to ignore the RESERVED
* feature from the latter versions of the manual.
*
* If it becomes aparrent that latter versions of the 2410 remove these
* bits, then action will have to be taken to differentiate the versions
* and change the policy on BREAK
*
* BJD, 04-Nov-2004
*/
#include <linux/config.h>
#include <linux/config.h>
#if defined(CONFIG_SERIAL_S3C2410_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#include <linux/module.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/ioport.h>
#include <linux/device.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/init.h>
#include <linux/sysrq.h>
#include <linux/console.h>
#include <linux/console.h>
#include <linux/tty.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/tty_flip.h>
#include <linux/serial_core.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
#include <linux/serial.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/irq.h>
#include <asm/hardware.h>
#include <asm/hardware.h>
#include <asm/hardware/clock.h>
#include <asm/arch/regs-serial.h>
#include <asm/arch/regs-serial.h>
#include <asm/arch/regs-gpio.h>
#include <asm/mach-types.h>
/* structures */
struct
s3c24xx_uart_info
{
char
*
name
;
unsigned
int
type
;
unsigned
int
fifosize
;
unsigned
long
rx_fifomask
;
unsigned
long
rx_fifoshift
;
unsigned
long
tx_fifomask
;
unsigned
long
tx_fifoshift
;
unsigned
long
tx_fifofull
;
/* clock source control */
int
(
*
get_clksrc
)(
struct
uart_port
*
,
struct
s3c24xx_uart_clksrc
*
clk
);
int
(
*
set_clksrc
)(
struct
uart_port
*
,
struct
s3c24xx_uart_clksrc
*
clk
);
};
struct
s3c24xx_uart_port
{
unsigned
char
rx_claimed
;
unsigned
char
tx_claimed
;
struct
s3c24xx_uart_info
*
info
;
struct
s3c24xx_uart_clksrc
*
clksrc
;
struct
clk
*
clk
;
struct
clk
*
baudclk
;
struct
uart_port
port
;
};
/* configuration defines */
#if 0
#if 0
#include <asm/debug-ll.h>
#if 1
#define dbg(x...) llprintk(x)
/* send debug to the low-level output routines */
extern void printascii(const char *);
static void
s3c24xx_serial_dbg(const char *fmt, ...)
{
va_list va;
char buff[256];
va_start(va, fmt);
vsprintf(buff, fmt, va);
va_end(va);
printascii(buff);
}
#define dbg(x...) s3c24xx_serial_dbg(x)
#else
#else
#define dbg(x...)
#define dbg(x...)
printk(KERN_DEBUG "s3c24xx: ");
#endif
#endif
#else
/* no debug */
#define dbg(x...) do {} while(0)
#endif
/* UART name and device definitions */
#define SERIAL_S3C2410_NAME "ttySAC"
#define S3C24XX_SERIAL_NAME "ttySAC"
#define SERIAL_S3C2410_MAJOR 204
#define S3C24XX_SERIAL_DEVFS "tts/"
#define SERIAL_S3C2410_MINOR 64
#define S3C24XX_SERIAL_MAJOR 204
#define S3C24XX_SERIAL_MINOR 64
/* conversion functions */
#define s3c24xx_dev_to_port(__dev) (struct uart_port *)dev_get_drvdata(__dev)
#define s3c24xx_dev_to_cfg(__dev) (struct s3c2410_uartcfg *)((__dev)->platform_data)
/* we can support 3 uarts, but not always use them */
/* we can support 3 uarts, but not always use them */
#define NR_PORTS (3)
#define NR_PORTS (3)
static
const
char
serial_s3c2410_name
[]
=
"Samsung S3C2410 UART"
;
/* port irq numbers */
/* port irq numbers */
#define TX_IRQ(port) ((port)->irq + 1)
#define TX_IRQ(port) ((port)->irq + 1)
#define RX_IRQ(port) ((port)->irq)
#define RX_IRQ(port) ((port)->irq)
#define tx_enabled(port) ((port)->unused[0])
/* register access controls */
#define rx_enabled(port) ((port)->unused[1])
/* flag to ignore all characters comming in */
#define RXSTAT_DUMMY_READ (0x10000000)
/* access functions */
#define portaddr(port, reg) ((
void *)((port)->membase + (reg)
))
#define portaddr(port, reg) ((
port)->membase + (reg
))
#define rd_regb(port, reg) (__raw_readb(portaddr(port, reg)))
#define rd_regb(port, reg) (__raw_readb(portaddr(port, reg)))
#define rd_regl(port, reg) (__raw_readl(portaddr(port, reg)))
#define rd_regl(port, reg) (__raw_readl(portaddr(port, reg)))
...
@@ -68,102 +165,230 @@ static const char serial_s3c2410_name[] = "Samsung S3C2410 UART";
...
@@ -68,102 +165,230 @@ static const char serial_s3c2410_name[] = "Samsung S3C2410 UART";
#define wr_regl(port, reg, val) \
#define wr_regl(port, reg, val) \
do { __raw_writel(val, portaddr(port, reg)); } while(0)
do { __raw_writel(val, portaddr(port, reg)); } while(0)
/* macros to change one thing to another */
#define tx_enabled(port) ((port)->unused[0])
#define rx_enabled(port) ((port)->unused[1])
/* flag to ignore all characters comming in */
#define RXSTAT_DUMMY_READ (0x10000000)
static
inline
struct
s3c24xx_uart_port
*
to_ourport
(
struct
uart_port
*
port
)
{
return
container_of
(
port
,
struct
s3c24xx_uart_port
,
port
);
}
/* translate a port to the device name */
static
inline
char
*
s3c24xx_serial_portname
(
struct
uart_port
*
port
)
{
return
to_platform_device
(
port
->
dev
)
->
name
;
}
static
int
s3c24xx_serial_txempty_nofifo
(
struct
uart_port
*
port
)
{
return
(
rd_regl
(
port
,
S3C2410_UTRSTAT
)
&
S3C2410_UTRSTAT_TXE
);
}
static
void
s3c24xx_serial_rx_enable
(
struct
uart_port
*
port
)
{
unsigned
long
flags
;
unsigned
int
ucon
,
ufcon
;
int
count
=
10000
;
/* code */
spin_lock_irqsave
(
&
port
->
lock
,
flags
);
while
(
--
count
&&
!
s3c24xx_serial_txempty_nofifo
(
port
))
udelay
(
100
);
ufcon
=
rd_regl
(
port
,
S3C2410_UFCON
);
ufcon
|=
S3C2410_UFCON_RESETRX
;
wr_regl
(
port
,
S3C2410_UFCON
,
ufcon
);
ucon
=
rd_regl
(
port
,
S3C2410_UCON
);
ucon
|=
S3C2410_UCON_RXIRQMODE
;
wr_regl
(
port
,
S3C2410_UCON
,
ucon
);
rx_enabled
(
port
)
=
1
;
spin_unlock_irqrestore
(
&
port
->
lock
,
flags
);
}
static
void
s3c24xx_serial_rx_disable
(
struct
uart_port
*
port
)
{
unsigned
long
flags
;
unsigned
int
ucon
;
spin_lock_irqsave
(
&
port
->
lock
,
flags
);
ucon
=
rd_regl
(
port
,
S3C2410_UCON
);
ucon
&=
~
S3C2410_UCON_RXIRQMODE
;
wr_regl
(
port
,
S3C2410_UCON
,
ucon
);
rx_enabled
(
port
)
=
0
;
spin_unlock_irqrestore
(
&
port
->
lock
,
flags
);
}
static
void
static
void
s
erial_s3c2410
_stop_tx
(
struct
uart_port
*
port
,
unsigned
int
tty_stop
)
s
3c24xx_serial
_stop_tx
(
struct
uart_port
*
port
,
unsigned
int
tty_stop
)
{
{
if
(
tx_enabled
(
port
))
{
if
(
tx_enabled
(
port
))
{
disable_irq
(
TX_IRQ
(
port
));
disable_irq
(
TX_IRQ
(
port
));
tx_enabled
(
port
)
=
0
;
tx_enabled
(
port
)
=
0
;
if
(
port
->
flags
&
UPF_CONS_FLOW
)
s3c24xx_serial_rx_enable
(
port
);
}
}
}
}
static
void
static
void
s
erial_s3c2410
_start_tx
(
struct
uart_port
*
port
,
unsigned
int
tty_start
)
s
3c24xx_serial
_start_tx
(
struct
uart_port
*
port
,
unsigned
int
tty_start
)
{
{
if
(
!
tx_enabled
(
port
))
{
if
(
!
tx_enabled
(
port
))
{
if
(
port
->
flags
&
UPF_CONS_FLOW
)
s3c24xx_serial_rx_disable
(
port
);
enable_irq
(
TX_IRQ
(
port
));
enable_irq
(
TX_IRQ
(
port
));
tx_enabled
(
port
)
=
1
;
tx_enabled
(
port
)
=
1
;
}
}
}
}
static
void
serial_s3c2410_stop_rx
(
struct
uart_port
*
port
)
static
void
s3c24xx_serial_stop_rx
(
struct
uart_port
*
port
)
{
{
if
(
rx_enabled
(
port
))
{
if
(
rx_enabled
(
port
))
{
dbg
(
"s
erial_s3c2410
_stop_rx: port=%p
\n
"
,
port
);
dbg
(
"s
3c24xx_serial
_stop_rx: port=%p
\n
"
,
port
);
disable_irq
(
RX_IRQ
(
port
));
disable_irq
(
RX_IRQ
(
port
));
rx_enabled
(
port
)
=
0
;
rx_enabled
(
port
)
=
0
;
}
}
}
}
static
void
serial_s3c2410_enable_ms
(
struct
uart_port
*
port
)
static
void
s3c24xx_serial_enable_ms
(
struct
uart_port
*
port
)
{
}
static
inline
struct
s3c24xx_uart_info
*
s3c24xx_port_to_info
(
struct
uart_port
*
port
)
{
return
to_ourport
(
port
)
->
info
;
}
static
inline
struct
s3c2410_uartcfg
*
s3c24xx_port_to_cfg
(
struct
uart_port
*
port
)
{
{
if
(
port
->
dev
==
NULL
)
return
NULL
;
return
(
struct
s3c2410_uartcfg
*
)
port
->
dev
->
platform_data
;
}
}
static
int
s3c24xx_serial_rx_fifocnt
(
struct
s3c24xx_uart_port
*
ourport
,
unsigned
long
ufstat
)
{
struct
s3c24xx_uart_info
*
info
=
ourport
->
info
;
return
(
ufstat
&
info
->
rx_fifomask
)
>>
info
->
rx_fifoshift
;
}
/* ? - where has parity gone?? */
/* ? - where has parity gone?? */
#define S3C2410_UERSTAT_PARITY (0x1000)
#define S3C2410_UERSTAT_PARITY (0x1000)
static
irqreturn_t
static
irqreturn_t
s
erial_s3c2410
_rx_chars
(
int
irq
,
void
*
dev_id
,
struct
pt_regs
*
regs
)
s
3c24xx_serial
_rx_chars
(
int
irq
,
void
*
dev_id
,
struct
pt_regs
*
regs
)
{
{
struct
uart_port
*
port
=
dev_id
;
struct
s3c24xx_uart_port
*
ourport
=
dev_id
;
struct
uart_port
*
port
=
&
ourport
->
port
;
struct
tty_struct
*
tty
=
port
->
info
->
tty
;
struct
tty_struct
*
tty
=
port
->
info
->
tty
;
unsigned
int
ufcon
,
ch
,
flag
,
rxs
,
uf
stat
;
unsigned
int
ufcon
,
ch
,
flag
,
ufstat
,
uer
stat
;
int
max_count
=
256
;
int
max_count
=
64
;
while
(
max_count
--
>
0
)
{
while
(
max_count
--
>
0
)
{
ufcon
=
rd_regl
(
port
,
S3C2410_UFCON
);
ufcon
=
rd_regl
(
port
,
S3C2410_UFCON
);
ufstat
=
rd_regl
(
port
,
S3C2410_UFSTAT
);
ufstat
=
rd_regl
(
port
,
S3C2410_UFSTAT
);
if
(
S3C2410_UFCON_RXC
(
ufstat
)
==
0
)
if
(
s3c24xx_serial_rx_fifocnt
(
ourport
,
ufstat
)
==
0
)
break
;
break
;
if
(
tty
->
flip
.
count
>=
TTY_FLIPBUF_SIZE
)
{
if
(
tty
->
flip
.
count
>=
TTY_FLIPBUF_SIZE
)
{
if
(
tty
->
low_latency
)
if
(
tty
->
low_latency
)
tty_flip_buffer_push
(
tty
);
tty_flip_buffer_push
(
tty
);
/*
/*
* If this failed then we will throw away the
* If this failed then we will throw away the
* bytes but must do so to clear interrupts
* bytes but must do so to clear interrupts
*/
*/
}
}
uerstat
=
rd_regl
(
port
,
S3C2410_UERSTAT
);
ch
=
rd_regb
(
port
,
S3C2410_URXH
);
ch
=
rd_regb
(
port
,
S3C2410_URXH
);
if
(
port
->
flags
&
UPF_CONS_FLOW
)
{
int
txe
=
s3c24xx_serial_txempty_nofifo
(
port
);
if
(
rx_enabled
(
port
))
{
if
(
!
txe
)
{
rx_enabled
(
port
)
=
0
;
continue
;
}
}
else
{
if
(
txe
)
{
ufcon
|=
S3C2410_UFCON_RESETRX
;
wr_regl
(
port
,
S3C2410_UFCON
,
ufcon
);
rx_enabled
(
port
)
=
1
;
goto
out
;
}
continue
;
}
}
/* insert the character into the buffer */
flag
=
TTY_NORMAL
;
flag
=
TTY_NORMAL
;
port
->
icount
.
rx
++
;
port
->
icount
.
rx
++
;
rxs
=
rd_regb
(
port
,
S3C2410_UERSTAT
)
|
RXSTAT_DUMMY_READ
;
if
(
uerstat
&
S3C2410_UERSTAT_ANY
)
{
dbg
(
"rxerr: port ch=0x%02x, rxs=0x%08x
\n
"
,
ch
,
uerstat
);
if
(
rxs
&
S3C2410_UERSTAT_ANY
)
{
/* check for break */
if
(
rxs
&
S3C2410_UERSTAT_FRAME
)
if
(
uerstat
&
S3C2410_UERSTAT_BREAK
)
{
dbg
(
"break!
\n
"
);
port
->
icount
.
brk
++
;
if
(
uart_handle_break
(
port
))
goto
ignore_char
;
}
if
(
uerstat
&
S3C2410_UERSTAT_FRAME
)
port
->
icount
.
frame
++
;
port
->
icount
.
frame
++
;
if
(
rxs
&
S3C2410_UERSTAT_OVERRUN
)
if
(
uerstat
&
S3C2410_UERSTAT_OVERRUN
)
port
->
icount
.
overrun
++
;
port
->
icount
.
overrun
++
;
rxs
&=
port
->
read_status_mask
;
uerstat
&=
port
->
read_status_mask
;
if
(
rxs
&
S3C2410_UERSTAT_PARITY
)
if
(
uerstat
&
S3C2410_UERSTAT_BREAK
)
flag
=
TTY_BREAK
;
else
if
(
uerstat
&
S3C2410_UERSTAT_PARITY
)
flag
=
TTY_PARITY
;
flag
=
TTY_PARITY
;
else
if
(
rxs
&
(
S3C2410_UERSTAT_FRAME
|
S3C2410_UERSTAT_OVERRUN
))
else
if
(
uerstat
&
(
S3C2410_UERSTAT_FRAME
|
S3C2410_UERSTAT_OVERRUN
))
flag
=
TTY_FRAME
;
flag
=
TTY_FRAME
;
}
}
if
((
rxs
&
port
->
ignore_status_mask
)
==
0
)
{
if
(
uart_handle_sysrq_char
(
port
,
ch
,
regs
))
goto
ignore_char
;
if
((
uerstat
&
port
->
ignore_status_mask
)
==
0
)
{
tty_insert_flip_char
(
tty
,
ch
,
flag
);
tty_insert_flip_char
(
tty
,
ch
,
flag
);
}
}
if
((
rxs
&
S3C2410_UERSTAT_OVERRUN
)
&&
if
((
uerstat
&
S3C2410_UERSTAT_OVERRUN
)
&&
tty
->
flip
.
count
<
TTY_FLIPBUF_SIZE
)
{
tty
->
flip
.
count
<
TTY_FLIPBUF_SIZE
)
{
/*
/*
* Overrun is special, since it's reported
* Overrun is special, since it's reported
* immediately, and doesn't affect the current
* immediately, and doesn't affect the current
* character.
* character.
*/
*/
tty_insert_flip_char
(
tty
,
0
,
TTY_OVERRUN
);
tty_insert_flip_char
(
tty
,
0
,
TTY_OVERRUN
);
}
}
ignore_char:
continue
;
}
}
tty_flip_buffer_push
(
tty
);
tty_flip_buffer_push
(
tty
);
...
@@ -171,10 +396,10 @@ serial_s3c2410_rx_chars(int irq, void *dev_id, struct pt_regs *regs)
...
@@ -171,10 +396,10 @@ serial_s3c2410_rx_chars(int irq, void *dev_id, struct pt_regs *regs)
return
IRQ_HANDLED
;
return
IRQ_HANDLED
;
}
}
static
irqreturn_t
static
irqreturn_t
s3c24xx_serial_tx_chars
(
int
irq
,
void
*
id
,
struct
pt_regs
*
regs
)
serial_s3c2410_tx_chars
(
int
irq
,
void
*
dev_id
,
struct
pt_regs
*
regs
)
{
{
struct
uart_port
*
port
=
(
struct
uart_port
*
)
dev_id
;
struct
s3c24xx_uart_port
*
ourport
=
id
;
struct
uart_port
*
port
=
&
ourport
->
port
;
struct
circ_buf
*
xmit
=
&
port
->
info
->
xmit
;
struct
circ_buf
*
xmit
=
&
port
->
info
->
xmit
;
int
count
=
256
;
int
count
=
256
;
...
@@ -190,14 +415,14 @@ serial_s3c2410_tx_chars(int irq, void *dev_id, struct pt_regs *regs)
...
@@ -190,14 +415,14 @@ serial_s3c2410_tx_chars(int irq, void *dev_id, struct pt_regs *regs)
*/
*/
if
(
uart_circ_empty
(
xmit
)
||
uart_tx_stopped
(
port
))
{
if
(
uart_circ_empty
(
xmit
)
||
uart_tx_stopped
(
port
))
{
s
erial_s3c2410
_stop_tx
(
port
,
0
);
s
3c24xx_serial
_stop_tx
(
port
,
0
);
goto
out
;
goto
out
;
}
}
/* try and drain the buffer... */
/* try and drain the buffer... */
while
(
!
uart_circ_empty
(
xmit
)
&&
count
--
>
0
)
{
while
(
!
uart_circ_empty
(
xmit
)
&&
count
--
>
0
)
{
if
(
rd_regl
(
port
,
S3C2410_UFSTAT
)
&
S3C2410_UFSTAT_TXFULL
)
if
(
rd_regl
(
port
,
S3C2410_UFSTAT
)
&
ourport
->
info
->
tx_fifofull
)
break
;
break
;
wr_regb
(
port
,
S3C2410_UTXH
,
xmit
->
buf
[
xmit
->
tail
]);
wr_regb
(
port
,
S3C2410_UTXH
,
xmit
->
buf
[
xmit
->
tail
]);
...
@@ -209,22 +434,31 @@ serial_s3c2410_tx_chars(int irq, void *dev_id, struct pt_regs *regs)
...
@@ -209,22 +434,31 @@ serial_s3c2410_tx_chars(int irq, void *dev_id, struct pt_regs *regs)
uart_write_wakeup
(
port
);
uart_write_wakeup
(
port
);
if
(
uart_circ_empty
(
xmit
))
if
(
uart_circ_empty
(
xmit
))
s
erial_s3c2410
_stop_tx
(
port
,
0
);
s
3c24xx_serial
_stop_tx
(
port
,
0
);
out:
out:
return
IRQ_HANDLED
;
return
IRQ_HANDLED
;
}
}
static
unsigned
int
static
unsigned
int
s3c24xx_serial_tx_empty
(
struct
uart_port
*
port
)
serial_s3c2410_tx_empty
(
struct
uart_port
*
port
)
{
{
unsigned
int
ufcon
=
rd_regl
(
port
,
S3C2410_UFCON
);
struct
s3c24xx_uart_info
*
info
=
s3c24xx_port_to_info
(
port
);
return
(
S3C2410_UFCON_TXC
(
ufcon
)
!=
0
)
?
0
:
TIOCSER_TEMT
;
unsigned
long
ufstat
=
rd_regl
(
port
,
S3C2410_UFSTAT
);
unsigned
long
ufcon
=
rd_regl
(
port
,
S3C2410_UFCON
);
if
(
ufcon
&
S3C2410_UFCON_FIFOMODE
)
{
if
((
ufstat
&
info
->
tx_fifomask
)
!=
0
||
(
ufstat
&
info
->
tx_fifofull
))
return
0
;
return
1
;
}
return
s3c24xx_serial_txempty_nofifo
(
port
);
}
}
/* no modem control lines */
/* no modem control lines */
static
unsigned
int
static
unsigned
int
s3c24xx_serial_get_mctrl
(
struct
uart_port
*
port
)
serial_s3c2410_get_mctrl
(
struct
uart_port
*
port
)
{
{
unsigned
int
umstat
=
rd_regb
(
port
,
S3C2410_UMSTAT
);
unsigned
int
umstat
=
rd_regb
(
port
,
S3C2410_UMSTAT
);
...
@@ -234,13 +468,12 @@ serial_s3c2410_get_mctrl(struct uart_port *port)
...
@@ -234,13 +468,12 @@ serial_s3c2410_get_mctrl(struct uart_port *port)
return
TIOCM_CAR
|
TIOCM_DSR
;
return
TIOCM_CAR
|
TIOCM_DSR
;
}
}
static
void
static
void
s3c24xx_serial_set_mctrl
(
struct
uart_port
*
port
,
unsigned
int
mctrl
)
serial_s3c2410_set_mctrl
(
struct
uart_port
*
port
,
unsigned
int
mctrl
)
{
{
/* todo - possibly remove AFC and do manual CTS */
/* todo - possibly remove AFC and do manual CTS */
}
}
static
void
s
erial_s3c2410
_break_ctl
(
struct
uart_port
*
port
,
int
break_state
)
static
void
s
3c24xx_serial
_break_ctl
(
struct
uart_port
*
port
,
int
break_state
)
{
{
unsigned
long
flags
;
unsigned
long
flags
;
unsigned
int
ucon
;
unsigned
int
ucon
;
...
@@ -259,46 +492,250 @@ static void serial_s3c2410_break_ctl(struct uart_port *port, int break_state)
...
@@ -259,46 +492,250 @@ static void serial_s3c2410_break_ctl(struct uart_port *port, int break_state)
spin_unlock_irqrestore
(
&
port
->
lock
,
flags
);
spin_unlock_irqrestore
(
&
port
->
lock
,
flags
);
}
}
static
int
serial_s3c2410_startup
(
struct
uart_port
*
port
)
static
void
s3c24xx_serial_shutdown
(
struct
uart_port
*
port
)
{
{
struct
s3c24xx_uart_port
*
ourport
=
to_ourport
(
port
);
if
(
ourport
->
tx_claimed
)
{
free_irq
(
TX_IRQ
(
port
),
ourport
);
tx_enabled
(
port
)
=
0
;
ourport
->
tx_claimed
=
0
;
}
if
(
ourport
->
rx_claimed
)
{
free_irq
(
RX_IRQ
(
port
),
ourport
);
ourport
->
rx_claimed
=
0
;
rx_enabled
(
port
)
=
0
;
}
}
static
int
s3c24xx_serial_startup
(
struct
uart_port
*
port
)
{
struct
s3c24xx_uart_port
*
ourport
=
to_ourport
(
port
);
unsigned
long
flags
;
int
ret
;
int
ret
;
tx_enabled
(
port
)
=
1
;
dbg
(
"s3c24xx_serial_startup: port=%p (%08lx,%p)
\n
"
,
rx_enabled
(
port
)
=
1
;
port
->
mapbase
,
port
->
membase
);
local_irq_save
(
flags
);
dbg
(
"serial_s3c2410_startup: port=%p (%p)
\n
"
,
rx_enabled
(
port
)
=
1
;
port
,
port
->
mapbase
);
ret
=
request_irq
(
RX_IRQ
(
port
),
serial_s3c2410_rx_chars
,
0
,
ret
=
request_irq
(
RX_IRQ
(
port
),
serial_s3c2410_name
,
port
);
s3c24xx_serial_rx_chars
,
0
,
s3c24xx_serial_portname
(
port
),
ourport
);
if
(
ret
!=
0
)
if
(
ret
!=
0
)
{
printk
(
KERN_ERR
"cannot get irq %d
\n
"
,
RX_IRQ
(
port
));
return
ret
;
return
ret
;
}
ourport
->
rx_claimed
=
1
;
dbg
(
"requesting tx irq...
\n
"
);
tx_enabled
(
port
)
=
1
;
ret
=
request_irq
(
TX_IRQ
(
port
),
serial_s3c2410_tx_chars
,
0
,
ret
=
request_irq
(
TX_IRQ
(
port
),
serial_s3c2410_name
,
port
);
s3c24xx_serial_tx_chars
,
0
,
s3c24xx_serial_portname
(
port
),
ourport
);
if
(
ret
)
{
if
(
ret
)
{
free_irq
(
RX_IRQ
(
port
),
port
);
printk
(
KERN_ERR
"cannot get irq %d
\n
"
,
TX_IRQ
(
port
)
);
return
ret
;
goto
err
;
}
}
ourport
->
tx_claimed
=
1
;
dbg
(
"s3c24xx_serial_startup ok
\n
"
);
/* the port reset code should have done the correct
/* the port reset code should have done the correct
* register setup for the port controls */
* register setup for the port controls */
local_irq_restore
(
flags
);
return
ret
;
err:
s3c24xx_serial_shutdown
(
port
);
local_irq_restore
(
flags
);
return
ret
;
return
ret
;
}
}
static
void
serial_s3c2410_shutdown
(
struct
uart_port
*
port
)
/* power power management control */
static
void
s3c24xx_serial_pm
(
struct
uart_port
*
port
,
unsigned
int
level
,
unsigned
int
old
)
{
{
free_irq
(
TX_IRQ
(
port
),
port
);
struct
s3c24xx_uart_port
*
ourport
=
to_ourport
(
port
);
free_irq
(
RX_IRQ
(
port
),
port
);
switch
(
level
)
{
case
3
:
if
(
!
IS_ERR
(
ourport
->
baudclk
)
&&
ourport
->
baudclk
!=
NULL
)
clk_disable
(
ourport
->
baudclk
);
clk_disable
(
ourport
->
clk
);
break
;
case
0
:
clk_enable
(
ourport
->
clk
);
if
(
!
IS_ERR
(
ourport
->
baudclk
)
&&
ourport
->
baudclk
!=
NULL
)
clk_enable
(
ourport
->
baudclk
);
break
;
default:
printk
(
KERN_ERR
"s3c24xx_serial: unknown pm %d
\n
"
,
level
);
}
}
}
static
void
/* baud rate calculation
serial_s3c2410_set_termios
(
struct
uart_port
*
port
,
struct
termios
*
termios
,
*
struct
termios
*
old
)
* The UARTs on the S3C2410/S3C2440 can take their clocks from a number
* of different sources, including the peripheral clock ("pclk") and an
* external clock ("uclk"). The S3C2440 also adds the core clock ("fclk")
* with a programmable extra divisor.
*
* The following code goes through the clock sources, and calculates the
* baud clocks (and the resultant actual baud rates) and then tries to
* pick the closest one and select that.
*
* NOTES:
* 1) there is no current code to properly select/deselect FCLK on
* the s3c2440, so only specify FCLK or non-FCLK in the clock
* sources for the UART
*
*/
#define MAX_CLKS (8)
static
struct
s3c24xx_uart_clksrc
tmp_clksrc
=
{
.
name
=
"pclk"
,
.
min_baud
=
0
,
.
max_baud
=
0
,
.
divisor
=
1
,
};
static
inline
int
s3c24xx_serial_getsource
(
struct
uart_port
*
port
,
struct
s3c24xx_uart_clksrc
*
c
)
{
struct
s3c24xx_uart_info
*
info
=
s3c24xx_port_to_info
(
port
);
return
(
info
->
get_clksrc
)(
port
,
c
);
}
static
inline
int
s3c24xx_serial_setsource
(
struct
uart_port
*
port
,
struct
s3c24xx_uart_clksrc
*
c
)
{
struct
s3c24xx_uart_info
*
info
=
s3c24xx_port_to_info
(
port
);
return
(
info
->
set_clksrc
)(
port
,
c
);
}
struct
baud_calc
{
struct
s3c24xx_uart_clksrc
*
clksrc
;
unsigned
int
calc
;
unsigned
int
quot
;
struct
clk
*
src
;
};
static
int
s3c24xx_serial_calcbaud
(
struct
baud_calc
*
calc
,
struct
uart_port
*
port
,
struct
s3c24xx_uart_clksrc
*
clksrc
,
unsigned
int
baud
)
{
unsigned
long
rate
;
calc
->
src
=
clk_get
(
port
->
dev
,
clksrc
->
name
);
if
(
calc
->
src
==
NULL
||
IS_ERR
(
calc
->
src
))
return
0
;
rate
=
clk_get_rate
(
calc
->
src
);
calc
->
clksrc
=
clksrc
;
calc
->
quot
=
(
rate
+
(
8
*
baud
))
/
(
16
*
baud
);
calc
->
calc
=
(
rate
/
(
calc
->
quot
*
16
));
calc
->
quot
--
;
return
1
;
}
static
unsigned
int
s3c24xx_serial_getclk
(
struct
uart_port
*
port
,
struct
s3c24xx_uart_clksrc
**
clksrc
,
struct
clk
**
clk
,
unsigned
int
baud
)
{
{
struct
s3c2410_uartcfg
*
cfg
=
s3c24xx_port_to_cfg
(
port
);
struct
s3c24xx_uart_clksrc
*
clkp
;
struct
baud_calc
res
[
MAX_CLKS
];
struct
baud_calc
*
resptr
,
*
best
,
*
sptr
;
int
i
;
clkp
=
cfg
->
clocks
;
best
=
NULL
;
if
(
cfg
->
clocks_size
<
2
)
{
if
(
cfg
->
clocks_size
==
0
)
clkp
=
&
tmp_clksrc
;
s3c24xx_serial_calcbaud
(
res
,
port
,
clkp
,
baud
);
best
=
res
;
resptr
=
best
+
1
;
}
else
{
resptr
=
res
;
for
(
i
=
0
;
i
<
cfg
->
clocks_size
;
i
++
,
clkp
++
)
{
if
(
s3c24xx_serial_calcbaud
(
resptr
,
port
,
clkp
,
baud
))
resptr
++
;
}
}
/* ok, we now need to select the best clock we found */
if
(
!
best
)
{
unsigned
int
deviation
=
(
1
<<
30
)
|
((
1
<<
30
)
-
1
);
int
calc_deviation
;
for
(
sptr
=
res
;
sptr
<
resptr
;
sptr
++
)
{
printk
(
KERN_DEBUG
"found clk %p (%s) quot %d, calc %d
\n
"
,
sptr
->
clksrc
,
sptr
->
clksrc
->
name
,
sptr
->
quot
,
sptr
->
calc
);
calc_deviation
=
baud
-
sptr
->
calc
;
if
(
calc_deviation
<
0
)
calc_deviation
=
-
calc_deviation
;
if
(
calc_deviation
<
deviation
)
{
best
=
sptr
;
deviation
=
calc_deviation
;
}
}
printk
(
KERN_DEBUG
"best %p (deviation %d)
\n
"
,
best
,
deviation
);
}
printk
(
KERN_DEBUG
"selected clock %p (%s) quot %d, calc %d
\n
"
,
best
->
clksrc
,
best
->
clksrc
->
name
,
best
->
quot
,
best
->
calc
);
/* store results to pass back */
*
clksrc
=
best
->
clksrc
;
*
clk
=
best
->
src
;
return
best
->
quot
;
}
static
void
s3c24xx_serial_set_termios
(
struct
uart_port
*
port
,
struct
termios
*
termios
,
struct
termios
*
old
)
{
struct
s3c2410_uartcfg
*
cfg
=
s3c24xx_port_to_cfg
(
port
);
struct
s3c24xx_uart_port
*
ourport
=
to_ourport
(
port
);
struct
s3c24xx_uart_clksrc
*
clksrc
;
struct
clk
*
clk
;
unsigned
long
flags
;
unsigned
long
flags
;
unsigned
int
baud
,
quot
;
unsigned
int
baud
,
quot
;
unsigned
int
ulcon
;
unsigned
int
ulcon
;
...
@@ -309,16 +746,34 @@ serial_s3c2410_set_termios(struct uart_port *port, struct termios *termios,
...
@@ -309,16 +746,34 @@ serial_s3c2410_set_termios(struct uart_port *port, struct termios *termios,
termios
->
c_cflag
&=
~
(
HUPCL
|
CRTSCTS
|
CMSPAR
);
termios
->
c_cflag
&=
~
(
HUPCL
|
CRTSCTS
|
CMSPAR
);
termios
->
c_cflag
|=
CLOCAL
;
termios
->
c_cflag
|=
CLOCAL
;
/*
* We don't support BREAK character recognition.
*/
termios
->
c_iflag
&=
~
(
IGNBRK
|
BRKINT
);
/*
/*
* Ask the core to calculate the divisor for us.
* Ask the core to calculate the divisor for us.
*/
*/
baud
=
uart_get_baud_rate
(
port
,
termios
,
old
,
0
,
port
->
uartclk
/
16
);
quot
=
uart_get_divisor
(
port
,
baud
);
baud
=
uart_get_baud_rate
(
port
,
termios
,
old
,
0
,
115200
*
8
);
if
(
baud
==
38400
&&
(
port
->
flags
&
UPF_SPD_MASK
)
==
UPF_SPD_CUST
)
quot
=
port
->
custom_divisor
;
else
quot
=
s3c24xx_serial_getclk
(
port
,
&
clksrc
,
&
clk
,
baud
);
/* check to see if we need to change clock source */
if
(
ourport
->
clksrc
!=
clksrc
||
ourport
->
baudclk
!=
clk
)
{
s3c24xx_serial_setsource
(
port
,
clksrc
);
if
(
ourport
->
baudclk
!=
NULL
&&
!
IS_ERR
(
ourport
->
baudclk
))
{
clk_disable
(
ourport
->
baudclk
);
clk_unuse
(
ourport
->
baudclk
);
ourport
->
baudclk
=
NULL
;
}
clk_use
(
clk
);
clk_enable
(
clk
);
ourport
->
clksrc
=
clksrc
;
ourport
->
baudclk
=
clk
;
}
switch
(
termios
->
c_cflag
&
CSIZE
)
{
switch
(
termios
->
c_cflag
&
CSIZE
)
{
case
CS5
:
case
CS5
:
...
@@ -340,6 +795,9 @@ serial_s3c2410_set_termios(struct uart_port *port, struct termios *termios,
...
@@ -340,6 +795,9 @@ serial_s3c2410_set_termios(struct uart_port *port, struct termios *termios,
break
;
break
;
}
}
/* preserve original lcon IR settings */
ulcon
|=
(
cfg
->
ulcon
&
S3C2410_LCON_IRM
);
if
(
termios
->
c_cflag
&
CSTOPB
)
if
(
termios
->
c_cflag
&
CSTOPB
)
ulcon
|=
S3C2410_LCON_STOPB
;
ulcon
|=
S3C2410_LCON_STOPB
;
...
@@ -352,18 +810,12 @@ serial_s3c2410_set_termios(struct uart_port *port, struct termios *termios,
...
@@ -352,18 +810,12 @@ serial_s3c2410_set_termios(struct uart_port *port, struct termios *termios,
ulcon
|=
S3C2410_LCON_PNONE
;
ulcon
|=
S3C2410_LCON_PNONE
;
}
}
/*
if (port->fifosize)
enable_fifo()
*/
spin_lock_irqsave
(
&
port
->
lock
,
flags
);
spin_lock_irqsave
(
&
port
->
lock
,
flags
);
dbg
(
"setting ulcon to %08x
\n
"
,
ulcon
);
dbg
(
"setting ulcon to %08x, brddiv to %d
\n
"
,
ulcon
,
quot
);
//dbg("<flushing output from serial>\n");
/* set the ulcon register */
wr_regl
(
port
,
S3C2410_ULCON
,
ulcon
);
wr_regl
(
port
,
S3C2410_ULCON
,
ulcon
);
wr_regl
(
port
,
S3C2410_UBRDIV
,
quot
);
dbg
(
"uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x
\n
"
,
dbg
(
"uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x
\n
"
,
rd_regl
(
port
,
S3C2410_ULCON
),
rd_regl
(
port
,
S3C2410_ULCON
),
...
@@ -400,113 +852,151 @@ serial_s3c2410_set_termios(struct uart_port *port, struct termios *termios,
...
@@ -400,113 +852,151 @@ serial_s3c2410_set_termios(struct uart_port *port, struct termios *termios,
spin_unlock_irqrestore
(
&
port
->
lock
,
flags
);
spin_unlock_irqrestore
(
&
port
->
lock
,
flags
);
}
}
static
const
char
*
s
erial_s3c2410
_type
(
struct
uart_port
*
port
)
static
const
char
*
s
3c24xx_serial
_type
(
struct
uart_port
*
port
)
{
{
return
port
->
type
==
PORT_S3C2410
?
"S3C2410"
:
NULL
;
switch
(
port
->
type
)
{
case
PORT_S3C2410
:
return
"S3C2410"
;
case
PORT_S3C2440
:
return
"S3C2440"
;
default:
return
NULL
;
}
}
}
#define MAP_SIZE (0x100)
#define MAP_SIZE (0x100)
static
void
static
void
s3c24xx_serial_release_port
(
struct
uart_port
*
port
)
serial_s3c2410_release_port
(
struct
uart_port
*
port
)
{
{
release_mem_region
(
port
->
mapbase
,
MAP_SIZE
);
release_mem_region
(
port
->
mapbase
,
MAP_SIZE
);
}
}
static
int
static
int
s3c24xx_serial_request_port
(
struct
uart_port
*
port
)
serial_s3c2410_request_port
(
struct
uart_port
*
port
)
{
{
return
request_mem_region
(
port
->
mapbase
,
MAP_SIZE
,
serial_s3c2410_name
)
char
*
name
=
s3c24xx_serial_portname
(
port
);
!=
NULL
?
0
:
-
EBUSY
;
return
request_mem_region
(
port
->
mapbase
,
MAP_SIZE
,
name
)
?
0
:
-
EBUSY
;
}
}
static
void
static
void
s3c24xx_serial_config_port
(
struct
uart_port
*
port
,
int
flags
)
serial_s3c2410_config_port
(
struct
uart_port
*
port
,
int
flags
)
{
{
struct
s3c24xx_uart_info
*
info
=
s3c24xx_port_to_info
(
port
);
if
(
flags
&
UART_CONFIG_TYPE
&&
if
(
flags
&
UART_CONFIG_TYPE
&&
s
erial_s3c2410
_request_port
(
port
)
==
0
)
s
3c24xx_serial
_request_port
(
port
)
==
0
)
port
->
type
=
PORT_S3C2410
;
port
->
type
=
info
->
type
;
}
}
/*
/*
* verify the new serial_struct (for TIOCSSERIAL).
* verify the new serial_struct (for TIOCSSERIAL).
*/
*/
static
int
static
int
s
erial_s3c2410
_verify_port
(
struct
uart_port
*
port
,
struct
serial_struct
*
ser
)
s
3c24xx_serial
_verify_port
(
struct
uart_port
*
port
,
struct
serial_struct
*
ser
)
{
{
int
ret
=
0
;
struct
s3c24xx_uart_info
*
info
=
s3c24xx_port_to_info
(
port
)
;
if
(
ser
->
type
!=
PORT_UNKNOWN
&&
ser
->
type
!=
PORT_S3C2410
)
if
(
ser
->
type
!=
PORT_UNKNOWN
&&
ser
->
type
!=
info
->
type
)
ret
=
-
EINVAL
;
ret
urn
-
EINVAL
;
return
ret
;
return
0
;
}
}
static
struct
uart_ops
serial_s3c2410_ops
=
{
.
tx_empty
=
serial_s3c2410_tx_empty
,
#ifdef CONFIG_SERIAL_S3C2410_CONSOLE
.
get_mctrl
=
serial_s3c2410_get_mctrl
,
.
set_mctrl
=
serial_s3c2410_set_mctrl
,
static
struct
console
s3c24xx_serial_console
;
.
stop_tx
=
serial_s3c2410_stop_tx
,
.
start_tx
=
serial_s3c2410_start_tx
,
#define S3C24XX_SERIAL_CONSOLE &s3c24xx_serial_console
.
stop_rx
=
serial_s3c2410_stop_rx
,
#else
.
enable_ms
=
serial_s3c2410_enable_ms
,
#define S3C24XX_SERIAL_CONSOLE NULL
.
break_ctl
=
serial_s3c2410_break_ctl
,
#endif
.
startup
=
serial_s3c2410_startup
,
.
shutdown
=
serial_s3c2410_shutdown
,
static
struct
uart_ops
s3c24xx_serial_ops
=
{
.
set_termios
=
serial_s3c2410_set_termios
,
.
pm
=
s3c24xx_serial_pm
,
.
type
=
serial_s3c2410_type
,
.
tx_empty
=
s3c24xx_serial_tx_empty
,
.
release_port
=
serial_s3c2410_release_port
,
.
get_mctrl
=
s3c24xx_serial_get_mctrl
,
.
request_port
=
serial_s3c2410_request_port
,
.
set_mctrl
=
s3c24xx_serial_set_mctrl
,
.
config_port
=
serial_s3c2410_config_port
,
.
stop_tx
=
s3c24xx_serial_stop_tx
,
.
verify_port
=
serial_s3c2410_verify_port
,
.
start_tx
=
s3c24xx_serial_start_tx
,
.
stop_rx
=
s3c24xx_serial_stop_rx
,
.
enable_ms
=
s3c24xx_serial_enable_ms
,
.
break_ctl
=
s3c24xx_serial_break_ctl
,
.
startup
=
s3c24xx_serial_startup
,
.
shutdown
=
s3c24xx_serial_shutdown
,
.
set_termios
=
s3c24xx_serial_set_termios
,
.
type
=
s3c24xx_serial_type
,
.
release_port
=
s3c24xx_serial_release_port
,
.
request_port
=
s3c24xx_serial_request_port
,
.
config_port
=
s3c24xx_serial_config_port
,
.
verify_port
=
s3c24xx_serial_verify_port
,
};
static
struct
uart_driver
s3c24xx_uart_drv
=
{
.
owner
=
THIS_MODULE
,
.
dev_name
=
"s3c2410_serial"
,
.
nr
=
3
,
.
cons
=
S3C24XX_SERIAL_CONSOLE
,
.
driver_name
=
S3C24XX_SERIAL_NAME
,
.
devfs_name
=
S3C24XX_SERIAL_DEVFS
,
.
major
=
S3C24XX_SERIAL_MAJOR
,
.
minor
=
S3C24XX_SERIAL_MINOR
,
};
};
static
struct
uart_port
serial_s3c2410_ports
[
NR_PORTS
]
=
{
static
struct
s3c24xx_uart_port
s3c24xx_serial_ports
[
NR_PORTS
]
=
{
{
[
0
]
=
{
.
membase
=
0
,
.
port
=
{
.
mapbase
=
0
,
.
lock
=
SPIN_LOCK_UNLOCKED
,
.
iotype
=
UPIO_MEM
,
.
membase
=
0
,
.
irq
=
IRQ_S3CUART_RX0
,
.
mapbase
=
0
,
.
uartclk
=
0
,
.
iotype
=
UPIO_MEM
,
.
fifosize
=
16
,
.
irq
=
IRQ_S3CUART_RX0
,
.
ops
=
&
serial_s3c2410_ops
,
.
uartclk
=
0
,
.
flags
=
UPF_BOOT_AUTOCONF
,
.
fifosize
=
16
,
.
line
=
0
,
.
ops
=
&
s3c24xx_serial_ops
,
.
flags
=
UPF_BOOT_AUTOCONF
,
.
line
=
0
,
}
},
[
1
]
=
{
.
port
=
{
.
lock
=
SPIN_LOCK_UNLOCKED
,
.
membase
=
0
,
.
mapbase
=
0
,
.
iotype
=
UPIO_MEM
,
.
irq
=
IRQ_S3CUART_RX1
,
.
uartclk
=
0
,
.
fifosize
=
16
,
.
ops
=
&
s3c24xx_serial_ops
,
.
flags
=
UPF_BOOT_AUTOCONF
,
.
line
=
1
,
}
},
},
{
.
membase
=
0
,
.
mapbase
=
0
,
.
iotype
=
UPIO_MEM
,
.
irq
=
IRQ_S3CUART_RX1
,
.
uartclk
=
0
,
.
fifosize
=
16
,
.
ops
=
&
serial_s3c2410_ops
,
.
flags
=
UPF_BOOT_AUTOCONF
,
.
line
=
1
,
}
#if NR_PORTS > 2
#if NR_PORTS > 2
,
{
[
2
]
=
{
.
membase
=
0
,
.
port
=
{
.
mapbase
=
0
,
.
lock
=
SPIN_LOCK_UNLOCKED
,
.
iotype
=
UPIO_MEM
,
.
membase
=
0
,
.
irq
=
IRQ_S3CUART_RX2
,
.
mapbase
=
0
,
.
uartclk
=
0
,
.
iotype
=
UPIO_MEM
,
.
fifosize
=
16
,
.
irq
=
IRQ_S3CUART_RX2
,
.
ops
=
&
serial_s3c2410_ops
,
.
uartclk
=
0
,
.
flags
=
UPF_BOOT_AUTOCONF
,
.
fifosize
=
16
,
.
line
=
2
,
.
ops
=
&
s3c24xx_serial_ops
,
.
flags
=
UPF_BOOT_AUTOCONF
,
.
line
=
2
,
}
}
}
#endif
#endif
};
};
static
int
s
erial_s3c2410
_resetport
(
struct
uart_port
*
port
,
s
tatic
int
s3c24xx_serial
_resetport
(
struct
uart_port
*
port
,
struct
s3c2410_uartcfg
*
cfg
)
struct
s3c2410_uartcfg
*
cfg
)
{
{
/* ensure registers are setup */
/* ensure registers are setup */
dbg
(
"s
erial_s3c2410_resetport: port=%p (%08
x), cfg=%p
\n
"
,
dbg
(
"s
3c24xx_serial_resetport: port=%p (%08l
x), cfg=%p
\n
"
,
port
,
port
->
mapbase
,
cfg
);
port
,
port
->
mapbase
,
cfg
);
wr_regl
(
port
,
S3C2410_UCON
,
cfg
->
ucon
);
wr_regl
(
port
,
S3C2410_UCON
,
cfg
->
ucon
);
...
@@ -520,105 +1010,429 @@ serial_s3c2410_resetport(struct uart_port *port,
...
@@ -520,105 +1010,429 @@ serial_s3c2410_resetport(struct uart_port *port,
return
0
;
return
0
;
}
}
/* s
erial_s3c2410_init_ports
/* s
3c24xx_serial_init_port
*
*
* initialise the serial ports from the machine provided initialisation
* initialise a single serial port from the platform device given
* data.
*/
*/
static
int
serial_s3c2410_init_ports
(
void
)
static
int
s3c24xx_serial_init_port
(
struct
s3c24xx_uart_port
*
ourport
,
struct
s3c24xx_uart_info
*
info
,
struct
platform_device
*
platdev
)
{
{
struct
uart_port
*
ptr
=
serial_s3c2410_ports
;
struct
uart_port
*
port
=
&
ourport
->
port
;
struct
s3c2410_uartcfg
*
cfg
=
s3c2410_uartcfgs
;
struct
s3c2410_uartcfg
*
cfg
;
static
int
inited
=
0
;
struct
resource
*
res
;
int
i
;
dbg
(
"s3c24xx_serial_init_port: port=%p, platdev=%p
\n
"
,
port
,
platdev
);
if
(
inited
)
if
(
platdev
==
NULL
)
return
-
ENODEV
;
cfg
=
s3c24xx_dev_to_cfg
(
&
platdev
->
dev
);
if
(
port
->
mapbase
!=
0
)
return
0
;
return
0
;
inited
=
1
;
dbg
(
"serial_s3c2410_init_ports: initialising ports...
\n
"
);
if
(
cfg
->
hwport
>
3
)
return
-
EINVAL
;
for
(
i
=
0
;
i
<
NR_PORTS
;
i
++
,
ptr
++
,
cfg
++
)
{
/* setup info for port */
port
->
dev
=
&
platdev
->
dev
;
ourport
->
info
=
info
;
if
(
cfg
->
hwport
>
3
)
/* copy the info in from provided structure */
continu
e
;
ourport
->
port
.
fifosize
=
info
->
fifosiz
e
;
dbg
(
"serial_s3c2410_init_ports: port %d (hw %d)...
\n
"
,
dbg
(
"s3c24xx_serial_init_port: %p (hw %d)...
\n
"
,
port
,
cfg
->
hwport
);
i
,
cfg
->
hwport
);
if
(
cfg
->
clock
!=
NULL
)
port
->
uartclk
=
1
;
ptr
->
uartclk
=
*
cfg
->
clock
;
switch
(
cfg
->
hwport
)
{
if
(
cfg
->
uart_flags
&
UPF_CONS_FLOW
)
{
case
0
:
dbg
(
"s3c24xx_serial_init_port: enabling flow control
\n
"
);
ptr
->
mapbase
=
S3C2410_PA_UART0
;
port
->
flags
|=
UPF_CONS_FLOW
;
ptr
->
membase
=
(
char
*
)
S3C2410_VA_UART0
;
}
ptr
->
irq
=
IRQ_S3CUART_RX0
;
break
;
case
1
:
/* sort our the physical and virtual addresses for each UART */
ptr
->
mapbase
=
S3C2410_PA_UART1
;
ptr
->
membase
=
(
char
*
)
S3C2410_VA_UART1
;
ptr
->
irq
=
IRQ_S3CUART_RX1
;
break
;
case
2
:
res
=
platform_get_resource
(
platdev
,
IORESOURCE_MEM
,
0
);
ptr
->
mapbase
=
S3C2410_PA_UART2
;
if
(
res
==
NULL
)
{
ptr
->
membase
=
(
char
*
)
S3C2410_VA_UART2
;
printk
(
KERN_ERR
"failed to find memory resource for uart
\n
"
);
ptr
->
irq
=
IRQ_S3CUART_RX2
;
return
-
EINVAL
;
break
;
}
}
dbg
(
"resource %p (%lx..%lx)
\n
"
,
res
,
res
->
start
,
res
->
end
);
port
->
mapbase
=
res
->
start
;
port
->
membase
=
(
void
__iomem
*
)(
res
->
start
-
S3C2410_PA_UART
);
port
->
membase
+=
S3C2410_VA_UART
;
port
->
irq
=
platform_get_irq
(
platdev
,
0
);
ourport
->
clk
=
clk_get
(
&
platdev
->
dev
,
"uart"
);
if
(
ourport
->
clk
!=
NULL
&&
!
IS_ERR
(
ourport
->
clk
))
clk_use
(
ourport
->
clk
);
dbg
(
"port: map=%08x, mem=%08x, irq=%d, clock=%ld
\n
"
,
port
->
mapbase
,
port
->
membase
,
port
->
irq
,
port
->
uartclk
);
/* reset the fifos (and setup the uart) */
s3c24xx_serial_resetport
(
port
,
cfg
);
return
0
;
}
/* Device driver serial port probe */
static
int
probe_index
=
0
;
int
s3c24xx_serial_probe
(
struct
device
*
_dev
,
struct
s3c24xx_uart_info
*
info
)
{
struct
s3c24xx_uart_port
*
ourport
;
struct
platform_device
*
dev
=
to_platform_device
(
_dev
);
int
ret
;
dbg
(
"s3c24xx_serial_probe(%p, %p) %d
\n
"
,
_dev
,
info
,
probe_index
);
ourport
=
&
s3c24xx_serial_ports
[
probe_index
];
probe_index
++
;
dbg
(
"%s: initialising port %p...
\n
"
,
__FUNCTION__
,
ourport
);
ret
=
s3c24xx_serial_init_port
(
ourport
,
info
,
dev
);
if
(
ret
<
0
)
goto
probe_err
;
dbg
(
"%s: adding port
\n
"
,
__FUNCTION__
);
uart_add_one_port
(
&
s3c24xx_uart_drv
,
&
ourport
->
port
);
dev_set_drvdata
(
_dev
,
&
ourport
->
port
);
return
0
;
probe_err:
return
ret
;
}
int
s3c24xx_serial_remove
(
struct
device
*
_dev
)
{
struct
uart_port
*
port
=
s3c24xx_dev_to_port
(
_dev
);
if
(
port
)
uart_remove_one_port
(
&
s3c24xx_uart_drv
,
port
);
return
0
;
}
/* UART power management code */
#ifdef CONFIG_PM
int
s3c24xx_serial_suspend
(
struct
device
*
dev
,
u32
state
,
u32
level
)
{
struct
uart_port
*
port
=
s3c24xx_dev_to_port
(
dev
);
if
(
port
&&
level
==
SUSPEND_DISABLE
)
uart_suspend_port
(
&
s3c24xx_uart_drv
,
port
);
return
0
;
}
int
s3c24xx_serial_resume
(
struct
device
*
dev
,
u32
level
)
{
struct
uart_port
*
port
=
s3c24xx_dev_to_port
(
dev
);
struct
s3c24xx_uart_port
*
ourport
=
to_ourport
(
port
);
if
(
port
&&
level
==
RESUME_ENABLE
)
{
clk_enable
(
ourport
->
clk
);
s3c24xx_serial_resetport
(
port
,
s3c24xx_port_to_cfg
(
port
));
clk_disable
(
ourport
->
clk
);
uart_resume_port
(
&
s3c24xx_uart_drv
,
port
);
}
return
0
;
}
#else
#define s3c24xx_serial_suspend NULL
#define s3c24xx_serial_resume NULL
#endif
int
s3c24xx_serial_init
(
struct
device_driver
*
drv
,
struct
s3c24xx_uart_info
*
info
)
{
dbg
(
"s3c24xx_serial_init(%p,%p)
\n
"
,
drv
,
info
);
return
driver_register
(
drv
);
}
/* now comes the code to initialise either the s3c2410 or s3c2440 serial
* port information
*/
/* cpu specific variations on the serial port support */
#ifdef CONFIG_CPU_S3C2410
static
int
s3c2410_serial_setsource
(
struct
uart_port
*
port
,
struct
s3c24xx_uart_clksrc
*
clk
)
{
unsigned
long
ucon
=
rd_regl
(
port
,
S3C2410_UCON
);
if
(
strcmp
(
clk
->
name
,
"uclk"
)
==
0
)
ucon
|=
S3C2410_UCON_UCLK
;
else
ucon
&=
~
S3C2410_UCON_UCLK
;
wr_regl
(
port
,
S3C2410_UCON
,
ucon
);
return
0
;
}
static
int
s3c2410_serial_getsource
(
struct
uart_port
*
port
,
struct
s3c24xx_uart_clksrc
*
clk
)
{
unsigned
long
ucon
=
rd_regl
(
port
,
S3C2410_UCON
);
clk
->
divisor
=
1
;
clk
->
name
=
(
ucon
&
S3C2410_UCON_UCLK
)
?
"uclk"
:
"pclk"
;
return
0
;
}
static
struct
s3c24xx_uart_info
s3c2410_uart_inf
=
{
.
name
=
"Samsung S3C2410 UART"
,
.
type
=
PORT_S3C2410
,
.
fifosize
=
16
,
.
rx_fifomask
=
S3C2410_UFSTAT_RXMASK
,
.
rx_fifoshift
=
S3C2410_UFSTAT_RXSHIFT
,
.
tx_fifofull
=
S3C2410_UFSTAT_TXFULL
,
.
tx_fifomask
=
S3C2410_UFSTAT_TXMASK
,
.
tx_fifoshift
=
S3C2410_UFSTAT_TXSHIFT
,
.
get_clksrc
=
s3c2410_serial_getsource
,
.
set_clksrc
=
s3c2410_serial_setsource
,
};
/* device management */
static
int
s3c2410_serial_probe
(
struct
device
*
dev
)
{
return
s3c24xx_serial_probe
(
dev
,
&
s3c2410_uart_inf
);
}
static
struct
device_driver
s3c2410_serial_drv
=
{
.
name
=
"s3c2410-uart"
,
.
bus
=
&
platform_bus_type
,
.
probe
=
s3c2410_serial_probe
,
.
remove
=
s3c24xx_serial_remove
,
.
suspend
=
s3c24xx_serial_suspend
,
.
resume
=
s3c24xx_serial_resume
,
};
static
inline
int
s3c2410_serial_init
(
void
)
{
return
s3c24xx_serial_init
(
&
s3c2410_serial_drv
,
&
s3c2410_uart_inf
);
}
static
inline
void
s3c2410_serial_exit
(
void
)
{
driver_unregister
(
&
s3c2410_serial_drv
);
}
#define s3c2410_uart_inf_at &s3c2410_uart_inf
#else
static
inline
int
s3c2410_serial_init
(
void
)
{
return
0
;
}
static
inline
void
s3c2410_serial_exit
(
void
)
{
}
#define s3c2410_uart_inf_at NULL
#endif
/* CONFIG_CPU_S3C2410 */
#ifdef CONFIG_CPU_S3C2440
static
int
s3c2440_serial_setsource
(
struct
uart_port
*
port
,
struct
s3c24xx_uart_clksrc
*
clk
)
{
unsigned
long
ucon
=
rd_regl
(
port
,
S3C2410_UCON
);
// todo - proper fclk<>nonfclk switch //
ucon
&=
~
S3C2440_UCON_CLKMASK
;
if
(
strcmp
(
clk
->
name
,
"uclk"
)
==
0
)
ucon
|=
S3C2440_UCON_UCLK
;
else
if
(
strcmp
(
clk
->
name
,
"pclk"
)
==
0
)
ucon
|=
S3C2440_UCON_PCLK
;
else
if
(
strcmp
(
clk
->
name
,
"fclk"
)
==
0
)
ucon
|=
S3C2440_UCON_FCLK
;
else
{
printk
(
KERN_ERR
"unknown clock source %s
\n
"
,
clk
->
name
);
return
-
EINVAL
;
}
wr_regl
(
port
,
S3C2410_UCON
,
ucon
);
return
0
;
}
static
int
s3c2440_serial_getsource
(
struct
uart_port
*
port
,
struct
s3c24xx_uart_clksrc
*
clk
)
{
unsigned
long
ucon
=
rd_regl
(
port
,
S3C2410_UCON
);
switch
(
ucon
&
S3C2440_UCON_CLKMASK
)
{
case
S3C2440_UCON_UCLK
:
clk
->
divisor
=
1
;
clk
->
name
=
"uclk"
;
break
;
case
S3C2440_UCON_PCLK
:
case
S3C2440_UCON_PCLK2
:
clk
->
divisor
=
1
;
clk
->
name
=
"pclk"
;
break
;
case
S3C2440_UCON_FCLK
:
clk
->
divisor
=
7
;
/* todo - work out divisor */
clk
->
name
=
"fclk"
;
break
;
}
return
0
;
}
static
struct
s3c24xx_uart_info
s3c2440_uart_inf
=
{
.
name
=
"Samsung S3C2440 UART"
,
.
type
=
PORT_S3C2440
,
.
fifosize
=
64
,
.
rx_fifomask
=
S3C2440_UFSTAT_RXMASK
,
.
rx_fifoshift
=
S3C2440_UFSTAT_RXSHIFT
,
.
tx_fifofull
=
S3C2440_UFSTAT_TXFULL
,
.
tx_fifomask
=
S3C2440_UFSTAT_TXMASK
,
.
tx_fifoshift
=
S3C2440_UFSTAT_TXSHIFT
,
.
get_clksrc
=
s3c2440_serial_getsource
,
.
set_clksrc
=
s3c2440_serial_setsource
};
/* device management */
static
int
s3c2440_serial_probe
(
struct
device
*
dev
)
{
dbg
(
"s3c2440_serial_probe: dev=%p
\n
"
,
dev
);
return
s3c24xx_serial_probe
(
dev
,
&
s3c2440_uart_inf
);
}
static
struct
device_driver
s3c2440_serial_drv
=
{
.
name
=
"s3c2440-uart"
,
.
bus
=
&
platform_bus_type
,
.
probe
=
s3c2440_serial_probe
,
.
remove
=
s3c24xx_serial_remove
,
.
suspend
=
s3c24xx_serial_suspend
,
.
resume
=
s3c24xx_serial_resume
,
};
static
inline
int
s3c2440_serial_init
(
void
)
{
return
s3c24xx_serial_init
(
&
s3c2440_serial_drv
,
&
s3c2440_uart_inf
);
}
static
inline
void
s3c2440_serial_exit
(
void
)
{
driver_unregister
(
&
s3c2440_serial_drv
);
}
#define s3c2440_uart_inf_at &s3c2440_uart_inf
#else
static
inline
int
s3c2440_serial_init
(
void
)
{
return
0
;
}
static
inline
void
s3c2440_serial_exit
(
void
)
{
}
#define s3c2440_uart_inf_at NULL
#endif
/* CONFIG_CPU_S3C2440 */
/* module initialisation code */
if
(
ptr
->
mapbase
==
0
)
static
int
__init
s3c24xx_serial_modinit
(
void
)
continue
;
{
int
ret
;
/* reset the fifos (and setup the uart */
ret
=
uart_register_driver
(
&
s3c24xx_uart_drv
);
serial_s3c2410_resetport
(
ptr
,
cfg
);
if
(
ret
<
0
)
{
printk
(
KERN_ERR
"failed to register UART driver
\n
"
);
return
-
1
;
}
}
s3c2410_serial_init
();
s3c2440_serial_init
();
return
0
;
return
0
;
}
}
static
void
__exit
s3c24xx_serial_modexit
(
void
)
{
s3c2410_serial_exit
();
s3c2440_serial_exit
();
uart_unregister_driver
(
&
s3c24xx_uart_drv
);
}
module_init
(
s3c24xx_serial_modinit
);
module_exit
(
s3c24xx_serial_modexit
);
/* Console code */
#ifdef CONFIG_SERIAL_S3C2410_CONSOLE
#ifdef CONFIG_SERIAL_S3C2410_CONSOLE
static
struct
uart_port
*
cons_uart
;
static
struct
uart_port
*
cons_uart
;
static
int
static
int
s
erial_s3c2410
_console_txrdy
(
struct
uart_port
*
port
,
unsigned
int
ufcon
)
s
3c24xx_serial
_console_txrdy
(
struct
uart_port
*
port
,
unsigned
int
ufcon
)
{
{
struct
s3c24xx_uart_info
*
info
=
s3c24xx_port_to_info
(
port
);
unsigned
long
ufstat
,
utrstat
;
unsigned
long
ufstat
,
utrstat
;
if
(
ufcon
&
S3C2410_UFCON_FIFOMODE
)
{
if
(
ufcon
&
S3C2410_UFCON_FIFOMODE
)
{
/* fifo mode - check ammount of data in fifo registers... */
/* fifo mode - check ammount of data in fifo registers... */
ufstat
=
rd_regl
(
port
,
S3C2410_UFSTAT
);
ufstat
=
rd_regl
(
port
,
S3C2410_UFSTAT
);
return
(
ufstat
&
info
->
tx_fifofull
)
?
0
:
1
;
return
S3C2410_UFCON_TXC
(
ufstat
)
<
12
;
}
}
/* in non-fifo mode, we go and use the tx buffer empty */
/* in non-fifo mode, we go and use the tx buffer empty */
utrstat
=
rd_regl
(
port
,
S3C2410_UTRSTAT
);
utrstat
=
rd_regl
(
port
,
S3C2410_UTRSTAT
);
return
(
utrstat
&
S3C2410_UTRSTAT_TXE
)
?
1
:
0
;
return
(
utrstat
&
S3C2410_UTRSTAT_TXFE
)
?
1
:
0
;
}
}
static
void
static
void
s
erial_s3c2410
_console_write
(
struct
console
*
co
,
const
char
*
s
,
s
3c24xx_serial
_console_write
(
struct
console
*
co
,
const
char
*
s
,
unsigned
int
count
)
unsigned
int
count
)
{
{
int
i
;
int
i
;
unsigned
int
ufcon
=
rd_regl
(
cons_uart
,
S3C2410_UFCON
);
unsigned
int
ufcon
=
rd_regl
(
cons_uart
,
S3C2410_UFCON
);
for
(
i
=
0
;
i
<
count
;
i
++
)
{
for
(
i
=
0
;
i
<
count
;
i
++
)
{
while
(
!
s
erial_s3c2410
_console_txrdy
(
cons_uart
,
ufcon
))
while
(
!
s
3c24xx_serial
_console_txrdy
(
cons_uart
,
ufcon
))
barrier
();
barrier
();
wr_regb
(
cons_uart
,
S3C2410_UTXH
,
s
[
i
]);
wr_regb
(
cons_uart
,
S3C2410_UTXH
,
s
[
i
]);
if
(
s
[
i
]
==
'\n'
)
{
if
(
s
[
i
]
==
'\n'
)
{
while
(
!
s
erial_s3c2410
_console_txrdy
(
cons_uart
,
ufcon
))
while
(
!
s
3c24xx_serial
_console_txrdy
(
cons_uart
,
ufcon
))
barrier
();
barrier
();
wr_regb
(
cons_uart
,
S3C2410_UTXH
,
'\r'
);
wr_regb
(
cons_uart
,
S3C2410_UTXH
,
'\r'
);
...
@@ -627,17 +1441,21 @@ serial_s3c2410_console_write(struct console *co, const char *s,
...
@@ -627,17 +1441,21 @@ serial_s3c2410_console_write(struct console *co, const char *s,
}
}
static
void
__init
static
void
__init
s
erial_s3c2410
_get_options
(
struct
uart_port
*
port
,
int
*
baud
,
s
3c24xx_serial
_get_options
(
struct
uart_port
*
port
,
int
*
baud
,
int
*
parity
,
int
*
bits
)
int
*
parity
,
int
*
bits
)
{
{
struct
s3c24xx_uart_clksrc
clksrc
;
unsigned
int
ulcon
,
ucon
,
ubrdiv
;
struct
clk
*
clk
;
unsigned
int
ulcon
;
unsigned
int
ucon
;
unsigned
int
ubrdiv
;
unsigned
long
rate
;
ulcon
=
rd_regl
(
port
,
S3C2410_ULCON
);
ulcon
=
rd_regl
(
port
,
S3C2410_ULCON
);
ucon
=
rd_regl
(
port
,
S3C2410_UCON
);
ucon
=
rd_regl
(
port
,
S3C2410_UCON
);
ubrdiv
=
rd_regl
(
port
,
S3C2410_UBRDIV
);
ubrdiv
=
rd_regl
(
port
,
S3C2410_UBRDIV
);
dbg
(
"s
erial_s3c2410
_get_options: port=%p
\n
"
dbg
(
"s
3c24xx_serial
_get_options: port=%p
\n
"
"registers: ulcon=%08x, ucon=%08x, ubdriv=%08x
\n
"
,
"registers: ulcon=%08x, ucon=%08x, ubdriv=%08x
\n
"
,
port
,
ulcon
,
ucon
,
ubrdiv
);
port
,
ulcon
,
ucon
,
ubrdiv
);
...
@@ -669,21 +1487,53 @@ serial_s3c2410_get_options(struct uart_port *port, int *baud,
...
@@ -669,21 +1487,53 @@ serial_s3c2410_get_options(struct uart_port *port, int *baud,
*
parity
=
'o'
;
*
parity
=
'o'
;
break
;
break
;
default:
case
S3C2410_LCON_PNONE
:
case
S3C2410_LCON_PNONE
:
default:
*
parity
=
'n'
;
*
parity
=
'n'
;
}
}
/* now calculate the baud rate */
/* now calculate the baud rate */
*
baud
=
port
->
uartclk
/
(
16
*
(
ubrdiv
+
1
));
s3c24xx_serial_getsource
(
port
,
&
clksrc
);
clk
=
clk_get
(
port
->
dev
,
clksrc
.
name
);
if
(
!
IS_ERR
(
clk
)
&&
clk
!=
NULL
)
rate
=
clk_get_rate
(
clk
);
else
rate
=
1
;
*
baud
=
rate
/
(
16
*
(
ubrdiv
+
1
));
dbg
(
"calculated baud %d
\n
"
,
*
baud
);
dbg
(
"calculated baud %d
\n
"
,
*
baud
);
}
}
}
}
/* s3c24xx_serial_init_ports
*
* initialise the serial ports from the machine provided initialisation
* data.
*/
static
int
s3c24xx_serial_init_ports
(
struct
s3c24xx_uart_info
*
info
)
{
struct
s3c24xx_uart_port
*
ptr
=
s3c24xx_serial_ports
;
struct
platform_device
**
platdev_ptr
;
int
i
;
dbg
(
"s3c24xx_serial_init_ports: initialising ports...
\n
"
);
platdev_ptr
=
s3c24xx_uart_devs
;
for
(
i
=
0
;
i
<
NR_PORTS
;
i
++
,
ptr
++
,
platdev_ptr
++
)
{
s3c24xx_serial_init_port
(
ptr
,
info
,
*
platdev_ptr
);
}
return
0
;
}
static
int
__init
static
int
__init
s
erial_s3c2410
_console_setup
(
struct
console
*
co
,
char
*
options
)
s
3c24xx_serial
_console_setup
(
struct
console
*
co
,
char
*
options
)
{
{
struct
uart_port
*
port
;
struct
uart_port
*
port
;
int
baud
=
9600
;
int
baud
=
9600
;
...
@@ -691,23 +1541,26 @@ serial_s3c2410_console_setup(struct console *co, char *options)
...
@@ -691,23 +1541,26 @@ serial_s3c2410_console_setup(struct console *co, char *options)
int
parity
=
'n'
;
int
parity
=
'n'
;
int
flow
=
'n'
;
int
flow
=
'n'
;
dbg
(
"s3c24xx_serial_console_setup: co=%p (%d), %s
\n
"
,
co
,
co
->
index
,
options
);
/* is this a valid port */
/* is this a valid port */
if
(
co
->
index
==
-
1
||
co
->
index
>=
NR_PORTS
)
if
(
co
->
index
==
-
1
||
co
->
index
>=
NR_PORTS
)
co
->
index
=
0
;
co
->
index
=
0
;
port
=
&
s
erial_s3c2410_ports
[
co
->
index
]
;
port
=
&
s
3c24xx_serial_ports
[
co
->
index
].
port
;
/* is the port configured? */
/* is the port configured? */
if
(
port
->
mapbase
==
0x0
)
{
if
(
port
->
mapbase
==
0x0
)
{
co
->
index
=
0
;
co
->
index
=
0
;
port
=
&
s
erial_s3c2410_ports
[
co
->
index
]
;
port
=
&
s
3c24xx_serial_ports
[
co
->
index
].
port
;
}
}
cons_uart
=
port
;
cons_uart
=
port
;
dbg
(
"s
erial_s3c2410
_console_setup: port=%p (%d)
\n
"
,
port
,
co
->
index
);
dbg
(
"s
3c24xx_serial
_console_setup: port=%p (%d)
\n
"
,
port
,
co
->
index
);
/*
/*
* Check whether an invalid uart number has been specified, and
* Check whether an invalid uart number has been specified, and
...
@@ -717,138 +1570,68 @@ serial_s3c2410_console_setup(struct console *co, char *options)
...
@@ -717,138 +1570,68 @@ serial_s3c2410_console_setup(struct console *co, char *options)
if
(
options
)
if
(
options
)
uart_parse_options
(
options
,
&
baud
,
&
parity
,
&
bits
,
&
flow
);
uart_parse_options
(
options
,
&
baud
,
&
parity
,
&
bits
,
&
flow
);
else
else
serial_s3c2410_get_options
(
port
,
&
baud
,
&
parity
,
&
bits
);
s3c24xx_serial_get_options
(
port
,
&
baud
,
&
parity
,
&
bits
);
dbg
(
"s3c24xx_serial_console_setup: baud %d
\n
"
,
baud
);
return
uart_set_options
(
port
,
co
,
baud
,
parity
,
bits
,
flow
);
return
uart_set_options
(
port
,
co
,
baud
,
parity
,
bits
,
flow
);
}
}
static
struct
uart_driver
s3c2410_uart_drv
;
/* s3c24xx_serial_initconsole
*
* initialise the console from one of the uart drivers
*/
static
struct
console
s
erial_s3c2410
_console
=
static
struct
console
s
3c24xx_serial
_console
=
{
{
.
name
=
SERIAL_S3C2410_NAME
,
.
name
=
S3C24XX_SERIAL_NAME
,
.
write
=
serial_s3c2410_console_write
,
.
device
=
uart_console_device
,
.
device
=
uart_console_device
,
.
setup
=
serial_s3c2410_console_setup
,
.
flags
=
CON_PRINTBUFFER
,
.
flags
=
CON_PRINTBUFFER
,
.
index
=
-
1
,
.
index
=
-
1
,
.
data
=
&
s3c2410_uart_drv
,
.
write
=
s3c24xx_serial_console_write
,
};
.
setup
=
s3c24xx_serial_console_setup
static
int
__init
s3c2410_console_init
(
void
)
{
dbg
(
"s3c2410_console_init:
\n
"
);
serial_s3c2410_init_ports
();
register_console
(
&
serial_s3c2410_console
);
return
0
;
}
console_initcall
(
s3c2410_console_init
);
#define SERIAL_S3C2410_CONSOLE &serial_s3c2410_console
#else
#define SERIAL_S3C2410_CONSOLE NULL
#endif
static
struct
uart_driver
s3c2410_uart_drv
=
{
.
owner
=
THIS_MODULE
,
.
driver_name
=
SERIAL_S3C2410_NAME
,
.
dev_name
=
SERIAL_S3C2410_NAME
,
.
major
=
SERIAL_S3C2410_MAJOR
,
.
minor
=
SERIAL_S3C2410_MINOR
,
.
nr
=
3
,
.
cons
=
SERIAL_S3C2410_CONSOLE
,
};
/* device driver */
static
int
s3c2410_serial_probe
(
struct
device
*
_dev
);
static
int
s3c2410_serial_remove
(
struct
device
*
_dev
);
static
struct
device_driver
s3c2410_serial_drv
=
{
.
name
=
"s3c2410-uart"
,
.
bus
=
&
platform_bus_type
,
.
probe
=
s3c2410_serial_probe
,
.
remove
=
s3c2410_serial_remove
,
.
suspend
=
NULL
,
.
resume
=
NULL
,
};
};
#define s3c2410_dev_to_port(__dev) (struct uart_port *)dev_get_drvdata(__dev)
static
int
s3c24
10_serial_probe
(
struct
device
*
_dev
)
static
int
s3c24
xx_serial_initconsole
(
void
)
{
{
struct
platform_device
*
dev
=
to_platform_device
(
_dev
);
struct
s3c24xx_uart_info
*
info
;
struct
resource
*
res
=
dev
->
resource
;
struct
platform_device
*
dev
=
s3c24xx_uart_devs
[
0
];
int
i
;
dbg
(
"s3c24
10_serial_probe: dev=%p, _dev=%p, res=%p
\n
"
,
_dev
,
dev
,
res
);
dbg
(
"s3c24
xx_serial_initconsole
\n
"
);
for
(
i
=
0
;
i
<
dev
->
num_resources
;
i
++
,
res
++
)
/* select driver based on the cpu */
if
(
res
->
flags
&
IORESOURCE_MEM
)
break
;
if
(
i
<
dev
->
num_resources
)
{
struct
uart_port
*
ptr
=
serial_s3c2410_ports
;
for
(
i
=
0
;
i
<
NR_PORTS
;
i
++
,
ptr
++
)
{
if
(
dev
==
NULL
)
{
dbg
(
"s3c2410_serial_probe: ptr=%p (%08x, %08x)
\n
"
,
printk
(
KERN_ERR
"s3c24xx: no devices for console init
\n
"
);
ptr
,
ptr
->
mapbase
,
ptr
->
membase
);
return
0
;
if
(
ptr
->
mapbase
!=
res
->
start
)
continue
;
dbg
(
"s3c2410_serial_probe: got device %p: port=%p
\n
"
,
_dev
,
ptr
);
uart_add_one_port
(
&
s3c2410_uart_drv
,
ptr
);
dev_set_drvdata
(
_dev
,
ptr
);
break
;
}
}
}
return
0
;
if
(
strcmp
(
dev
->
name
,
"s3c2410-uart"
)
==
0
)
{
}
info
=
s3c2410_uart_inf_at
;
}
else
if
(
strcmp
(
dev
->
name
,
"s3c2440-uart"
)
==
0
)
{
info
=
s3c2440_uart_inf_at
;
}
else
{
printk
(
KERN_ERR
"s3c24xx: no driver for %s
\n
"
,
dev
->
name
);
return
0
;
}
static
int
s3c2410_serial_remove
(
struct
device
*
_dev
)
if
(
info
==
NULL
)
{
{
printk
(
KERN_ERR
"s3c24xx: no driver for console
\n
"
);
struct
uart_port
*
port
=
s3c2410_dev_to_port
(
_dev
);
return
0
;
}
if
(
port
)
s3c24xx_serial_console
.
data
=
&
s3c24xx_uart_drv
;
uart_remove_one_port
(
&
s3c2410_uart_drv
,
port
);
s3c24xx_serial_init_ports
(
info
);
register_console
(
&
s3c24xx_serial_console
);
return
0
;
return
0
;
}
}
console_initcall
(
s3c24xx_serial_initconsole
);
#endif
/* CONFIG_SERIAL_S3C2410_CONSOLE */
static
int
__init
serial_s3c2410_init
(
void
)
{
int
ret
;
printk
(
KERN_INFO
"S3C2410X Serial, (c) 2003 Simtec Electronics
\n
"
);
ret
=
uart_register_driver
(
&
s3c2410_uart_drv
);
if
(
ret
!=
0
)
return
ret
;
ret
=
driver_register
(
&
s3c2410_serial_drv
);
if
(
ret
)
{
uart_unregister_driver
(
&
s3c2410_uart_drv
);
}
return
ret
;
}
static
void
__exit
serial_s3c2410_exit
(
void
)
{
driver_unregister
(
&
s3c2410_serial_drv
);
uart_unregister_driver
(
&
s3c2410_uart_drv
);
}
module_init
(
serial_s3c2410_init
);
module_exit
(
serial_s3c2410_exit
);
MODULE_LICENSE
(
"GPL"
);
MODULE_LICENSE
(
"GPL"
);
MODULE_AUTHOR
(
"Ben Dooks <ben@simtec.co.uk>"
);
MODULE_AUTHOR
(
"Ben Dooks <ben@simtec.co.uk>"
);
MODULE_DESCRIPTION
(
"Samsung S3C2410
X (S3C2410) Serial
driver"
);
MODULE_DESCRIPTION
(
"Samsung S3C2410
/S3C2440 Serial port
driver"
);
include/asm-arm/arch-s3c2410/regs-serial.h
View file @
70ca2c43
...
@@ -68,6 +68,12 @@
...
@@ -68,6 +68,12 @@
#define S3C2410_LCON_STOPB (1<<2)
#define S3C2410_LCON_STOPB (1<<2)
#define S3C2410_LCON_IRM (1<<6)
#define S3C2410_LCON_IRM (1<<6)
#define S3C2440_UCON_CLKMASK (3<<10)
#define S3C2440_UCON_PCLK (0<<10)
#define S3C2440_UCON_UCLK (1<<10)
#define S3C2440_UCON_PCLK2 (2<<10)
#define S3C2440_UCON_FCLK (3<<10)
#define S3C2410_UCON_UCLK (1<<10)
#define S3C2410_UCON_UCLK (1<<10)
#define S3C2410_UCON_SBREAK (1<<4)
#define S3C2410_UCON_SBREAK (1<<4)
...
@@ -77,19 +83,35 @@
...
@@ -77,19 +83,35 @@
#define S3C2410_UCON_RXIRQMODE (1<<0)
#define S3C2410_UCON_RXIRQMODE (1<<0)
#define S3C2410_UCON_RXFIFO_TOI (1<<7)
#define S3C2410_UCON_RXFIFO_TOI (1<<7)
#define S3C2410_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | S3C2410_UCON_RXILEVEL \
#define S3C2410_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | \
| S3C2410_UCON_TXIRQMODE | S3C2410_UCON_RXIRQMODE \
S3C2410_UCON_RXILEVEL | \
| S3C2410_UCON_RXFIFO_TOI)
S3C2410_UCON_TXIRQMODE | \
S3C2410_UCON_RXIRQMODE | \
S3C2410_UCON_RXFIFO_TOI)
#define S3C2410_UFCON_FIFOMODE (1<<0)
#define S3C2410_UFCON_FIFOMODE (1<<0)
#define S3C2410_UFCON_TXTRIG0 (0<<6)
#define S3C2410_UFCON_TXTRIG0 (0<<6)
#define S3C2410_UFCON_RXTRIG8 (1<<4)
#define S3C2410_UFCON_RXTRIG8 (1<<4)
#define S3C2410_UFCON_RXTRIG12 (2<<4)
#define S3C2410_UFCON_RXTRIG12 (2<<4)
/* S3C2440 FIFO trigger levels */
#define S3C2440_UFCON_RXTRIG1 (0<<4)
#define S3C2440_UFCON_RXTRIG8 (1<<4)
#define S3C2440_UFCON_RXTRIG16 (2<<4)
#define S3C2440_UFCON_RXTRIG32 (3<<4)
#define S3C2440_UFCON_TXTRIG0 (0<<6)
#define S3C2440_UFCON_TXTRIG16 (1<<6)
#define S3C2440_UFCON_TXTRIG32 (2<<6)
#define S3C2440_UFCON_TXTRIG48 (3<<6)
#define S3C2410_UFCON_RESETBOTH (3<<1)
#define S3C2410_UFCON_RESETBOTH (3<<1)
#define S3C2410_UFCON_RESETTX (1<<2)
#define S3C2410_UFCON_RESETRX (1<<1)
#define S3C2410_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | S3C2410_UFCON_TXTRIG0 \
#define S3C2410_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | \
| S3C2410_UFCON_RXTRIG8 )
S3C2410_UFCON_TXTRIG0 | \
S3C2410_UFCON_RXTRIG8 )
#define S3C2410_UFSTAT_TXFULL (1<<9)
#define S3C2410_UFSTAT_TXFULL (1<<9)
#define S3C2410_UFSTAT_RXFULL (1<<8)
#define S3C2410_UFSTAT_RXFULL (1<<8)
...
@@ -111,32 +133,36 @@
...
@@ -111,32 +133,36 @@
#define S3C2410_UERSTAT_OVERRUN (1<<0)
#define S3C2410_UERSTAT_OVERRUN (1<<0)
#define S3C2410_UERSTAT_FRAME (1<<2)
#define S3C2410_UERSTAT_FRAME (1<<2)
#define S3C2410_UERSTAT_ANY (S3C2410_UERSTAT_OVERRUN | S3C2410_UERSTAT_FRAME)
#define S3C2410_UERSTAT_BREAK (1<<3)
#define S3C2410_UERSTAT_ANY (S3C2410_UERSTAT_OVERRUN | \
S3C2410_UERSTAT_FRAME | \
S3C2410_UERSTAT_BREAK)
/* fifo size information */
#define S3C2410_UMSTAT_CTS (1<<0)
#define S3C2410_UMSTAT_DeltaCTS (1<<2)
#ifndef __ASSEMBLY__
#ifndef __ASSEMBLY__
static
inline
int
S3C2410_UFCON_RXC
(
int
fcon
)
{
if
(
fcon
&
S3C2410_UFSTAT_RXFULL
)
return
16
;
return
((
fcon
)
&
S3C2410_UFSTAT_RXMASK
)
>>
S3C2410_UFSTAT_RXSHIFT
;
}
static
inline
int
S3C2410_UFCON_TXC
(
int
fcon
)
/* struct s3c24xx_uart_clksrc
{
*
if
(
fcon
&
S3C2410_UFSTAT_TXFULL
)
* this structure defines a named clock source that can be used for the
return
16
;
* uart, so that the best clock can be selected for the requested baud
* rate.
return
((
fcon
)
&
S3C2410_UFSTAT_TXMASK
)
>>
S3C2410_UFSTAT_TXSHIFT
;
*
}
* min_baud and max_baud define the range of baud-rates this clock is
#endif
/* __ASSEMBLY__ */
* acceptable for, if they are both zero, it is assumed any baud rate that
* can be generated from this clock will be used.
*
* divisor gives the divisor from the clock to the one seen by the uart
*/
#define S3C2410_UMSTAT_CTS (1<<0)
struct
s3c24xx_uart_clksrc
{
#define S3C2410_UMSTAT_DeltaCTS (1<<2)
const
char
*
name
;
unsigned
int
divisor
;
unsigned
int
min_baud
;
unsigned
int
max_baud
;
};
#ifndef __ASSEMBLY__
/* configuration structure for per-machine configurations for the
/* configuration structure for per-machine configurations for the
* serial port
* serial port
*
*
...
@@ -148,15 +174,23 @@ struct s3c2410_uartcfg {
...
@@ -148,15 +174,23 @@ struct s3c2410_uartcfg {
unsigned
char
hwport
;
/* hardware port number */
unsigned
char
hwport
;
/* hardware port number */
unsigned
char
unused
;
unsigned
char
unused
;
unsigned
short
flags
;
unsigned
short
flags
;
unsigned
long
uart_flags
;
/* default uart flags */
unsigned
long
*
clock
;
/* pointer to clock rate */
unsigned
long
ucon
;
/* value of ucon for port */
unsigned
long
ucon
;
/* value of ucon for port */
unsigned
long
ulcon
;
/* value of ulcon for port */
unsigned
long
ulcon
;
/* value of ulcon for port */
unsigned
long
ufcon
;
/* value of ufcon for port */
unsigned
long
ufcon
;
/* value of ufcon for port */
struct
s3c24xx_uart_clksrc
*
clocks
;
unsigned
int
clocks_size
;
};
};
extern
struct
s3c2410_uartcfg
*
s3c2410_uartcfgs
;
/* s3c24xx_uart_devs
*
* this is exported from the core as we cannot use driver_register(),
* or platform_add_device() before the console_initcall()
*/
extern
struct
platform_device
*
s3c24xx_uart_devs
[
3
];
#endif
/* __ASSEMBLY__ */
#endif
/* __ASSEMBLY__ */
...
...
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