Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
linux-seco-imx
Manage
Activity
Members
Labels
Plan
Issues
0
Issue boards
Milestones
Iterations
Jira
Code
Merge requests
11
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Locked files
Deploy
Releases
Package Registry
Model registry
Operate
Terraform modules
Analyze
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Clea OS
bsp
nxp
linux-seco-imx
Commits
19649d46
Commit
19649d46
authored
3 years ago
by
Davide Cardillo
Browse files
Options
Downloads
Patches
Plain Diff
[i.MX6][CPLD][driver/seco] Add driver v2.1 for SECO CPLD LPC/GPIO
Add LPC tty device: /dev/lpctty#
parent
13493f42
Loading
Loading
No related merge requests found
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
drivers/tty/serial/Kconfig
+9
-0
9 additions, 0 deletions
drivers/tty/serial/Kconfig
drivers/tty/serial/Makefile
+1
-0
1 addition, 0 deletions
drivers/tty/serial/Makefile
drivers/tty/serial/lpc_serial.c
+1085
-0
1085 additions, 0 deletions
drivers/tty/serial/lpc_serial.c
with
1095 additions
and
0 deletions
drivers/tty/serial/Kconfig
+
9
−
0
View file @
19649d46
...
@@ -1538,6 +1538,15 @@ config SERIAL_OWL_CONSOLE
...
@@ -1538,6 +1538,15 @@ config SERIAL_OWL_CONSOLE
Say 'Y' here if you wish to use Actions Semiconductor S500/S900 UART
Say 'Y' here if you wish to use Actions Semiconductor S500/S900 UART
as the system console.
as the system console.
config SERIAL_LPC_SIO
tristate "SuperIO Serial port via LPC bus"
depends on OF
depends on SECO_CPLD_FW
depends on SERIAL_8250
help
The QuadMo747 supports the LPC Bridge to communicate with
a superIO which has UART controller.
config SERIAL_RDA
config SERIAL_RDA
bool "RDA Micro serial port support"
bool "RDA Micro serial port support"
depends on ARCH_RDA || COMPILE_TEST
depends on ARCH_RDA || COMPILE_TEST
...
...
This diff is collapsed.
Click to expand it.
drivers/tty/serial/Makefile
+
1
−
0
View file @
19649d46
...
@@ -96,3 +96,4 @@ obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o
...
@@ -96,3 +96,4 @@ obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o
obj-$(CONFIG_SERIAL_KGDB_NMI)
+=
kgdb_nmi.o
obj-$(CONFIG_SERIAL_KGDB_NMI)
+=
kgdb_nmi.o
obj-$(CONFIG_KGDB_SERIAL_CONSOLE)
+=
kgdboc.o
obj-$(CONFIG_KGDB_SERIAL_CONSOLE)
+=
kgdboc.o
obj-$(CONFIG_SERIAL_LPC_SIO)
+=
lpc_serial.o
This diff is collapsed.
Click to expand it.
drivers/tty/serial/lpc_serial.c
0 → 100644
+
1085
−
0
View file @
19649d46
#include
<linux/module.h>
#include
<linux/ioport.h>
#include
<linux/init.h>
#include
<linux/console.h>
#include
<linux/sysrq.h>
#include
<linux/platform_device.h>
#include
<linux/tty.h>
#include
<linux/tty_flip.h>
#include
<linux/serial_core.h>
#include
<linux/serial.h>
#include
<linux/serial_reg.h>
#include
<linux/clk.h>
#include
<linux/delay.h>
#include
<linux/of_irq.h>
#include
<linux/rational.h>
#include
<linux/slab.h>
#include
<linux/of.h>
#include
<linux/of_device.h>
#include
<linux/io.h>
#include
<linux/gpio.h>
#include
<linux/of_gpio.h>
#include
<asm/irq.h>
#include
<linux/sched/signal.h>
#include
<linux/workqueue.h>
#include
<linux/sio_lpc_serial.h>
#define DRIVER_NAME "lpc_serial"
#define DEV_NAME "ttylpc"
#define UART_NR 2
#define SIO_UART_INFO(fmt, arg...) printk(KERN_INFO "UART SIO LPC: " fmt "\n" , ## arg)
#define SIO_UART_ERR(fmt, arg...) printk(KERN_ERR "%s: " fmt "\n" , __func__ , ## arg)
#define SIO_UART_DBG(fmt, arg...) pr_debug("%s: " fmt "\n" , __func__ , ## arg)
//static struct workqueue_struct *serial_wq;
static
unsigned
char
lpc_rx_chars
(
struct
lpc_serial_port
*
lpc_port
,
unsigned
char
lsr
);
static
struct
of_device_id
of_platform_serial_table
[];
static
struct
lpc_serial_port
*
lpc_ports
[
UART_NR
];
static
void
lpc_clear_fifos
(
struct
uart_port
*
port
)
{
struct
lpc_serial_port
*
lpc_port
=
container_of
(
port
,
struct
lpc_serial_port
,
port
);
if
(
!
lpc_port
)
return
;
lpc_port
->
serial_out
(
port
,
UART_FCR
,
UART_FCR_ENABLE_FIFO
);
lpc_port
->
serial_out
(
port
,
UART_FCR
,
UART_FCR_ENABLE_FIFO
|
UART_FCR_CLEAR_RCVR
|
UART_FCR_CLEAR_XMIT
);
lpc_port
->
serial_out
(
port
,
UART_FCR
,
0
);
}
static
unsigned
int
lpc_tx_empty
(
struct
uart_port
*
port
)
{
struct
lpc_serial_port
*
lpc_port
=
container_of
(
port
,
struct
lpc_serial_port
,
port
);
unsigned
long
flags
;
unsigned
int
lsr
;
if
(
!
lpc_port
)
return
0
;
spin_lock_irqsave
(
&
port
->
lock
,
flags
);
lsr
=
lpc_port
->
serial_in
(
port
,
UART_LSR
);
spin_unlock_irqrestore
(
&
port
->
lock
,
flags
);
return
(
lsr
&
(
UART_LSR_TEMT
|
UART_LSR_THRE
))
==
(
UART_LSR_TEMT
|
UART_LSR_THRE
)
?
TIOCSER_TEMT
:
0
;
}
static
unsigned
int
get_modem_status
(
struct
uart_port
*
port
)
{
struct
lpc_serial_port
*
lpc_port
=
container_of
(
port
,
struct
lpc_serial_port
,
port
);
unsigned
int
status
;
if
(
!
lpc_port
)
return
0
;
status
=
lpc_port
->
serial_in
(
port
,
UART_MSR
);
status
|=
lpc_port
->
uart_reg
[
UART_MSR
];
lpc_port
->
uart_reg
[
UART_MSR
]
=
0
;
if
(
status
&
UART_MSR_ANY_DELTA
&&
lpc_port
->
uart_reg
[
UART_IER
]
&&
port
->
state
!=
NULL
)
{
if
(
status
&
UART_MSR_TERI
)
port
->
icount
.
rng
++
;
if
(
status
&
UART_MSR_DDSR
)
port
->
icount
.
dsr
++
;
if
(
status
&
UART_MSR_DDCD
)
uart_handle_dcd_change
(
port
,
status
&
UART_MSR_DCD
);
if
(
status
&
UART_MSR_DCTS
)
uart_handle_cts_change
(
port
,
status
&
UART_MSR_CTS
);
wake_up_interruptible
(
&
port
->
state
->
port
.
delta_msr_wait
);
}
return
status
;
}
static
unsigned
int
lpc_get_mctrl
(
struct
uart_port
*
port
)
{
struct
lpc_serial_port
*
lpc_port
=
container_of
(
port
,
struct
lpc_serial_port
,
port
);
unsigned
int
status
;
unsigned
int
ret
=
0
;
if
(
!
lpc_port
)
return
0
;
status
=
get_modem_status
(
port
);
if
(
status
&
UART_MSR_DCD
)
ret
|=
TIOCM_CAR
;
if
(
status
&
UART_MSR_RI
)
ret
|=
TIOCM_RNG
;
if
(
status
&
UART_MSR_DSR
)
ret
|=
TIOCM_DSR
;
if
(
status
&
UART_MSR_CTS
)
ret
|=
TIOCM_CTS
;
return
ret
;
}
static
void
lpc_set_mctrl
(
struct
uart_port
*
port
,
unsigned
int
mctrl
)
{
struct
lpc_serial_port
*
lpc_port
=
container_of
(
port
,
struct
lpc_serial_port
,
port
);
unsigned
char
mcr
=
0
;
if
(
!
lpc_port
)
return
;
if
(
mctrl
&
TIOCM_RTS
&&
!
lpc_port
->
is_rs485
)
mcr
|=
UART_MCR_RTS
;
if
(
mctrl
&
TIOCM_DTR
)
mcr
|=
UART_MCR_DTR
;
if
(
mctrl
&
TIOCM_LOOP
)
mcr
|=
UART_MCR_LOOP
;
if
(
mctrl
&
TIOCM_OUT2
)
mcr
|=
UART_MCR_OUT2
;
lpc_port
->
serial_out
(
&
lpc_port
->
port
,
UART_MCR
,
mcr
);
}
static
void
lpc_set_rts_status
(
struct
lpc_serial_port
*
lpc_port
,
int
val
)
{
struct
uart_port
*
port
=
&
lpc_port
->
port
;
unsigned
int
status
=
lpc_port
->
serial_in
(
port
,
UART_MCR
);
status
=
val
?
status
|
UART_MCR_RTS
:
status
&
~
UART_MCR_RTS
;
lpc_port
->
serial_out
(
port
,
UART_MCR
,
status
);
}
static
void
lpc_set_silent_read
(
struct
lpc_serial_port
*
lpc_port
,
int
en
)
{
struct
uart_port
*
port
=
&
lpc_port
->
port
;
unsigned
int
status
=
lpc_port
->
serial_in
(
port
,
UART_IER
);
status
=
!
en
?
status
|
(
UART_IER_RDI
|
UART_IER_RLSI
|
UART_IER_MSI
)
:
status
&
~
(
UART_IER_RDI
|
UART_IER_RLSI
|
UART_IER_MSI
);
lpc_port
->
serial_out
(
port
,
UART_IER
,
status
);
}
#define BOUDRATE_REFERENCE 115200
static
inline
int
__boudrate_getDelay
(
struct
lpc_serial_port
*
lpc_port
)
{
int
boud
=
lpc_port
->
port
.
uartclk
/
(
lpc_port
->
boudrate
*
16
);
if
(
boud
>
BOUDRATE_REFERENCE
)
boud
=
BOUDRATE_REFERENCE
;
return
(
8000000
/
boud
);
}
static
inline
int
__boudrate_byte_time
(
struct
lpc_serial_port
*
lpc_port
)
{
int
boud
=
lpc_port
->
port
.
uartclk
/
(
lpc_port
->
boudrate
*
16
);
if
(
boud
>
BOUDRATE_REFERENCE
)
boud
=
BOUDRATE_REFERENCE
;
return
((
120758
-
boud
)
/
15
)
+
1
;
}
#define WAIT_SLAVE_EVENT_USEC 100000
static
int
wait_uart_event
(
struct
lpc_serial_port
*
lpc_port
,
int
reg
,
unsigned
char
mask
,
int
val
)
{
int
ret
=
0
;
int
reg_val
;
ktime_t
orig_time
=
ktime_get
();
while
(
1
)
{
reg_val
=
lpc_port
->
serial_in
(
&
lpc_port
->
port
,
reg
);
if
(
!!
(
reg_val
&
mask
)
==
val
)
break
;
if
(
unlikely
(
fatal_signal_pending
(
current
))
)
{
ret
=
-
EINTR
;
break
;
}
if
(
ktime_to_us
(
ktime_sub
(
ktime_get
(),
orig_time
))
>=
WAIT_SLAVE_EVENT_USEC
)
{
ret
=
-
ETIMEDOUT
;
break
;
}
udelay
(
5
);
}
return
ret
;
}
static
void
end_transmission
(
struct
uart_port
*
port
)
{
struct
lpc_serial_port
*
lpc_port
=
container_of
(
port
,
struct
lpc_serial_port
,
port
);
unsigned
int
rts_delay
=
0
;
wait_uart_event
(
lpc_port
,
UART_LSR
,
UART_LSR_TEMT
,
1
);
if
(
lpc_port
->
is_rs485
)
{
if
(
lpc_port
->
is_half_duplex
)
{
lpc_set_silent_read
(
lpc_port
,
0
);
}
rts_delay
=
__boudrate_getDelay
(
lpc_port
);
udelay
(
rts_delay
);
lpc_set_rts_status
(
lpc_port
,
0
);
udelay
(
rts_delay
);
}
}
static
void
lpc_stop_tx
(
struct
uart_port
*
port
)
{
struct
lpc_serial_port
*
lpc_port
=
container_of
(
port
,
struct
lpc_serial_port
,
port
);
if
(
!
lpc_port
)
return
;
if
(
lpc_port
->
uart_reg
[
UART_IER
]
&
UART_IER_THRI
)
{
lpc_port
->
uart_reg
[
UART_IER
]
&=
~
UART_IER_THRI
;
lpc_port
->
serial_out
(
port
,
UART_IER
,
lpc_port
->
uart_reg
[
UART_IER
]);
}
lpc_port
->
tx_mode
=
0
;
}
static
unsigned
int
lpc_set_modem_status
(
struct
lpc_serial_port
*
lpc_port
)
{
struct
uart_port
*
port
=
&
lpc_port
->
port
;
unsigned
int
status
=
lpc_port
->
serial_in
(
port
,
UART_MSR
);
status
|=
lpc_port
->
uart_reg
[
UART_MSR
];
lpc_port
->
uart_reg
[
UART_MSR
]
=
0
;
if
(
status
&
UART_MSR_ANY_DELTA
&&
lpc_port
->
uart_reg
[
UART_IER
]
&
UART_IER_MSI
&&
port
->
state
!=
NULL
)
{
if
(
status
&
UART_MSR_TERI
)
port
->
icount
.
rng
++
;
if
(
status
&
UART_MSR_DDSR
)
port
->
icount
.
dsr
++
;
if
(
status
&
UART_MSR_DDCD
)
uart_handle_dcd_change
(
port
,
status
&
UART_MSR_DCD
);
if
(
status
&
UART_MSR_DCTS
)
uart_handle_cts_change
(
port
,
status
&
UART_MSR_CTS
);
wake_up_interruptible
(
&
port
->
state
->
port
.
delta_msr_wait
);
}
return
status
;
}
static
inline
void
transmit_buffer
(
struct
lpc_serial_port
*
lpc_port
)
{
int
payload_size
,
count
,
rts_delay
=
0
;
unsigned
char
lsr
;
unsigned
long
flags
;
struct
uart_port
*
port
=
&
lpc_port
->
port
;
struct
circ_buf
*
xmit
=
&
lpc_port
->
port
.
state
->
xmit
;
unsigned
long
time_byte
=
__boudrate_byte_time
(
lpc_port
);
ktime_t
orig_time
;
unsigned
long
delta_t
;
if
(
lpc_port
->
is_rs485
)
{
rts_delay
=
__boudrate_getDelay
(
lpc_port
);
lpc_set_rts_status
(
lpc_port
,
1
);
if
(
lpc_port
->
is_half_duplex
)
{
/* unset the flag for the Data Ready IRQ */
lpc_set_silent_read
(
lpc_port
,
1
);
}
}
udelay
(
rts_delay
);
lpc_port
->
tx_mode
=
1
;
if
(
lpc_port
->
payload_size
)
payload_size
=
lpc_port
->
payload_size
;
else
payload_size
=
1024
;
// virtual infinite size
spin_lock_irqsave
(
&
lpc_port
->
port
.
lock
,
flags
);
if
(
port
->
x_char
)
{
lpc_port
->
serial_out
(
port
,
UART_TX
,
port
->
x_char
);
port
->
icount
.
tx
++
;
port
->
x_char
=
0
;
goto
irq_en
;
}
if
(
uart_tx_stopped
(
port
)
||
uart_circ_empty
(
xmit
)
)
{
spin_unlock_irqrestore
(
&
lpc_port
->
port
.
lock
,
flags
);
lpc_stop_tx
(
port
);
return
;
}
count
=
0
;
orig_time
=
ktime_get
();
while
(
!
uart_circ_empty
(
xmit
)
&&
(
++
count
<=
payload_size
))
{
lpc_port
->
serial_out
(
port
,
UART_TX
,
xmit
->
buf
[
xmit
->
tail
]);
xmit
->
tail
=
(
xmit
->
tail
+
1
)
&
(
UART_XMIT_SIZE
-
1
);
port
->
icount
.
tx
++
;
if
(
count
%
(
port
->
fifosize
/
4
)
==
0
)
{
delta_t
=
ktime_to_us
(
ktime_sub
(
ktime_get
(),
orig_time
));
if
(
time_byte
>
delta_t
)
udelay
(
time_byte
-
delta_t
);
orig_time
=
ktime_get
();
lsr
=
(
unsigned
char
)
lpc_port
->
serial_in
(
&
lpc_port
->
port
,
UART_LSR
);
lsr
=
lpc_rx_chars
(
lpc_port
,
lsr
);
}
}
if
(
lpc_port
->
is_half_duplex
)
{
delta_t
=
ktime_to_us
(
ktime_sub
(
ktime_get
(),
orig_time
));
if
(
time_byte
>
delta_t
)
udelay
(
time_byte
-
delta_t
);
/* flush RX buffer */
lsr
=
(
unsigned
char
)
lpc_port
->
serial_in
(
&
lpc_port
->
port
,
UART_LSR
);
lsr
=
lpc_rx_chars
(
lpc_port
,
lsr
);
}
end_transmission
(
port
);
if
(
uart_circ_chars_pending
(
xmit
)
<
WAKEUP_CHARS
)
{
uart_write_wakeup
(
port
);
}
wait_uart_event
(
lpc_port
,
UART_LSR
,
UART_LSR_TEMT
,
1
);
if
(
uart_circ_empty
(
xmit
)
)
{
lpc_stop_tx
(
port
);
spin_unlock_irqrestore
(
&
lpc_port
->
port
.
lock
,
flags
);
}
else
{
spin_unlock_irqrestore
(
&
lpc_port
->
port
.
lock
,
flags
);
tasklet_schedule
(
&
lpc_port
->
tasklet
);
}
return
;
irq_en:
end_transmission
(
port
);
lpc_port
->
tx_mode
=
0
;
spin_unlock_irqrestore
(
&
lpc_port
->
port
.
lock
,
flags
);
}
static
void
work_tx
(
unsigned
long
data
)
{
struct
lpc_serial_port
*
lpc_port
=
(
struct
lpc_serial_port
*
)
data
;
transmit_buffer
(
lpc_port
);
}
static
void
lpc_start_tx
(
struct
uart_port
*
port
)
{
struct
lpc_serial_port
*
lpc_port
=
container_of
(
port
,
struct
lpc_serial_port
,
port
);
if
(
!
lpc_port
)
return
;
if
(
!
(
lpc_port
->
uart_reg
[
UART_IER
]
&
UART_IER_THRI
)
)
{
lpc_port
->
uart_reg
[
UART_IER
]
|=
UART_IER_THRI
;
lpc_port
->
serial_out
(
port
,
UART_IER
,
lpc_port
->
uart_reg
[
UART_IER
]);
// this action will rise an UART interrupt
}
}
static
void
lpc_stop_rx
(
struct
uart_port
*
port
)
{
struct
lpc_serial_port
*
lpc_port
=
container_of
(
port
,
struct
lpc_serial_port
,
port
);
lpc_port
->
uart_reg
[
UART_IER
]
&=
~
UART_IER_RLSI
;
lpc_port
->
port
.
read_status_mask
&=
~
UART_LSR_DR
;
lpc_port
->
serial_out
(
port
,
UART_IER
,
lpc_port
->
uart_reg
[
UART_IER
]);
}
static
void
lpc_enable_ms
(
struct
uart_port
*
port
)
{
struct
lpc_serial_port
*
lpc_port
=
container_of
(
port
,
struct
lpc_serial_port
,
port
);
lpc_port
->
uart_reg
[
UART_IER
]
|=
UART_IER_MSI
;
lpc_port
->
serial_out
(
port
,
UART_IER
,
lpc_port
->
uart_reg
[
UART_IER
]);
}
static
void
lpc_break_ctl
(
struct
uart_port
*
port
,
int
break_state
)
{
struct
lpc_serial_port
*
lpc_port
=
container_of
(
port
,
struct
lpc_serial_port
,
port
);
unsigned
long
flags
;
if
(
!
lpc_port
)
return
;
spin_lock_irqsave
(
&
port
->
lock
,
flags
);
if
(
break_state
==
-
1
)
lpc_port
->
uart_reg
[
UART_LCR
]
|=
UART_LCR_SBC
;
else
lpc_port
->
uart_reg
[
UART_LCR
]
&=
~
UART_LCR_SBC
;
lpc_port
->
serial_out
(
port
,
UART_LCR
,
lpc_port
->
uart_reg
[
UART_LCR
]);
spin_unlock_irqrestore
(
&
port
->
lock
,
flags
);
}
static
unsigned
char
lpc_rx_chars
(
struct
lpc_serial_port
*
lpc_port
,
unsigned
char
lsr
)
{
unsigned
char
ch
;
char
flag
;
int
max_count
=
256
;
struct
uart_port
*
port
=
&
lpc_port
->
port
;
if
(
!
port
)
return
0x00
;
do
{
if
(
likely
(
lsr
&
UART_LSR_DR
)
)
ch
=
lpc_port
->
serial_in
(
port
,
UART_RX
);
else
if
(
!
lpc_port
->
tx_mode
)
ch
=
0x00
;
else
return
lsr
;
flag
=
TTY_NORMAL
;
port
->
icount
.
rx
++
;
lsr
|=
lpc_port
->
uart_reg
[
UART_LSR
];
lpc_port
->
uart_reg
[
UART_LSR
]
=
0
;
if
(
!
uart_handle_sysrq_char
(
port
,
ch
)
)
{
uart_insert_char
(
port
,
lsr
,
UART_LSR_OE
,
ch
,
flag
);
}
lsr
=
(
unsigned
char
)
lpc_port
->
serial_in
(
&
lpc_port
->
port
,
UART_LSR
);
}
while
(
(
lsr
&
(
UART_LSR_DR
|
UART_LSR_BI
))
&&
(
max_count
--
>
0
)
);
spin_unlock
(
&
port
->
lock
);
tty_flip_buffer_push
(
&
port
->
state
->
port
);
spin_lock
(
&
port
->
lock
);
return
lsr
;
}
#define INT_PENDING_MASK 0x01
#define INT_CODE_MASK 0x0E
#define INT_CODE_PRIOR_1 0x03 // UART Receive Status
#define INT_CODE_PRIOR_2A 0x02 // RBR Data Ready
#define INT_CODE_PRIOR_2B 0x06 // FIFO Data Timeout
#define INT_CODE_PRIOR_3 0x01 // TBR Empty
#define INT_CODE_PRIOR_4 0x00 // Handshake status
static
irqreturn_t
lpc_serial_isr
(
int
irq
,
void
*
dev_id
)
{
int
*
i
=
(
int
*
)
dev_id
;
unsigned
char
int_pending
,
int_code
,
iir
,
lsr
;
unsigned
long
flags
;
int
already_unlock
=
0
;
struct
lpc_serial_port
*
lpc_port
;
if
(
*
i
>=
UART_NR
)
{
return
IRQ_RETVAL
(
0
);
}
lpc_port
=
lpc_ports
[
*
i
];
if
(
!
lpc_port
)
return
IRQ_RETVAL
(
0
);
spin_lock_irqsave
(
&
lpc_port
->
port
.
lock
,
flags
);
iir
=
(
unsigned
char
)
lpc_port
->
serial_in
(
&
lpc_port
->
port
,
UART_IIR
);
lsr
=
(
unsigned
char
)
lpc_port
->
serial_in
(
&
lpc_port
->
port
,
UART_LSR
);
int_pending
=
iir
&
INT_PENDING_MASK
;
if
(
int_pending
==
0
)
{
// there is an interrupt to manage
int_code
=
(
iir
&
INT_CODE_MASK
)
>>
1
;
switch
(
int_code
)
{
case
INT_CODE_PRIOR_1
:
// read LSR registr -> operation already performed
break
;
case
INT_CODE_PRIOR_2A
:
lpc_port
->
rx_lsr
=
lsr
;
lsr
=
lpc_rx_chars
(
lpc_port
,
lsr
);
break
;
case
INT_CODE_PRIOR_2B
:
lpc_port
->
rx_lsr
=
lsr
;
lsr
=
lpc_rx_chars
(
lpc_port
,
lsr
);
break
;
case
INT_CODE_PRIOR_3
:
if
(
lsr
&
UART_LSR_THRE
)
{
lpc_set_modem_status
(
lpc_port
);
already_unlock
=
1
;
lpc_port
->
tx_mode
=
0
;
spin_unlock_irqrestore
(
&
lpc_port
->
port
.
lock
,
flags
);
tasklet_schedule
(
&
lpc_port
->
tasklet
);
}
break
;
case
INT_CODE_PRIOR_4
:
break
;
default:
break
;
}
}
if
(
!
already_unlock
)
{
spin_unlock_irqrestore
(
&
lpc_port
->
port
.
lock
,
flags
);
}
return
IRQ_HANDLED
;
}
static
int
lpc_startup
(
struct
uart_port
*
port
)
{
struct
lpc_serial_port
*
lpc_port
=
container_of
(
port
,
struct
lpc_serial_port
,
port
);
unsigned
long
flags
;
int
retval
;
if
(
!
lpc_port
)
return
-
ENODEV
;
lpc_port
->
uart_reg
[
UART_MCR
]
=
0
;
lpc_clear_fifos
(
port
);
/* Clear the interrupt registers */
lpc_port
->
serial_in
(
port
,
UART_LSR
);
lpc_port
->
serial_in
(
port
,
UART_RX
);
lpc_port
->
serial_in
(
port
,
UART_IIR
);
lpc_port
->
serial_in
(
port
,
UART_MSR
);
/* UART init */
lpc_port
->
serial_out
(
port
,
UART_LCR
,
UART_LCR_WLEN8
);
spin_lock_irqsave
(
&
port
->
lock
,
flags
);
if
(
port
->
irq
)
lpc_port
->
port
.
mctrl
|=
TIOCM_OUT2
;
if
(
lpc_port
->
is_rs485
)
{
lpc_port
->
port
.
mctrl
&=
~
TIOCM_RTS
;
}
lpc_set_mctrl
(
port
,
port
->
mctrl
);
spin_unlock_irqrestore
(
&
port
->
lock
,
flags
);
if
(
port
->
irq
)
{
spin_lock_irqsave
(
&
port
->
lock
,
flags
);
if
(
port
->
irqflags
&
IRQF_SHARED
)
disable_irq_nosync
(
port
->
irq
);
retval
=
request_any_context_irq
(
lpc_port
->
port
.
irq
,
lpc_serial_isr
,
IRQ_TYPE_EDGE_FALLING
,
"tty_lpc"
,
&
lpc_port
->
port
.
line
);
if
(
retval
<
0
)
{
return
-
EINVAL
;
}
spin_unlock_irqrestore
(
&
port
->
lock
,
flags
);
}
/* do test */
/* Clear the interrupt registers */
lpc_port
->
serial_in
(
port
,
UART_LSR
);
lpc_port
->
serial_in
(
port
,
UART_RX
);
lpc_port
->
serial_in
(
port
,
UART_IIR
);
lpc_port
->
serial_in
(
port
,
UART_MSR
);
lpc_port
->
uart_reg
[
UART_MSR
]
=
0
;
lpc_port
->
uart_reg
[
UART_IER
]
=
UART_IER_RLSI
|
UART_IER_RDI
;
lpc_port
->
serial_out
(
port
,
UART_IER
,
lpc_port
->
uart_reg
[
UART_IER
]);
lpc_port
->
uart_reg
[
UART_FCR
]
=
UART_FCR
|
UART_FCR_R_TRIG_11
|
UART_FCR_ENABLE_FIFO
;
lpc_port
->
serial_out
(
port
,
UART_FCR
,
UART_FCR_R_TRIG_11
|
UART_FCR_ENABLE_FIFO
);
if
(
lpc_port
->
is_rs485
)
{
lpc_set_rts_status
(
lpc_port
,
0
);
}
return
0
;
}
static
void
lpc_shutdown
(
struct
uart_port
*
port
)
{
struct
lpc_serial_port
*
lpc_port
=
container_of
(
port
,
struct
lpc_serial_port
,
port
);
unsigned
long
flags
;
if
(
!
lpc_port
)
return
;
// free IRQ
free_irq
(
port
->
irq
,
&
lpc_port
->
port
.
line
);
lpc_port
->
uart_reg
[
UART_IIR
]
=
0
;
lpc_port
->
serial_out
(
port
,
UART_IIR
,
lpc_port
->
uart_reg
[
UART_IIR
]);
spin_lock_irqsave
(
&
port
->
lock
,
flags
);
port
->
mctrl
&=
~
TIOCM_OUT2
;
// irq disable
if
(
lpc_port
->
is_rs485
)
port
->
mctrl
&=
~
TIOCM_RTS
;
lpc_set_mctrl
(
port
,
port
->
mctrl
);
spin_unlock_irqrestore
(
&
port
->
lock
,
flags
);
lpc_port
->
serial_out
(
port
,
UART_LCR
,
lpc_port
->
serial_in
(
port
,
UART_LCR
)
&
~
UART_LCR_SBC
);
lpc_clear_fifos
(
port
);
// flush buffer
lpc_port
->
serial_in
(
port
,
UART_RX
);
}
static
void
lpc_flush_buffer
(
struct
uart_port
*
port
)
{}
static
void
lpc_set_termios
(
struct
uart_port
*
port
,
struct
ktermios
*
termios
,
struct
ktermios
*
old
)
{
struct
lpc_serial_port
*
lpc_port
=
container_of
(
port
,
struct
lpc_serial_port
,
port
);
unsigned
char
cval
,
fval
;
unsigned
int
baud
,
quot
;
unsigned
long
flags
;
if
(
!
lpc_port
)
return
;
/* data length */
switch
(
termios
->
c_cflag
&
CSIZE
)
{
case
CS5
:
cval
=
UART_LCR_WLEN5
;
break
;
case
CS6
:
cval
=
UART_LCR_WLEN6
;
break
;
case
CS7
:
cval
=
UART_LCR_WLEN7
;
break
;
case
CS8
:
cval
=
UART_LCR_WLEN8
;
break
;
default:
cval
=
UART_LCR_WLEN8
;
}
/* stop bit
* if UART_LCR_WLEN5 -> 1.5 bit stop
* if UART_LCR_WLEN6, UART_LCR_WLEN7, UART_LCR_WLEN8 -> 2 bits stop
*/
if
(
termios
->
c_cflag
&
CSTOPB
)
{
cval
|=
UART_LCR_STOP
;
}
/* parity bit */
if
(
termios
->
c_cflag
&
PARENB
)
{
cval
|=
UART_LCR_PARITY
;
}
if
(
!
(
termios
->
c_cflag
&
PARODD
)
)
{
cval
|=
UART_LCR_EPAR
;
}
/* boudrate */
baud
=
uart_get_baud_rate
(
port
,
termios
,
old
,
port
->
uartclk
/
16
/
0xffff
,
port
->
uartclk
/
16
);
quot
=
uart_get_divisor
(
port
,
baud
);
spin_lock_irqsave
(
&
port
->
lock
,
flags
);
/* Update the per-port timeout. */
uart_update_timeout
(
port
,
termios
->
c_cflag
,
baud
);
/* FIFO Setting */
fval
=
UART_FCR_ENABLE_FIFO
;
// enable fifo
fval
&=
~
UART_FCR_DMA_SELECT
;
// disable DMA feature
fval
&=
~
UART_FCR_TRIGGER_MASK
;
fval
|=
UART_FCR_TRIGGER_8
;
// set FIFO size to 8 bytes
fval
|=
(
UART_FCR_CLEAR_RCVR
|
UART_FCR_CLEAR_XMIT
);
// reset TX and RX FIFO counter
/* Characteres to ignore */
lpc_port
->
port
.
ignore_status_mask
=
0
;
if
(
termios
->
c_iflag
&
IGNPAR
)
lpc_port
->
port
.
ignore_status_mask
|=
UART_LSR_PE
|
UART_LSR_FE
;
if
(
termios
->
c_iflag
&
IGNBRK
)
lpc_port
->
port
.
ignore_status_mask
|=
UART_LSR_BI
;
if
(
(
termios
->
c_iflag
&
(
IGNPAR
|
IGNBRK
))
==
(
IGNPAR
|
IGNBRK
)
)
lpc_port
->
port
.
ignore_status_mask
|=
UART_LSR_OE
;
if
(
(
termios
->
c_cflag
&
CREAD
)
==
0
)
lpc_port
->
port
.
ignore_status_mask
|=
UART_LSR_DR
;
lpc_port
->
serial_out
(
&
lpc_port
->
port
,
UART_LCR
,
cval
|
UART_LCR_DLAB
);
lpc_port
->
serial_out
(
&
lpc_port
->
port
,
UART_DLL
,
quot
&
0xFF
);
lpc_port
->
serial_out
(
&
lpc_port
->
port
,
UART_DLM
,
(
quot
>>
8
)
&
0xFF
);
lpc_port
->
serial_out
(
&
lpc_port
->
port
,
UART_LCR
,
cval
);
// restore bank
if
(
fval
&
UART_FCR_ENABLE_FIFO
)
{
/* two steps setting */
lpc_port
->
serial_out
(
&
lpc_port
->
port
,
UART_FCR
,
UART_FCR_ENABLE_FIFO
);
lpc_port
->
serial_out
(
&
lpc_port
->
port
,
UART_FCR
,
fval
);
}
if
(
lpc_port
->
is_rs485
)
port
->
mctrl
&=
~
TIOCM_RTS
;
lpc_set_mctrl
(
port
,
port
->
mctrl
);
lpc_port
->
boudrate
=
quot
;
if
(
tty_termios_baud_rate
(
termios
)
)
tty_termios_encode_baud_rate
(
termios
,
baud
,
baud
);
spin_unlock_irqrestore
(
&
port
->
lock
,
flags
);
}
static
const
char
*
lpc_type
(
struct
uart_port
*
port
)
{
struct
lpc_serial_port
*
lpc_port
=
container_of
(
port
,
struct
lpc_serial_port
,
port
);
if
(
!
lpc_port
)
return
NULL
;
switch
(
lpc_port
->
type
)
{
case
LPC_PORT_TYPE_W83627
:
return
"W83627"
;
case
LPC_PORT_TYPE_XR28V382
:
return
"xr28v382"
;
default:
return
NULL
;
}
}
static
void
lpc_config_port
(
struct
uart_port
*
port
,
int
flags
)
{
struct
lpc_serial_port
*
lpc_port
=
container_of
(
port
,
struct
lpc_serial_port
,
port
);
if
(
!
lpc_port
)
return
;
port
->
type
=
PORT_LPC
;
}
static
int
lpc_verify_port
(
struct
uart_port
*
port
,
struct
serial_struct
*
ser
)
{
if
(
ser
->
irq
>=
nr_irqs
||
ser
->
irq
<
0
)
return
-
EINVAL
;
if
(
ser
->
baud_base
>
115200
)
return
-
EINVAL
;
if
(
ser
->
type
<
PORT_UNKNOWN
)
return
-
EINVAL
;
return
0
;
}
static
void
lpc_console_write
(
struct
console
*
co
,
const
char
*
s
,
unsigned
int
count
)
{}
static
int
__init
lpc_console_setup
(
struct
console
*
co
,
char
*
options
)
{
struct
uart_port
*
port
;
struct
lpc_serial_port
*
lpc_port
;
int
baud
=
9600
;
int
bits
=
8
;
int
parity
=
'n'
;
int
flow
=
'n'
;
if
(
co
->
index
==
-
1
||
co
->
index
>
2
)
{
co
->
index
=
0
;
}
lpc_port
=
lpc_ports
[
co
->
index
];
if
(
lpc_port
==
NULL
)
return
-
ENODEV
;
port
=
&
lpc_port
->
port
;
if
(
options
)
uart_parse_options
(
options
,
&
baud
,
&
parity
,
&
bits
,
&
flow
);
return
uart_set_options
(
port
,
co
,
baud
,
parity
,
bits
,
flow
);
}
static
struct
uart_ops
lpc_pops
=
{
.
tx_empty
=
lpc_tx_empty
,
.
set_mctrl
=
lpc_set_mctrl
,
.
get_mctrl
=
lpc_get_mctrl
,
.
stop_tx
=
lpc_stop_tx
,
.
start_tx
=
lpc_start_tx
,
.
stop_rx
=
lpc_stop_rx
,
.
enable_ms
=
lpc_enable_ms
,
.
break_ctl
=
lpc_break_ctl
,
.
startup
=
lpc_startup
,
.
shutdown
=
lpc_shutdown
,
.
flush_buffer
=
lpc_flush_buffer
,
.
set_termios
=
lpc_set_termios
,
.
type
=
lpc_type
,
.
config_port
=
lpc_config_port
,
.
verify_port
=
lpc_verify_port
,
};
static
struct
uart_driver
lpc_reg
;
static
struct
console
lpc_console
=
{
.
name
=
DEV_NAME
,
.
write
=
lpc_console_write
,
.
device
=
uart_console_device
,
.
setup
=
lpc_console_setup
,
.
flags
=
CON_PRINTBUFFER
,
.
index
=
-
1
,
.
data
=
&
lpc_reg
,
};
static
struct
uart_driver
lpc_reg
=
{
.
owner
=
THIS_MODULE
,
.
driver_name
=
DRIVER_NAME
,
.
dev_name
=
DEV_NAME
,
.
major
=
210
,
.
minor
=
64
,
.
nr
=
2
,
.
cons
=
&
lpc_console
,
};
static
void
setup_from_of
(
struct
device_node
*
np
,
struct
lpc_serial_port
*
lpc_port
)
{
u32
prop
;
lpc_port
->
payload_size
=
0
;
// means infinite size
if
(
of_property_read_u32
(
np
,
"device_id"
,
&
prop
)
==
0
)
lpc_port
->
uart_ldev
=
prop
;
if
(
of_property_read_u32
(
np
,
"membase"
,
&
prop
)
==
0
)
lpc_port
->
membase
=
prop
;
if
(
of_property_read_u32
(
np
,
"uart-clk"
,
&
prop
)
==
0
)
{
lpc_port
->
port
.
uartclk
=
prop
;
lpc_port
->
uart_clk
=
prop
;
}
if
(
of_property_read_u32
(
np
,
"reg-shift"
,
&
prop
)
==
0
)
lpc_port
->
port
.
regshift
=
prop
;
if
(
of_property_read_u32
(
np
,
"fifo-size"
,
&
prop
)
==
0
)
lpc_port
->
port
.
fifosize
=
prop
;
if
(
of_property_read_u32
(
np
,
"pl-size"
,
&
prop
)
==
0
)
lpc_port
->
payload_size
=
prop
;
lpc_port
->
is_rs485
=
of_property_read_bool
(
np
,
"rs485"
);
if
(
lpc_port
->
is_rs485
)
{
SIO_UART_INFO
(
"UART#%d used as RS485"
,
lpc_port
->
uart_index
);
}
lpc_port
->
is_half_duplex
=
of_property_read_bool
(
np
,
"half-duplex"
);
if
(
lpc_port
->
is_half_duplex
)
{
SIO_UART_INFO
(
"UART#%d used in half duplex mode"
,
lpc_port
->
uart_index
);
}
lpc_port
->
port
.
mapbase
=
(
resource_size_t
)
lpc_port
->
membase
;
lpc_port
->
port
.
irq
=
irq_of_parse_and_map
(
np
,
0
);
}
static
int
lpc_serial_probe
(
struct
platform_device
*
pdev
)
{
const
struct
of_device_id
*
match
;
struct
device_node
*
np
=
pdev
->
dev
.
of_node
;
struct
lpc_serial_port
*
lpc_port
;
struct
serial_data
*
data
;
int
ret
,
err
;
match
=
of_match_device
(
of_platform_serial_table
,
&
pdev
->
dev
);
if
(
!
match
)
return
-
EINVAL
;
lpc_port
=
kzalloc
(
sizeof
(
struct
lpc_serial_port
),
GFP_KERNEL
);
if
(
!
lpc_port
)
{
SIO_UART_ERR
(
"cannot allocate memory for structure data"
);
err
=
-
ENOMEM
;
goto
err_data_allocate
;
}
lpc_port
->
sio_ops
=
(
struct
sio_w83627_ops
*
)
dev_get_platdata
(
&
pdev
->
dev
);
if
(
!
lpc_port
->
sio_ops
)
{
SIO_UART_ERR
(
"cannot obtain ops structure data"
);
err
=
-
EINVAL
;
goto
err_ops_data
;
}
if
(
!
lpc_port
->
sio_ops
->
read
||
!
lpc_port
->
sio_ops
->
write
||
!
lpc_port
->
sio_ops
->
g_read
||
!
lpc_port
->
sio_ops
->
g_write
)
{
SIO_UART_ERR
(
"write and read operation not assigned"
);
err
=
-
EINVAL
;
goto
err_ops_data
;
}
data
=
(
struct
serial_data
*
)
match
->
data
;
if
(
!
data
)
{
SIO_UART_ERR
(
"cannot obtain serial data"
);
err
=
-
EINVAL
;
goto
err_serial_data
;
}
lpc_port
->
port_type
=
data
->
port_type
;
lpc_port
->
uart_index
=
data
->
uart_index
;
lpc_port
->
port
.
line
=
data
->
uart_index
;
setup_from_of
(
np
,
lpc_port
);
platform_set_drvdata
(
pdev
,
lpc_port
);
switch
(
data
->
port_type
)
{
case
LPC_PORT_TYPE_W83627
:
ret
=
lpc_w83627_platform_serial_setup
(
pdev
,
lpc_port
);
break
;
case
LPC_PORT_TYPE_XR28V382
:
ret
=
lpc_xr28v382_platform_serial_setup
(
pdev
,
lpc_port
);
break
;
default:
ret
=
-
1
;
}
if
(
ret
)
{
SIO_UART_ERR
(
"error while setting UART"
);
err
=
ret
;
goto
err_uart_setting
;
}
lpc_port
->
port
.
dev
=
&
pdev
->
dev
;
lpc_port
->
port
.
iotype
=
UPIO_LPC
;
lpc_port
->
port
.
type
=
PORT_LPC
;
lpc_port
->
port
.
ops
=
&
lpc_pops
;
lpc_port
->
port
.
flags
=
UPF_BOOT_AUTOCONF
;
ret
=
lpc_w83627_sys_serial_setup
(
pdev
,
lpc_port
);
if
(
ret
)
{
SIO_UART_ERR
(
"cannot create sysfs interface"
);
err
=
ret
;
goto
err_sysfs
;
}
lpc_ports
[
lpc_port
->
port
.
line
]
=
lpc_port
;
lpc_port
->
tx_mode
=
0
;
tasklet_init
(
&
lpc_port
->
tasklet
,
work_tx
,
(
unsigned
long
)
lpc_port
);
ret
=
uart_add_one_port
(
&
lpc_reg
,
&
lpc_port
->
port
);
SIO_UART_INFO
(
"UART#%d LPC SuperIO driver probed!!!"
,
lpc_port
->
uart_index
);
return
0
;
err_sysfs:
err_uart_setting:
err_serial_data:
err_ops_data:
err_data_allocate:
return
err
;
}
static
int
lpc_serial_remove
(
struct
platform_device
*
pdev
)
{
struct
lpc_serial_port
*
lpc_port
=
(
struct
lpc_serial_port
*
)
platform_get_drvdata
(
pdev
);
return
uart_remove_one_port
(
&
lpc_reg
,
&
lpc_port
->
port
);
}
static
struct
serial_data
w83627dhg_A_16550a
=
{
LPC_UART_TYPE_W83627_A
,
LPC_PORT_TYPE_W83627
};
static
struct
serial_data
w83627dhg_B_16550a
=
{
LPC_UART_TYPE_W83627_B
,
LPC_PORT_TYPE_W83627
};
static
struct
serial_data
xr28v382_A_16550a
=
{
LPC_UART_TYPE_XR28V382_A
,
LPC_PORT_TYPE_XR28V382
};
static
struct
serial_data
xr28v382_B_16550a
=
{
LPC_UART_TYPE_XR28V382_B
,
LPC_PORT_TYPE_XR28V382
};
static
struct
of_device_id
of_platform_serial_table
[]
=
{
{
.
compatible
=
"nuvoton,w83627dhg_A_16550a"
,
.
data
=
&
w83627dhg_A_16550a
,
},
{
.
compatible
=
"nuvoton,w83627dhg_B_16550a"
,
.
data
=
&
w83627dhg_B_16550a
,
},
{
.
compatible
=
"MaxLinear,xr28v382_A_16550a"
,
.
data
=
&
xr28v382_A_16550a
,
},
{
.
compatible
=
"MaxLinear,xr28v382_B_16550a"
,
.
data
=
&
xr28v382_B_16550a
,
},
{
/* end of list */
},
};
static
struct
platform_driver
serial_lpc_driver
=
{
.
probe
=
lpc_serial_probe
,
.
remove
=
lpc_serial_remove
,
.
suspend
=
NULL
,
.
resume
=
NULL
,
.
driver
=
{
.
name
=
"lpc_serial"
,
.
owner
=
THIS_MODULE
,
.
of_match_table
=
of_platform_serial_table
,
},
};
static
int
__init
lpc_serial_init
(
void
)
{
int
ret
;
pr_info
(
"Serial: LPC driver"
);
ret
=
uart_register_driver
(
&
lpc_reg
);
if
(
ret
)
return
ret
;
ret
=
platform_driver_register
(
&
serial_lpc_driver
);
if
(
ret
!=
0
)
uart_unregister_driver
(
&
lpc_reg
);
return
ret
;
}
static
void
__exit
lpc_serial_exit
(
void
)
{
platform_driver_unregister
(
&
serial_lpc_driver
);
uart_unregister_driver
(
&
lpc_reg
);
}
module_init
(
lpc_serial_init
);
module_exit
(
lpc_serial_exit
);
MODULE_AUTHOR
(
"Davide Cardillo, SECO srl"
);
MODULE_DESCRIPTION
(
"SuperIO LPC UART interface"
);
MODULE_LICENSE
(
"GPL"
);
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment