2017-11-06 18:11:51 +01:00
// SPDX-License-Identifier: GPL-2.0+
2005-04-16 15:20:36 -07:00
/*
* Driver for CPM ( SCC / SMC ) serial ports ; core driver
*
* Based on arch / ppc / cpm2_io / uart . c by Dan Malek
* Based on ppc8xx . c by Thomas Gleixner
* Based on drivers / serial / amba . c by Russell King
*
2005-11-13 16:06:30 -08:00
* Maintainer : Kumar Gala ( galak @ kernel . crashing . org ) ( CPM2 )
2005-04-16 15:20:36 -07:00
* Pantelis Antoniou ( panto @ intracom . gr ) ( CPM1 )
2005-08-09 10:08:00 -07:00
*
2007-07-17 17:59:06 -05:00
* Copyright ( C ) 2004 , 2007 Freescale Semiconductor , Inc .
2005-04-16 15:20:36 -07:00
* ( C ) 2004 Intracom , S . A .
2006-04-29 23:06:00 +04:00
* ( C ) 2005 - 2006 MontaVista Software , Inc .
2008-06-12 07:53:48 -05:00
* Vitaly Bordug < vbordug @ ru . mvista . com >
2005-04-16 15:20:36 -07:00
*/
# include <linux/module.h>
# include <linux/tty.h>
2011-09-01 16:20:57 +02:00
# include <linux/tty_flip.h>
2005-04-16 15:20:36 -07:00
# include <linux/ioport.h>
# include <linux/init.h>
# include <linux/serial.h>
# include <linux/console.h>
# include <linux/sysrq.h>
# include <linux/device.h>
2018-10-30 15:09:49 -07:00
# include <linux/memblock.h>
2005-04-16 15:20:36 -07:00
# include <linux/dma-mapping.h>
2013-09-17 14:28:33 -05:00
# include <linux/of_address.h>
# include <linux/of_irq.h>
2008-06-12 08:05:09 -05:00
# include <linux/of_platform.h>
2020-03-01 00:18:42 +01:00
# include <linux/gpio/consumer.h>
2008-07-28 10:42:16 +02:00
# include <linux/clk.h>
2005-04-16 15:20:36 -07:00
2023-08-03 15:56:45 +02:00
# include <sysdev/fsl_soc.h>
2005-04-16 15:20:36 -07:00
# include <asm/io.h>
# include <asm/irq.h>
# include <asm/delay.h>
2007-07-17 17:59:06 -05:00
# include <asm/udbg.h>
2005-04-16 15:20:36 -07:00
# include <linux/serial_core.h>
# include <linux/kernel.h>
# include "cpm_uart.h"
/**************************************************************/
static int cpm_uart_tx_pump ( struct uart_port * port ) ;
static void cpm_uart_initbd ( struct uart_cpm_port * pinfo ) ;
/**************************************************************/
2012-09-24 08:19:03 +02:00
# define HW_BUF_SPD_THRESHOLD 2400
2010-11-11 10:53:03 +01:00
2023-08-03 15:56:47 +02:00
static void cpm_line_cr_cmd ( struct uart_cpm_port * port , int cmd )
{
cpm_command ( port - > command , cmd ) ;
}
2005-04-16 15:20:36 -07:00
/*
2005-08-09 10:08:00 -07:00
* Check , if transmit buffers are processed
2005-04-16 15:20:36 -07:00
*/
static unsigned int cpm_uart_tx_empty ( struct uart_port * port )
{
2014-10-05 19:01:06 +02:00
struct uart_cpm_port * pinfo =
container_of ( port , struct uart_cpm_port , port ) ;
2007-07-24 15:53:07 -05:00
cbd_t __iomem * bdp = pinfo - > tx_bd_base ;
2005-04-16 15:20:36 -07:00
int ret = 0 ;
while ( 1 ) {
2007-07-24 15:53:07 -05:00
if ( in_be16 ( & bdp - > cbd_sc ) & BD_SC_READY )
2005-04-16 15:20:36 -07:00
break ;
2007-07-24 15:53:07 -05:00
if ( in_be16 ( & bdp - > cbd_sc ) & BD_SC_WRAP ) {
2005-04-16 15:20:36 -07:00
ret = TIOCSER_TEMT ;
break ;
}
bdp + + ;
}
pr_debug ( " CPM uart[%d]:tx_empty: %d \n " , port - > line , ret ) ;
return ret ;
}
static void cpm_uart_set_mctrl ( struct uart_port * port , unsigned int mctrl )
{
2014-10-05 19:01:06 +02:00
struct uart_cpm_port * pinfo =
container_of ( port , struct uart_cpm_port , port ) ;
2008-07-24 18:36:37 +02:00
2020-03-01 00:18:42 +01:00
if ( pinfo - > gpios [ GPIO_RTS ] )
gpiod_set_value ( pinfo - > gpios [ GPIO_RTS ] , ! ( mctrl & TIOCM_RTS ) ) ;
2008-07-24 18:36:37 +02:00
2020-03-01 00:18:42 +01:00
if ( pinfo - > gpios [ GPIO_DTR ] )
gpiod_set_value ( pinfo - > gpios [ GPIO_DTR ] , ! ( mctrl & TIOCM_DTR ) ) ;
2005-04-16 15:20:36 -07:00
}
static unsigned int cpm_uart_get_mctrl ( struct uart_port * port )
{
2014-10-05 19:01:06 +02:00
struct uart_cpm_port * pinfo =
container_of ( port , struct uart_cpm_port , port ) ;
2008-07-24 18:36:37 +02:00
unsigned int mctrl = TIOCM_CTS | TIOCM_DSR | TIOCM_CAR ;
2020-03-01 00:18:42 +01:00
if ( pinfo - > gpios [ GPIO_CTS ] ) {
if ( gpiod_get_value ( pinfo - > gpios [ GPIO_CTS ] ) )
2008-07-24 18:36:37 +02:00
mctrl & = ~ TIOCM_CTS ;
}
2020-03-01 00:18:42 +01:00
if ( pinfo - > gpios [ GPIO_DSR ] ) {
if ( gpiod_get_value ( pinfo - > gpios [ GPIO_DSR ] ) )
2008-07-24 18:36:37 +02:00
mctrl & = ~ TIOCM_DSR ;
}
2020-03-01 00:18:42 +01:00
if ( pinfo - > gpios [ GPIO_DCD ] ) {
if ( gpiod_get_value ( pinfo - > gpios [ GPIO_DCD ] ) )
2008-07-24 18:36:37 +02:00
mctrl & = ~ TIOCM_CAR ;
}
2020-03-01 00:18:42 +01:00
if ( pinfo - > gpios [ GPIO_RI ] ) {
if ( ! gpiod_get_value ( pinfo - > gpios [ GPIO_RI ] ) )
2008-07-24 18:36:37 +02:00
mctrl | = TIOCM_RNG ;
}
return mctrl ;
2005-04-16 15:20:36 -07:00
}
/*
* Stop transmitter
*/
2005-08-31 10:12:14 +01:00
static void cpm_uart_stop_tx ( struct uart_port * port )
2005-04-16 15:20:36 -07:00
{
2014-10-05 19:01:06 +02:00
struct uart_cpm_port * pinfo =
container_of ( port , struct uart_cpm_port , port ) ;
2007-07-24 15:53:07 -05:00
smc_t __iomem * smcp = pinfo - > smcp ;
scc_t __iomem * sccp = pinfo - > sccp ;
2005-04-16 15:20:36 -07:00
pr_debug ( " CPM uart[%d]:stop tx \n " , port - > line ) ;
if ( IS_SMC ( pinfo ) )
2007-07-24 15:53:07 -05:00
clrbits8 ( & smcp - > smc_smcm , SMCM_TX ) ;
2005-04-16 15:20:36 -07:00
else
2007-07-24 15:53:07 -05:00
clrbits16 ( & sccp - > scc_sccm , UART_SCCM_TX ) ;
2005-04-16 15:20:36 -07:00
}
/*
* Start transmitter
*/
2005-08-31 10:12:14 +01:00
static void cpm_uart_start_tx ( struct uart_port * port )
2005-04-16 15:20:36 -07:00
{
2014-10-05 19:01:06 +02:00
struct uart_cpm_port * pinfo =
container_of ( port , struct uart_cpm_port , port ) ;
2007-07-24 15:53:07 -05:00
smc_t __iomem * smcp = pinfo - > smcp ;
scc_t __iomem * sccp = pinfo - > sccp ;
2005-04-16 15:20:36 -07:00
pr_debug ( " CPM uart[%d]:start tx \n " , port - > line ) ;
if ( IS_SMC ( pinfo ) ) {
2007-07-24 15:53:07 -05:00
if ( in_8 ( & smcp - > smc_smcm ) & SMCM_TX )
2005-04-16 15:20:36 -07:00
return ;
} else {
2007-07-24 15:53:07 -05:00
if ( in_be16 ( & sccp - > scc_sccm ) & UART_SCCM_TX )
2005-04-16 15:20:36 -07:00
return ;
}
if ( cpm_uart_tx_pump ( port ) ! = 0 ) {
2005-08-09 10:08:00 -07:00
if ( IS_SMC ( pinfo ) ) {
2007-07-24 15:53:07 -05:00
setbits8 ( & smcp - > smc_smcm , SMCM_TX ) ;
2005-08-09 10:08:00 -07:00
} else {
2007-07-24 15:53:07 -05:00
setbits16 ( & sccp - > scc_sccm , UART_SCCM_TX ) ;
2005-08-09 10:08:00 -07:00
}
2005-04-16 15:20:36 -07:00
}
}
/*
2005-08-09 10:08:00 -07:00
* Stop receiver
2005-04-16 15:20:36 -07:00
*/
static void cpm_uart_stop_rx ( struct uart_port * port )
{
2014-10-05 19:01:06 +02:00
struct uart_cpm_port * pinfo =
container_of ( port , struct uart_cpm_port , port ) ;
2007-07-24 15:53:07 -05:00
smc_t __iomem * smcp = pinfo - > smcp ;
scc_t __iomem * sccp = pinfo - > sccp ;
2005-04-16 15:20:36 -07:00
pr_debug ( " CPM uart[%d]:stop rx \n " , port - > line ) ;
if ( IS_SMC ( pinfo ) )
2007-07-24 15:53:07 -05:00
clrbits8 ( & smcp - > smc_smcm , SMCM_RX ) ;
2005-04-16 15:20:36 -07:00
else
2007-07-24 15:53:07 -05:00
clrbits16 ( & sccp - > scc_sccm , UART_SCCM_RX ) ;
2005-04-16 15:20:36 -07:00
}
/*
2005-08-09 10:08:00 -07:00
* Generate a break .
2005-04-16 15:20:36 -07:00
*/
static void cpm_uart_break_ctl ( struct uart_port * port , int break_state )
{
2014-10-05 19:01:06 +02:00
struct uart_cpm_port * pinfo =
container_of ( port , struct uart_cpm_port , port ) ;
2005-04-16 15:20:36 -07:00
pr_debug ( " CPM uart[%d]:break ctrl, break_state: %d \n " , port - > line ,
break_state ) ;
if ( break_state )
2007-07-17 17:59:06 -05:00
cpm_line_cr_cmd ( pinfo , CPM_CR_STOP_TX ) ;
2005-04-16 15:20:36 -07:00
else
2007-07-17 17:59:06 -05:00
cpm_line_cr_cmd ( pinfo , CPM_CR_RESTART_TX ) ;
2005-04-16 15:20:36 -07:00
}
/*
* Transmit characters , refill buffer descriptor , if possible
*/
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 14:55:46 +01:00
static void cpm_uart_int_tx ( struct uart_port * port )
2005-04-16 15:20:36 -07:00
{
pr_debug ( " CPM uart[%d]:TX INT \n " , port - > line ) ;
cpm_uart_tx_pump ( port ) ;
}
2008-07-23 11:30:16 -05:00
# ifdef CONFIG_CONSOLE_POLL
static int serial_polled ;
# endif
2005-04-16 15:20:36 -07:00
/*
* Receive characters
*/
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 14:55:46 +01:00
static void cpm_uart_int_rx ( struct uart_port * port )
2005-04-16 15:20:36 -07:00
{
int i ;
2007-07-24 15:53:07 -05:00
unsigned char ch ;
u8 * cp ;
2013-01-03 15:53:01 +01:00
struct tty_port * tport = & port - > state - > port ;
2014-10-05 19:01:06 +02:00
struct uart_cpm_port * pinfo =
container_of ( port , struct uart_cpm_port , port ) ;
2007-07-24 15:53:07 -05:00
cbd_t __iomem * bdp ;
2005-04-16 15:20:36 -07:00
u16 status ;
unsigned int flg ;
pr_debug ( " CPM uart[%d]:RX INT \n " , port - > line ) ;
/* Just loop through the closed BDs and copy the characters into
* the buffer .
*/
bdp = pinfo - > rx_cur ;
for ( ; ; ) {
2008-07-23 11:30:16 -05:00
# ifdef CONFIG_CONSOLE_POLL
if ( unlikely ( serial_polled ) ) {
serial_polled = 0 ;
return ;
}
# endif
2005-04-16 15:20:36 -07:00
/* get status */
2007-07-24 15:53:07 -05:00
status = in_be16 ( & bdp - > cbd_sc ) ;
2005-04-16 15:20:36 -07:00
/* If this one is empty, return happy */
if ( status & BD_SC_EMPTY )
break ;
/* get number of characters, and check spce in flip-buffer */
2007-07-24 15:53:07 -05:00
i = in_be16 ( & bdp - > cbd_datlen ) ;
2005-04-16 15:20:36 -07:00
2005-08-09 10:08:00 -07:00
/* If we have not enough room in tty flip buffer, then we try
2005-04-16 15:20:36 -07:00
* later , which will be the next rx - interrupt or a timeout
*/
2013-01-03 15:53:01 +01:00
if ( tty_buffer_request_room ( tport , i ) < i ) {
2006-02-08 21:40:13 +00:00
printk ( KERN_WARNING " No room in flip buffer \n " ) ;
return ;
2005-04-16 15:20:36 -07:00
}
/* get pointer */
2007-07-24 15:53:07 -05:00
cp = cpm2cpu_addr ( in_be32 ( & bdp - > cbd_bufaddr ) , pinfo ) ;
2005-04-16 15:20:36 -07:00
/* loop through the buffer */
while ( i - - > 0 ) {
ch = * cp + + ;
port - > icount . rx + + ;
flg = TTY_NORMAL ;
if ( status &
( BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV ) )
goto handle_error ;
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 14:55:46 +01:00
if ( uart_handle_sysrq_char ( port , ch ) )
2005-04-16 15:20:36 -07:00
continue ;
2008-07-23 11:30:16 -05:00
# ifdef CONFIG_CONSOLE_POLL
if ( unlikely ( serial_polled ) ) {
serial_polled = 0 ;
return ;
}
# endif
2005-04-16 15:20:36 -07:00
error_return :
2013-01-03 15:53:03 +01:00
tty_insert_flip_char ( tport , ch , flg ) ;
2005-04-16 15:20:36 -07:00
} /* End while (i--) */
/* This BD is ready to be used again. Clear status. get next */
2007-07-24 15:53:07 -05:00
clrbits16 ( & bdp - > cbd_sc , BD_SC_BR | BD_SC_FR | BD_SC_PR |
BD_SC_OV | BD_SC_ID ) ;
setbits16 ( & bdp - > cbd_sc , BD_SC_EMPTY ) ;
2005-04-16 15:20:36 -07:00
2007-07-24 15:53:07 -05:00
if ( in_be16 ( & bdp - > cbd_sc ) & BD_SC_WRAP )
2005-04-16 15:20:36 -07:00
bdp = pinfo - > rx_bd_base ;
else
bdp + + ;
2005-08-09 10:08:00 -07:00
2005-04-16 15:20:36 -07:00
} /* End for (;;) */
/* Write back buffer pointer */
2007-07-24 15:53:07 -05:00
pinfo - > rx_cur = bdp ;
2005-04-16 15:20:36 -07:00
/* activate BH processing */
2013-01-03 15:53:06 +01:00
tty_flip_buffer_push ( tport ) ;
2005-04-16 15:20:36 -07:00
return ;
/* Error processing */
handle_error :
/* Statistics */
if ( status & BD_SC_BR )
port - > icount . brk + + ;
if ( status & BD_SC_PR )
port - > icount . parity + + ;
if ( status & BD_SC_FR )
port - > icount . frame + + ;
if ( status & BD_SC_OV )
port - > icount . overrun + + ;
/* Mask out ignored conditions */
status & = port - > read_status_mask ;
/* Handle the remaining ones */
if ( status & BD_SC_BR )
flg = TTY_BREAK ;
else if ( status & BD_SC_PR )
flg = TTY_PARITY ;
else if ( status & BD_SC_FR )
flg = TTY_FRAME ;
/* overrun does not affect the current character ! */
if ( status & BD_SC_OV ) {
ch = 0 ;
flg = TTY_OVERRUN ;
/* We skip this buffer */
/* CHECK: Is really nothing senseful there */
/* ASSUMPTION: it contains nothing valid */
i = 0 ;
}
port - > sysrq = 0 ;
goto error_return ;
}
/*
* Asynchron mode interrupt handler
*/
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 14:55:46 +01:00
static irqreturn_t cpm_uart_int ( int irq , void * data )
2005-04-16 15:20:36 -07:00
{
u8 events ;
2008-02-06 01:36:20 -08:00
struct uart_port * port = data ;
2005-04-16 15:20:36 -07:00
struct uart_cpm_port * pinfo = ( struct uart_cpm_port * ) port ;
2007-07-24 15:53:07 -05:00
smc_t __iomem * smcp = pinfo - > smcp ;
scc_t __iomem * sccp = pinfo - > sccp ;
2005-04-16 15:20:36 -07:00
pr_debug ( " CPM uart[%d]:IRQ \n " , port - > line ) ;
if ( IS_SMC ( pinfo ) ) {
2007-07-24 15:53:07 -05:00
events = in_8 ( & smcp - > smc_smce ) ;
out_8 ( & smcp - > smc_smce , events ) ;
2005-04-16 15:20:36 -07:00
if ( events & SMCM_BRKE )
uart_handle_break ( port ) ;
if ( events & SMCM_RX )
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 14:55:46 +01:00
cpm_uart_int_rx ( port ) ;
2005-04-16 15:20:36 -07:00
if ( events & SMCM_TX )
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 14:55:46 +01:00
cpm_uart_int_tx ( port ) ;
2005-04-16 15:20:36 -07:00
} else {
2007-07-24 15:53:07 -05:00
events = in_be16 ( & sccp - > scc_scce ) ;
out_be16 ( & sccp - > scc_scce , events ) ;
2005-04-16 15:20:36 -07:00
if ( events & UART_SCCM_BRKE )
uart_handle_break ( port ) ;
if ( events & UART_SCCM_RX )
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 14:55:46 +01:00
cpm_uart_int_rx ( port ) ;
2005-04-16 15:20:36 -07:00
if ( events & UART_SCCM_TX )
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 14:55:46 +01:00
cpm_uart_int_tx ( port ) ;
2005-04-16 15:20:36 -07:00
}
return ( events ) ? IRQ_HANDLED : IRQ_NONE ;
}
static int cpm_uart_startup ( struct uart_port * port )
{
int retval ;
2014-10-05 19:01:06 +02:00
struct uart_cpm_port * pinfo =
container_of ( port , struct uart_cpm_port , port ) ;
2005-04-16 15:20:36 -07:00
pr_debug ( " CPM uart[%d]:startup \n " , port - > line ) ;
2009-03-06 11:01:23 +08:00
/* If the port is not the console, make sure rx is disabled. */
if ( ! ( pinfo - > flags & FLAG_CONSOLE ) ) {
/* Disable UART rx */
if ( IS_SMC ( pinfo ) ) {
clrbits16 ( & pinfo - > smcp - > smc_smcmr , SMCMR_REN ) ;
clrbits8 ( & pinfo - > smcp - > smc_smcm , SMCM_RX ) ;
} else {
clrbits32 ( & pinfo - > sccp - > scc_gsmrl , SCC_GSMRL_ENR ) ;
clrbits16 ( & pinfo - > sccp - > scc_sccm , UART_SCCM_RX ) ;
}
2012-09-24 08:17:08 +02:00
cpm_uart_initbd ( pinfo ) ;
2019-05-22 12:17:11 +00:00
if ( IS_SMC ( pinfo ) ) {
out_be32 ( & pinfo - > smcup - > smc_rstate , 0 ) ;
out_be32 ( & pinfo - > smcup - > smc_tstate , 0 ) ;
out_be16 ( & pinfo - > smcup - > smc_rbptr ,
in_be16 ( & pinfo - > smcup - > smc_rbase ) ) ;
out_be16 ( & pinfo - > smcup - > smc_tbptr ,
in_be16 ( & pinfo - > smcup - > smc_tbase ) ) ;
} else {
cpm_line_cr_cmd ( pinfo , CPM_CR_INIT_TRX ) ;
}
2009-03-06 11:01:23 +08:00
}
2005-04-16 15:20:36 -07:00
/* Install interrupt handler. */
retval = request_irq ( port - > irq , cpm_uart_int , 0 , " cpm_uart " , port ) ;
if ( retval )
return retval ;
/* Startup rx-int */
if ( IS_SMC ( pinfo ) ) {
2007-07-24 15:53:07 -05:00
setbits8 ( & pinfo - > smcp - > smc_smcm , SMCM_RX ) ;
setbits16 ( & pinfo - > smcp - > smc_smcmr , ( SMCMR_REN | SMCMR_TEN ) ) ;
2005-04-16 15:20:36 -07:00
} else {
2007-07-24 15:53:07 -05:00
setbits16 ( & pinfo - > sccp - > scc_sccm , UART_SCCM_RX ) ;
setbits32 ( & pinfo - > sccp - > scc_gsmrl , ( SCC_GSMRL_ENR | SCC_GSMRL_ENT ) ) ;
2005-04-16 15:20:36 -07:00
}
return 0 ;
}
2005-08-09 10:08:00 -07:00
inline void cpm_uart_wait_until_send ( struct uart_cpm_port * pinfo )
{
2005-09-03 15:55:37 -07:00
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
schedule_timeout ( pinfo - > wait_closing ) ;
2005-08-09 10:08:00 -07:00
}
2005-04-16 15:20:36 -07:00
/*
* Shutdown the uart
*/
static void cpm_uart_shutdown ( struct uart_port * port )
{
2014-10-05 19:01:06 +02:00
struct uart_cpm_port * pinfo =
container_of ( port , struct uart_cpm_port , port ) ;
2005-04-16 15:20:36 -07:00
pr_debug ( " CPM uart[%d]:shutdown \n " , port - > line ) ;
/* free interrupt handler */
free_irq ( port - > irq , port ) ;
/* If the port is not the console, disable Rx and Tx. */
if ( ! ( pinfo - > flags & FLAG_CONSOLE ) ) {
2005-08-09 10:08:00 -07:00
/* Wait for all the BDs marked sent */
2005-09-03 15:55:37 -07:00
while ( ! cpm_uart_tx_empty ( port ) ) {
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
2005-08-09 10:08:00 -07:00
schedule_timeout ( 2 ) ;
2005-09-03 15:55:37 -07:00
}
if ( pinfo - > wait_closing )
2005-08-09 10:08:00 -07:00
cpm_uart_wait_until_send ( pinfo ) ;
2005-04-16 15:20:36 -07:00
/* Stop uarts */
if ( IS_SMC ( pinfo ) ) {
2007-07-24 15:53:07 -05:00
smc_t __iomem * smcp = pinfo - > smcp ;
clrbits16 ( & smcp - > smc_smcmr , SMCMR_REN | SMCMR_TEN ) ;
clrbits8 ( & smcp - > smc_smcm , SMCM_RX | SMCM_TX ) ;
2005-04-16 15:20:36 -07:00
} else {
2007-07-24 15:53:07 -05:00
scc_t __iomem * sccp = pinfo - > sccp ;
clrbits32 ( & sccp - > scc_gsmrl , SCC_GSMRL_ENR | SCC_GSMRL_ENT ) ;
clrbits16 ( & sccp - > scc_sccm , UART_SCCM_TX | UART_SCCM_RX ) ;
2005-04-16 15:20:36 -07:00
}
/* Shut them really down and reinit buffer descriptors */
2008-07-23 21:29:50 -07:00
if ( IS_SMC ( pinfo ) ) {
out_be16 ( & pinfo - > smcup - > smc_brkcr , 0 ) ;
2007-07-17 17:59:06 -05:00
cpm_line_cr_cmd ( pinfo , CPM_CR_STOP_TX ) ;
2008-07-23 21:29:50 -07:00
} else {
out_be16 ( & pinfo - > sccup - > scc_brkcr , 0 ) ;
2007-07-17 17:59:06 -05:00
cpm_line_cr_cmd ( pinfo , CPM_CR_GRA_STOP_TX ) ;
2008-07-23 21:29:50 -07:00
}
2006-04-29 22:32:44 +04:00
2005-04-16 15:20:36 -07:00
cpm_uart_initbd ( pinfo ) ;
}
}
static void cpm_uart_set_termios ( struct uart_port * port ,
2007-05-08 12:19:21 -05:00
struct ktermios * termios ,
2022-08-16 14:57:37 +03:00
const struct ktermios * old )
2005-04-16 15:20:36 -07:00
{
int baud ;
unsigned long flags ;
u16 cval , scval , prev_mode ;
2014-10-05 19:01:06 +02:00
struct uart_cpm_port * pinfo =
container_of ( port , struct uart_cpm_port , port ) ;
2007-07-24 15:53:07 -05:00
smc_t __iomem * smcp = pinfo - > smcp ;
scc_t __iomem * sccp = pinfo - > sccp ;
2012-09-24 08:39:44 +02:00
int maxidl ;
2005-04-16 15:20:36 -07:00
pr_debug ( " CPM uart[%d]:set_termios \n " , port - > line ) ;
baud = uart_get_baud_rate ( port , termios , old , 0 , port - > uartclk / 16 ) ;
2021-01-05 13:02:37 +01:00
if ( baud < HW_BUF_SPD_THRESHOLD | | port - > flags & UPF_LOW_LATENCY )
2010-11-11 10:53:03 +01:00
pinfo - > rx_fifosize = 1 ;
else
pinfo - > rx_fifosize = RX_BUF_SIZE ;
2005-04-16 15:20:36 -07:00
2012-09-24 08:39:44 +02:00
/* MAXIDL is the timeout after which a receive buffer is closed
* when not full if no more characters are received .
* We calculate it from the baudrate so that the duration is
* always the same at standard rates : about 4 ms .
*/
maxidl = baud / 2400 ;
if ( maxidl < 1 )
maxidl = 1 ;
if ( maxidl > 0x10 )
maxidl = 0x10 ;
2005-04-16 15:20:36 -07:00
cval = 0 ;
scval = 0 ;
if ( termios - > c_cflag & CSTOPB ) {
cval | = SMCMR_SL ; /* Two stops */
scval | = SCU_PSMR_SL ;
}
if ( termios - > c_cflag & PARENB ) {
cval | = SMCMR_PEN ;
scval | = SCU_PSMR_PEN ;
if ( ! ( termios - > c_cflag & PARODD ) ) {
cval | = SMCMR_PM_EVEN ;
scval | = ( SCU_PSMR_REVP | SCU_PSMR_TEVP ) ;
}
}
2008-07-02 10:58:45 +02:00
/*
* Update the timeout
*/
uart_update_timeout ( port , termios - > c_cflag , baud ) ;
2005-04-16 15:20:36 -07:00
/*
* Set up parity check flag
*/
port - > read_status_mask = ( BD_SC_EMPTY | BD_SC_OV ) ;
if ( termios - > c_iflag & INPCK )
port - > read_status_mask | = BD_SC_FR | BD_SC_PR ;
if ( ( termios - > c_iflag & BRKINT ) | | ( termios - > c_iflag & PARMRK ) )
port - > read_status_mask | = BD_SC_BR ;
/*
* Characters to ignore
*/
port - > ignore_status_mask = 0 ;
if ( termios - > c_iflag & IGNPAR )
port - > ignore_status_mask | = BD_SC_PR | BD_SC_FR ;
if ( termios - > c_iflag & IGNBRK ) {
port - > ignore_status_mask | = BD_SC_BR ;
/*
* If we ' re ignore parity and break indicators , ignore
* overruns too . ( For real raw support ) .
*/
if ( termios - > c_iflag & IGNPAR )
port - > ignore_status_mask | = BD_SC_OV ;
}
/*
* ! ! ! ignore all characters if CREAD is not set
*/
if ( ( termios - > c_cflag & CREAD ) = = 0 )
port - > read_status_mask & = ~ BD_SC_EMPTY ;
2005-08-09 10:08:00 -07:00
2023-09-14 20:43:40 +02:06
uart_port_lock_irqsave ( port , & flags ) ;
2005-04-16 15:20:36 -07:00
if ( IS_SMC ( pinfo ) ) {
2022-08-30 11:49:22 +03:00
unsigned int bits = tty_get_frame_size ( termios - > c_cflag ) ;
2010-11-11 10:53:03 +01:00
/*
* MRBLR can be changed while an SMC / SCC is operating only
* if it is done in a single bus cycle with one 16 - bit move
* ( not two 8 - bit bus cycles back - to - back ) . This occurs when
* the cp shifts control to the next RxBD , so the change does
* not take effect immediately . To guarantee the exact RxBD
* on which the change occurs , change MRBLR only while the
* SMC / SCC receiver is disabled .
*/
out_be16 ( & pinfo - > smcup - > smc_mrblr , pinfo - > rx_fifosize ) ;
2012-09-24 08:39:44 +02:00
out_be16 ( & pinfo - > smcup - > smc_maxidl , maxidl ) ;
2010-11-11 10:53:03 +01:00
2005-04-16 15:20:36 -07:00
/* Set the mode register. We want to keep a copy of the
* enables , because we want to put them back if they were
* present .
*/
2008-07-23 21:29:50 -07:00
prev_mode = in_be16 ( & smcp - > smc_smcmr ) & ( SMCMR_REN | SMCMR_TEN ) ;
/* Output in *one* operation, so we don't interrupt RX/TX if they
2022-08-30 11:49:22 +03:00
* were already enabled .
* Character length programmed into the register is frame bits minus 1.
*/
out_be16 ( & smcp - > smc_smcmr , smcr_mk_clen ( bits - 1 ) | cval |
SMCMR_SM_UART | prev_mode ) ;
2005-04-16 15:20:36 -07:00
} else {
2022-08-30 11:49:22 +03:00
unsigned int bits = tty_get_char_size ( termios - > c_cflag ) ;
2010-11-11 10:53:03 +01:00
out_be16 ( & pinfo - > sccup - > scc_genscc . scc_mrblr , pinfo - > rx_fifosize ) ;
2012-09-24 08:39:44 +02:00
out_be16 ( & pinfo - > sccup - > scc_maxidl , maxidl ) ;
2022-08-30 11:49:22 +03:00
out_be16 ( & sccp - > scc_psmr , ( UART_LCR_WLEN ( bits ) < < 12 ) | scval ) ;
2005-04-16 15:20:36 -07:00
}
2008-07-28 10:42:16 +02:00
if ( pinfo - > clk )
clk_set_rate ( pinfo - > clk , baud ) ;
else
2023-08-03 15:56:46 +02:00
cpm_setbrg ( pinfo - > brg - 1 , baud ) ;
2023-09-14 20:43:40 +02:06
uart_port_unlock_irqrestore ( port , flags ) ;
2005-04-16 15:20:36 -07:00
}
static const char * cpm_uart_type ( struct uart_port * port )
{
pr_debug ( " CPM uart[%d]:uart_type \n " , port - > line ) ;
return port - > type = = PORT_CPM ? " CPM UART " : NULL ;
}
/*
* verify the new serial_struct ( for TIOCSSERIAL ) .
*/
static int cpm_uart_verify_port ( struct uart_port * port ,
struct serial_struct * ser )
{
int ret = 0 ;
pr_debug ( " CPM uart[%d]:verify_port \n " , port - > line ) ;
if ( ser - > type ! = PORT_UNKNOWN & & ser - > type ! = PORT_CPM )
ret = - EINVAL ;
2008-08-19 20:49:55 -07:00
if ( ser - > irq < 0 | | ser - > irq > = nr_irqs )
2005-04-16 15:20:36 -07:00
ret = - EINVAL ;
if ( ser - > baud_base < 9600 )
ret = - EINVAL ;
return ret ;
}
/*
* Transmit characters , refill buffer descriptor , if possible
*/
static int cpm_uart_tx_pump ( struct uart_port * port )
{
2007-07-24 15:53:07 -05:00
cbd_t __iomem * bdp ;
u8 * p ;
2005-04-16 15:20:36 -07:00
int count ;
2014-10-05 19:01:06 +02:00
struct uart_cpm_port * pinfo =
container_of ( port , struct uart_cpm_port , port ) ;
2009-09-24 16:05:52 +10:00
struct circ_buf * xmit = & port - > state - > xmit ;
2005-04-16 15:20:36 -07:00
/* Handle xon/xoff */
if ( port - > x_char ) {
/* Pick next descriptor and fill from buffer */
bdp = pinfo - > tx_cur ;
2007-07-24 15:53:07 -05:00
p = cpm2cpu_addr ( in_be32 ( & bdp - > cbd_bufaddr ) , pinfo ) ;
2005-08-09 10:08:00 -07:00
2005-12-29 19:18:49 -02:00
* p + + = port - > x_char ;
2007-07-24 15:53:07 -05:00
out_be16 ( & bdp - > cbd_datlen , 1 ) ;
setbits16 ( & bdp - > cbd_sc , BD_SC_READY ) ;
2005-04-16 15:20:36 -07:00
/* Get next BD. */
2007-07-24 15:53:07 -05:00
if ( in_be16 ( & bdp - > cbd_sc ) & BD_SC_WRAP )
2005-04-16 15:20:36 -07:00
bdp = pinfo - > tx_bd_base ;
else
bdp + + ;
pinfo - > tx_cur = bdp ;
port - > icount . tx + + ;
port - > x_char = 0 ;
return 1 ;
}
if ( uart_circ_empty ( xmit ) | | uart_tx_stopped ( port ) ) {
2005-08-31 10:12:14 +01:00
cpm_uart_stop_tx ( port ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
/* Pick next descriptor and fill from buffer */
bdp = pinfo - > tx_cur ;
2023-03-20 15:50:09 +02:00
while ( ! ( in_be16 ( & bdp - > cbd_sc ) & BD_SC_READY ) & & ! uart_circ_empty ( xmit ) ) {
2005-04-16 15:20:36 -07:00
count = 0 ;
2007-07-24 15:53:07 -05:00
p = cpm2cpu_addr ( in_be32 ( & bdp - > cbd_bufaddr ) , pinfo ) ;
2005-04-16 15:20:36 -07:00
while ( count < pinfo - > tx_fifosize ) {
* p + + = xmit - > buf [ xmit - > tail ] ;
2022-10-19 12:11:20 +03:00
uart_xmit_advance ( port , 1 ) ;
2005-04-16 15:20:36 -07:00
count + + ;
2023-03-20 15:50:09 +02:00
if ( uart_circ_empty ( xmit ) )
2005-04-16 15:20:36 -07:00
break ;
}
2007-07-24 15:53:07 -05:00
out_be16 ( & bdp - > cbd_datlen , count ) ;
setbits16 ( & bdp - > cbd_sc , BD_SC_READY ) ;
2005-04-16 15:20:36 -07:00
/* Get next BD. */
2007-07-24 15:53:07 -05:00
if ( in_be16 ( & bdp - > cbd_sc ) & BD_SC_WRAP )
2005-04-16 15:20:36 -07:00
bdp = pinfo - > tx_bd_base ;
else
bdp + + ;
}
pinfo - > tx_cur = bdp ;
if ( uart_circ_chars_pending ( xmit ) < WAKEUP_CHARS )
uart_write_wakeup ( port ) ;
if ( uart_circ_empty ( xmit ) ) {
2005-08-31 10:12:14 +01:00
cpm_uart_stop_tx ( port ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
return 1 ;
}
/*
* init buffer descriptors
*/
static void cpm_uart_initbd ( struct uart_cpm_port * pinfo )
{
int i ;
u8 * mem_addr ;
2007-07-24 15:53:07 -05:00
cbd_t __iomem * bdp ;
2005-04-16 15:20:36 -07:00
pr_debug ( " CPM uart[%d]:initbd \n " , pinfo - > port . line ) ;
/* Set the physical address of the host memory
* buffers in the buffer descriptors , and the
* virtual address for us to work with .
*/
mem_addr = pinfo - > mem_addr ;
bdp = pinfo - > rx_cur = pinfo - > rx_bd_base ;
for ( i = 0 ; i < ( pinfo - > rx_nrfifos - 1 ) ; i + + , bdp + + ) {
2007-07-24 15:53:07 -05:00
out_be32 ( & bdp - > cbd_bufaddr , cpu2cpm_addr ( mem_addr , pinfo ) ) ;
out_be16 ( & bdp - > cbd_sc , BD_SC_EMPTY | BD_SC_INTRPT ) ;
2005-04-16 15:20:36 -07:00
mem_addr + = pinfo - > rx_fifosize ;
}
2005-08-09 10:08:00 -07:00
2007-07-24 15:53:07 -05:00
out_be32 ( & bdp - > cbd_bufaddr , cpu2cpm_addr ( mem_addr , pinfo ) ) ;
out_be16 ( & bdp - > cbd_sc , BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT ) ;
2005-04-16 15:20:36 -07:00
/* Set the physical address of the host memory
* buffers in the buffer descriptors , and the
* virtual address for us to work with .
*/
mem_addr = pinfo - > mem_addr + L1_CACHE_ALIGN ( pinfo - > rx_nrfifos * pinfo - > rx_fifosize ) ;
bdp = pinfo - > tx_cur = pinfo - > tx_bd_base ;
for ( i = 0 ; i < ( pinfo - > tx_nrfifos - 1 ) ; i + + , bdp + + ) {
2007-07-24 15:53:07 -05:00
out_be32 ( & bdp - > cbd_bufaddr , cpu2cpm_addr ( mem_addr , pinfo ) ) ;
out_be16 ( & bdp - > cbd_sc , BD_SC_INTRPT ) ;
2005-04-16 15:20:36 -07:00
mem_addr + = pinfo - > tx_fifosize ;
}
2005-08-09 10:08:00 -07:00
2007-07-24 15:53:07 -05:00
out_be32 ( & bdp - > cbd_bufaddr , cpu2cpm_addr ( mem_addr , pinfo ) ) ;
out_be16 ( & bdp - > cbd_sc , BD_SC_WRAP | BD_SC_INTRPT ) ;
2005-04-16 15:20:36 -07:00
}
static void cpm_uart_init_scc ( struct uart_cpm_port * pinfo )
{
2007-07-24 15:53:07 -05:00
scc_t __iomem * scp ;
scc_uart_t __iomem * sup ;
2005-04-16 15:20:36 -07:00
pr_debug ( " CPM uart[%d]:init_scc \n " , pinfo - > port . line ) ;
scp = pinfo - > sccp ;
sup = pinfo - > sccup ;
/* Store address */
2007-07-24 15:53:07 -05:00
out_be16 ( & pinfo - > sccup - > scc_genscc . scc_rbase ,
( u8 __iomem * ) pinfo - > rx_bd_base - DPRAM_BASE ) ;
out_be16 ( & pinfo - > sccup - > scc_genscc . scc_tbase ,
( u8 __iomem * ) pinfo - > tx_bd_base - DPRAM_BASE ) ;
2005-04-16 15:20:36 -07:00
/* Set up the uart parameters in the
* parameter ram .
*/
2023-08-03 15:56:46 +02:00
out_8 ( & sup - > scc_genscc . scc_rfcr , CPMFCR_GBL | CPMFCR_EB ) ;
out_8 ( & sup - > scc_genscc . scc_tfcr , CPMFCR_GBL | CPMFCR_EB ) ;
2005-04-16 15:20:36 -07:00
2007-07-24 15:53:07 -05:00
out_be16 ( & sup - > scc_genscc . scc_mrblr , pinfo - > rx_fifosize ) ;
2012-09-24 08:20:18 +02:00
out_be16 ( & sup - > scc_maxidl , 0x10 ) ;
2007-07-24 15:53:07 -05:00
out_be16 ( & sup - > scc_brkcr , 1 ) ;
out_be16 ( & sup - > scc_parec , 0 ) ;
out_be16 ( & sup - > scc_frmec , 0 ) ;
out_be16 ( & sup - > scc_nosec , 0 ) ;
out_be16 ( & sup - > scc_brkec , 0 ) ;
out_be16 ( & sup - > scc_uaddr1 , 0 ) ;
out_be16 ( & sup - > scc_uaddr2 , 0 ) ;
out_be16 ( & sup - > scc_toseq , 0 ) ;
out_be16 ( & sup - > scc_char1 , 0x8000 ) ;
out_be16 ( & sup - > scc_char2 , 0x8000 ) ;
out_be16 ( & sup - > scc_char3 , 0x8000 ) ;
out_be16 ( & sup - > scc_char4 , 0x8000 ) ;
out_be16 ( & sup - > scc_char5 , 0x8000 ) ;
out_be16 ( & sup - > scc_char6 , 0x8000 ) ;
out_be16 ( & sup - > scc_char7 , 0x8000 ) ;
out_be16 ( & sup - > scc_char8 , 0x8000 ) ;
out_be16 ( & sup - > scc_rccm , 0xc0ff ) ;
2005-04-16 15:20:36 -07:00
/* Send the CPM an initialize command.
*/
2007-07-17 17:59:06 -05:00
cpm_line_cr_cmd ( pinfo , CPM_CR_INIT_TRX ) ;
2005-04-16 15:20:36 -07:00
/* Set UART mode, 8 bit, no parity, one stop.
* Enable receive and transmit .
*/
2007-07-24 15:53:07 -05:00
out_be32 ( & scp - > scc_gsmrh , 0 ) ;
out_be32 ( & scp - > scc_gsmrl ,
SCC_GSMRL_MODE_UART | SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16 ) ;
2005-04-16 15:20:36 -07:00
/* Enable rx interrupts and clear all pending events. */
2007-07-24 15:53:07 -05:00
out_be16 ( & scp - > scc_sccm , 0 ) ;
out_be16 ( & scp - > scc_scce , 0xffff ) ;
out_be16 ( & scp - > scc_dsr , 0x7e7e ) ;
out_be16 ( & scp - > scc_psmr , 0x3000 ) ;
2005-04-16 15:20:36 -07:00
2007-07-24 15:53:07 -05:00
setbits32 ( & scp - > scc_gsmrl , SCC_GSMRL_ENR | SCC_GSMRL_ENT ) ;
2005-04-16 15:20:36 -07:00
}
static void cpm_uart_init_smc ( struct uart_cpm_port * pinfo )
{
2007-07-24 15:53:07 -05:00
smc_t __iomem * sp ;
smc_uart_t __iomem * up ;
2005-04-16 15:20:36 -07:00
pr_debug ( " CPM uart[%d]:init_smc \n " , pinfo - > port . line ) ;
sp = pinfo - > smcp ;
up = pinfo - > smcup ;
/* Store address */
2007-07-24 15:53:07 -05:00
out_be16 ( & pinfo - > smcup - > smc_rbase ,
( u8 __iomem * ) pinfo - > rx_bd_base - DPRAM_BASE ) ;
out_be16 ( & pinfo - > smcup - > smc_tbase ,
( u8 __iomem * ) pinfo - > tx_bd_base - DPRAM_BASE ) ;
2005-04-16 15:20:36 -07:00
/*
2019-05-22 12:17:11 +00:00
* In case SMC is being relocated . . .
2005-04-16 15:20:36 -07:00
*/
2007-07-24 15:53:07 -05:00
out_be16 ( & up - > smc_rbptr , in_be16 ( & pinfo - > smcup - > smc_rbase ) ) ;
out_be16 ( & up - > smc_tbptr , in_be16 ( & pinfo - > smcup - > smc_tbase ) ) ;
out_be32 ( & up - > smc_rstate , 0 ) ;
out_be32 ( & up - > smc_tstate , 0 ) ;
out_be16 ( & up - > smc_brkcr , 1 ) ; /* number of break chars */
out_be16 ( & up - > smc_brkec , 0 ) ;
2005-04-16 15:20:36 -07:00
/* Set up the uart parameters in the
* parameter ram .
*/
2023-08-03 15:56:46 +02:00
out_8 ( & up - > smc_rfcr , CPMFCR_GBL | CPMFCR_EB ) ;
out_8 ( & up - > smc_tfcr , CPMFCR_GBL | CPMFCR_EB ) ;
2005-04-16 15:20:36 -07:00
2010-07-19 08:22:43 +02:00
/* Using idle character time requires some additional tuning. */
2007-07-24 15:53:07 -05:00
out_be16 ( & up - > smc_mrblr , pinfo - > rx_fifosize ) ;
2012-09-24 08:20:18 +02:00
out_be16 ( & up - > smc_maxidl , 0x10 ) ;
2007-07-24 15:53:07 -05:00
out_be16 ( & up - > smc_brklen , 0 ) ;
out_be16 ( & up - > smc_brkec , 0 ) ;
out_be16 ( & up - > smc_brkcr , 1 ) ;
2005-04-16 15:20:36 -07:00
/* Set UART mode, 8 bit, no parity, one stop.
* Enable receive and transmit .
*/
2007-07-24 15:53:07 -05:00
out_be16 ( & sp - > smc_smcmr , smcr_mk_clen ( 9 ) | SMCMR_SM_UART ) ;
2005-04-16 15:20:36 -07:00
/* Enable only rx interrupts clear all pending events. */
2007-07-24 15:53:07 -05:00
out_8 ( & sp - > smc_smcm , 0 ) ;
out_8 ( & sp - > smc_smce , 0xff ) ;
2005-04-16 15:20:36 -07:00
2007-07-24 15:53:07 -05:00
setbits16 ( & sp - > smc_smcmr , SMCMR_REN | SMCMR_TEN ) ;
2005-04-16 15:20:36 -07:00
}
serial: cpm_uart: Refactor cpm_uart_allocbuf()/cpm_uart_freebuf()
cpm_uart_freebuf() is identical for CPM1 and CPM2.
cpm_uart_allocbuf() only has a small difference between CPM1 and CPM2
as shown below:
CPM1:
if (is_con) {
/* was hostalloc but changed cause it blows away the */
/* large tlb mapping when pinning the kernel area */
mem_addr = (u8 *) cpm_dpram_addr(cpm_dpalloc(memsz, 8));
dma_addr = (u32)cpm_dpram_phys(mem_addr);
} else
mem_addr = dma_alloc_coherent(pinfo->port.dev, memsz, &dma_addr,
GFP_KERNEL);
CPM2:
if (is_con) {
mem_addr = kzalloc(memsz, GFP_NOWAIT);
dma_addr = virt_to_bus(mem_addr);
}
else
mem_addr = dma_alloc_coherent(pinfo->port.dev, memsz, &dma_addr,
GFP_KERNEL);
Refactor this by using IS_ENABLED(CONFIG_CPM1)
and move both functions in cpm_uart_core.c as they are used only there.
While doing this, add the necessary casts to silence sparse for the CPM1
part. This is because a dma alloc is not expected to be an iomem but
for CPM1 as we use DPRAM this is seen as iomem.
Also replace calls to cpm_dpxxxx() by relevant cpm_muram_xxxx() calls.
This is needed at least for cpm_dpram_phys() which is only defined
for CPM1. Just do the same for all so that cpm_dpxxxx() macros can get
droped in the future.
To silence checkpatch, replace printk(KERN_ERR by pr_err( and display
function name instead of hard coded filename. Also replace
mem_addr == NULL by !mem_addr.
Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
Link: https://lore.kernel.org/r/606dfdd258a4f2f2882e2e189bef37526bb3b499.1691068700.git.christophe.leroy@csgroup.eu
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-03 15:56:48 +02:00
/*
* Allocate DP - Ram and memory buffers . We need to allocate a transmit and
* receive buffer descriptors from dual port ram , and a character
* buffer area from host mem . If we are allocating for the console we need
* to do it from bootmem
*/
static int cpm_uart_allocbuf ( struct uart_cpm_port * pinfo , unsigned int is_con )
{
int dpmemsz , memsz ;
u8 __iomem * dp_mem ;
unsigned long dp_offset ;
u8 * mem_addr ;
dma_addr_t dma_addr = 0 ;
pr_debug ( " CPM uart[%d]:allocbuf \n " , pinfo - > port . line ) ;
dpmemsz = sizeof ( cbd_t ) * ( pinfo - > rx_nrfifos + pinfo - > tx_nrfifos ) ;
dp_offset = cpm_muram_alloc ( dpmemsz , 8 ) ;
if ( IS_ERR_VALUE ( dp_offset ) ) {
pr_err ( " %s: could not allocate buffer descriptors \n " , __func__ ) ;
return - ENOMEM ;
}
dp_mem = cpm_muram_addr ( dp_offset ) ;
memsz = L1_CACHE_ALIGN ( pinfo - > rx_nrfifos * pinfo - > rx_fifosize ) +
L1_CACHE_ALIGN ( pinfo - > tx_nrfifos * pinfo - > tx_fifosize ) ;
if ( IS_ENABLED ( CONFIG_CPM1 ) & & is_con ) {
/* was hostalloc but changed cause it blows away the */
/* large tlb mapping when pinning the kernel area */
mem_addr = ( u8 __force * ) cpm_muram_addr ( cpm_muram_alloc ( memsz , 8 ) ) ;
dma_addr = cpm_muram_dma ( ( void __iomem * ) mem_addr ) ;
} else if ( is_con ) {
mem_addr = kzalloc ( memsz , GFP_NOWAIT ) ;
dma_addr = virt_to_bus ( mem_addr ) ;
} else {
mem_addr = dma_alloc_coherent ( pinfo - > port . dev , memsz , & dma_addr ,
GFP_KERNEL ) ;
}
if ( ! mem_addr ) {
cpm_muram_free ( dp_offset ) ;
pr_err ( " %s: could not allocate coherent memory \n " , __func__ ) ;
return - ENOMEM ;
}
pinfo - > dp_addr = dp_offset ;
pinfo - > mem_addr = mem_addr ;
pinfo - > dma_addr = dma_addr ;
pinfo - > mem_size = memsz ;
pinfo - > rx_buf = mem_addr ;
pinfo - > tx_buf = pinfo - > rx_buf + L1_CACHE_ALIGN ( pinfo - > rx_nrfifos
* pinfo - > rx_fifosize ) ;
pinfo - > rx_bd_base = ( cbd_t __iomem * ) dp_mem ;
pinfo - > tx_bd_base = pinfo - > rx_bd_base + pinfo - > rx_nrfifos ;
return 0 ;
}
static void cpm_uart_freebuf ( struct uart_cpm_port * pinfo )
{
dma_free_coherent ( pinfo - > port . dev , L1_CACHE_ALIGN ( pinfo - > rx_nrfifos *
pinfo - > rx_fifosize ) +
L1_CACHE_ALIGN ( pinfo - > tx_nrfifos *
pinfo - > tx_fifosize ) , ( void __force * ) pinfo - > mem_addr ,
pinfo - > dma_addr ) ;
cpm_muram_free ( pinfo - > dp_addr ) ;
}
2005-04-16 15:20:36 -07:00
/*
* Initialize port . This is called from early_console stuff
* so we have to be careful here !
*/
static int cpm_uart_request_port ( struct uart_port * port )
{
2014-10-05 19:01:06 +02:00
struct uart_cpm_port * pinfo =
container_of ( port , struct uart_cpm_port , port ) ;
2005-04-16 15:20:36 -07:00
int ret ;
pr_debug ( " CPM uart[%d]:request port \n " , port - > line ) ;
if ( pinfo - > flags & FLAG_CONSOLE )
return 0 ;
if ( IS_SMC ( pinfo ) ) {
2007-07-24 15:53:07 -05:00
clrbits8 ( & pinfo - > smcp - > smc_smcm , SMCM_RX | SMCM_TX ) ;
clrbits16 ( & pinfo - > smcp - > smc_smcmr , SMCMR_REN | SMCMR_TEN ) ;
2005-04-16 15:20:36 -07:00
} else {
2007-07-24 15:53:07 -05:00
clrbits16 ( & pinfo - > sccp - > scc_sccm , UART_SCCM_TX | UART_SCCM_RX ) ;
clrbits32 ( & pinfo - > sccp - > scc_gsmrl , SCC_GSMRL_ENR | SCC_GSMRL_ENT ) ;
2005-04-16 15:20:36 -07:00
}
ret = cpm_uart_allocbuf ( pinfo , 0 ) ;
if ( ret )
return ret ;
cpm_uart_initbd ( pinfo ) ;
2005-08-09 10:08:00 -07:00
if ( IS_SMC ( pinfo ) )
cpm_uart_init_smc ( pinfo ) ;
else
cpm_uart_init_scc ( pinfo ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
static void cpm_uart_release_port ( struct uart_port * port )
{
2014-10-05 19:01:06 +02:00
struct uart_cpm_port * pinfo =
container_of ( port , struct uart_cpm_port , port ) ;
2005-04-16 15:20:36 -07:00
if ( ! ( pinfo - > flags & FLAG_CONSOLE ) )
cpm_uart_freebuf ( pinfo ) ;
}
/*
* Configure / autoconfigure the port .
*/
static void cpm_uart_config_port ( struct uart_port * port , int flags )
{
pr_debug ( " CPM uart[%d]:config_port \n " , port - > line ) ;
if ( flags & UART_CONFIG_TYPE ) {
port - > type = PORT_CPM ;
cpm_uart_request_port ( port ) ;
}
}
2008-07-23 11:30:16 -05:00
2010-06-17 11:13:40 +08:00
# if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_CPM_CONSOLE)
/*
* Write a string to the serial port
* Note that this is called with interrupts already disabled
*/
static void cpm_uart_early_write ( struct uart_cpm_port * pinfo ,
2014-05-29 09:48:43 +01:00
const char * string , u_int count , bool handle_linefeed )
2010-06-17 11:13:40 +08:00
{
unsigned int i ;
cbd_t __iomem * bdp , * bdbase ;
unsigned char * cpm_outp_addr ;
/* Get the address of the host memory buffer.
*/
bdp = pinfo - > tx_cur ;
bdbase = pinfo - > tx_bd_base ;
/*
* Now , do each character . This is not as bad as it looks
* since this is a holding FIFO and not a transmitting FIFO .
* We could add the complexity of filling the entire transmit
* buffer , but we would just wait longer between accesses . . . . . .
*/
for ( i = 0 ; i < count ; i + + , string + + ) {
/* Wait for transmitter fifo to empty.
* Ready indicates output is ready , and xmt is doing
* that , not that it is ready for us to send .
*/
while ( ( in_be16 ( & bdp - > cbd_sc ) & BD_SC_READY ) ! = 0 )
;
/* Send the character out.
* If the buffer address is in the CPM DPRAM , don ' t
* convert it .
*/
cpm_outp_addr = cpm2cpu_addr ( in_be32 ( & bdp - > cbd_bufaddr ) ,
pinfo ) ;
* cpm_outp_addr = * string ;
out_be16 ( & bdp - > cbd_datlen , 1 ) ;
setbits16 ( & bdp - > cbd_sc , BD_SC_READY ) ;
if ( in_be16 ( & bdp - > cbd_sc ) & BD_SC_WRAP )
bdp = bdbase ;
else
bdp + + ;
/* if a LF, also do CR... */
2014-05-29 09:48:43 +01:00
if ( handle_linefeed & & * string = = 10 ) {
2010-06-17 11:13:40 +08:00
while ( ( in_be16 ( & bdp - > cbd_sc ) & BD_SC_READY ) ! = 0 )
;
cpm_outp_addr = cpm2cpu_addr ( in_be32 ( & bdp - > cbd_bufaddr ) ,
pinfo ) ;
* cpm_outp_addr = 13 ;
out_be16 ( & bdp - > cbd_datlen , 1 ) ;
setbits16 ( & bdp - > cbd_sc , BD_SC_READY ) ;
if ( in_be16 ( & bdp - > cbd_sc ) & BD_SC_WRAP )
bdp = bdbase ;
else
bdp + + ;
}
}
/*
* Finally , Wait for transmitter & holding register to empty
* and restore the IER
*/
while ( ( in_be16 ( & bdp - > cbd_sc ) & BD_SC_READY ) ! = 0 )
;
pinfo - > tx_cur = bdp ;
}
# endif
2008-07-23 11:30:16 -05:00
# ifdef CONFIG_CONSOLE_POLL
/* Serial polling routines for writing and reading from the uart while
* in an interrupt or debug context .
*/
# define GDB_BUF_SIZE 512 /* power of 2, please */
static char poll_buf [ GDB_BUF_SIZE ] ;
static char * pollp ;
static int poll_chars ;
static int poll_wait_key ( char * obuf , struct uart_cpm_port * pinfo )
{
u_char c , * cp ;
volatile cbd_t * bdp ;
int i ;
/* Get the address of the host memory buffer.
*/
bdp = pinfo - > rx_cur ;
2018-09-14 10:32:50 +00:00
if ( bdp - > cbd_sc & BD_SC_EMPTY )
return NO_POLL_CHAR ;
2008-07-23 11:30:16 -05:00
/* If the buffer address is in the CPM DPRAM, don't
* convert it .
*/
cp = cpm2cpu_addr ( bdp - > cbd_bufaddr , pinfo ) ;
if ( obuf ) {
i = c = bdp - > cbd_datlen ;
while ( i - - > 0 )
* obuf + + = * cp + + ;
} else
c = * cp ;
bdp - > cbd_sc & = ~ ( BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV | BD_SC_ID ) ;
bdp - > cbd_sc | = BD_SC_EMPTY ;
if ( bdp - > cbd_sc & BD_SC_WRAP )
bdp = pinfo - > rx_bd_base ;
else
bdp + + ;
pinfo - > rx_cur = ( cbd_t * ) bdp ;
return ( int ) c ;
}
static int cpm_get_poll_char ( struct uart_port * port )
{
2014-10-05 19:01:06 +02:00
struct uart_cpm_port * pinfo =
container_of ( port , struct uart_cpm_port , port ) ;
2008-07-23 11:30:16 -05:00
if ( ! serial_polled ) {
serial_polled = 1 ;
poll_chars = 0 ;
}
if ( poll_chars < = 0 ) {
2018-09-14 10:32:50 +00:00
int ret = poll_wait_key ( poll_buf , pinfo ) ;
if ( ret = = NO_POLL_CHAR )
return ret ;
poll_chars = ret ;
2008-07-23 11:30:16 -05:00
pollp = poll_buf ;
}
poll_chars - - ;
return * pollp + + ;
}
static void cpm_put_poll_char ( struct uart_port * port ,
unsigned char c )
{
2014-10-05 19:01:06 +02:00
struct uart_cpm_port * pinfo =
container_of ( port , struct uart_cpm_port , port ) ;
2008-07-23 11:30:16 -05:00
static char ch [ 2 ] ;
ch [ 0 ] = ( char ) c ;
2014-05-29 09:48:43 +01:00
cpm_uart_early_write ( pinfo , ch , 1 , false ) ;
2008-07-23 11:30:16 -05:00
}
2020-12-23 09:38:46 +00:00
2021-10-27 09:53:26 +02:00
# ifdef CONFIG_SERIAL_CPM_CONSOLE
2020-12-23 09:38:46 +00:00
static struct uart_port * udbg_port ;
static void udbg_cpm_putc ( char c )
{
if ( c = = ' \n ' )
cpm_put_poll_char ( udbg_port , ' \r ' ) ;
cpm_put_poll_char ( udbg_port , c ) ;
}
static int udbg_cpm_getc_poll ( void )
{
int c = cpm_get_poll_char ( udbg_port ) ;
return c = = NO_POLL_CHAR ? - 1 : c ;
}
static int udbg_cpm_getc ( void )
{
int c ;
while ( ( c = udbg_cpm_getc_poll ( ) ) = = - 1 )
cpu_relax ( ) ;
return c ;
}
2021-10-27 09:53:26 +02:00
# endif /* CONFIG_SERIAL_CPM_CONSOLE */
2020-12-23 09:38:46 +00:00
2008-07-23 11:30:16 -05:00
# endif /* CONFIG_CONSOLE_POLL */
2017-08-13 08:21:47 +02:00
static const struct uart_ops cpm_uart_pops = {
2005-04-16 15:20:36 -07:00
. tx_empty = cpm_uart_tx_empty ,
. set_mctrl = cpm_uart_set_mctrl ,
. get_mctrl = cpm_uart_get_mctrl ,
. stop_tx = cpm_uart_stop_tx ,
. start_tx = cpm_uart_start_tx ,
. stop_rx = cpm_uart_stop_rx ,
. break_ctl = cpm_uart_break_ctl ,
. startup = cpm_uart_startup ,
. shutdown = cpm_uart_shutdown ,
. set_termios = cpm_uart_set_termios ,
. type = cpm_uart_type ,
. release_port = cpm_uart_release_port ,
. request_port = cpm_uart_request_port ,
. config_port = cpm_uart_config_port ,
. verify_port = cpm_uart_verify_port ,
2008-07-23 11:30:16 -05:00
# ifdef CONFIG_CONSOLE_POLL
. poll_get_char = cpm_get_poll_char ,
. poll_put_char = cpm_put_poll_char ,
# endif
2005-04-16 15:20:36 -07:00
} ;
2023-08-03 15:56:43 +02:00
static struct uart_cpm_port cpm_uart_ports [ UART_NR ] ;
2007-07-17 17:59:06 -05:00
2023-08-03 15:56:49 +02:00
static void __iomem * cpm_uart_map_pram ( struct uart_cpm_port * port ,
struct device_node * np )
{
void __iomem * pram ;
unsigned long offset ;
struct resource res ;
resource_size_t len ;
/* Don't remap parameter RAM if it has already been initialized
* during console setup .
*/
if ( IS_SMC ( port ) & & port - > smcup )
return port - > smcup ;
else if ( ! IS_SMC ( port ) & & port - > sccup )
return port - > sccup ;
if ( of_address_to_resource ( np , 1 , & res ) )
return NULL ;
len = resource_size ( & res ) ;
pram = ioremap ( res . start , len ) ;
if ( ! pram )
return NULL ;
if ( ! IS_ENABLED ( CONFIG_CPM2 ) | | ! IS_SMC ( port ) )
return pram ;
if ( len ! = 2 ) {
pr_warn ( " cpm_uart[%d]: device tree references "
" SMC pram, using boot loader/wrapper pram mapping. "
" Please fix your device tree to reference the pram "
" base register instead. \n " ,
port - > port . line ) ;
return pram ;
}
offset = cpm_muram_alloc ( 64 , 64 ) ;
out_be16 ( pram , offset ) ;
iounmap ( pram ) ;
return cpm_muram_addr ( offset ) ;
}
static void cpm_uart_unmap_pram ( struct uart_cpm_port * port , void __iomem * pram )
{
if ( ! IS_ENABLED ( CONFIG_CPM2 ) | | ! IS_SMC ( port ) )
iounmap ( pram ) ;
}
2007-07-24 15:53:07 -05:00
static int cpm_uart_init_port ( struct device_node * np ,
struct uart_cpm_port * pinfo )
2007-07-17 17:59:06 -05:00
{
const u32 * data ;
2007-07-24 15:53:07 -05:00
void __iomem * mem , * pram ;
2020-03-01 00:18:42 +01:00
struct device * dev = pinfo - > port . dev ;
2007-07-17 17:59:06 -05:00
int len ;
int ret ;
2008-07-24 18:36:37 +02:00
int i ;
2007-07-17 17:59:06 -05:00
2008-07-28 10:42:16 +02:00
data = of_get_property ( np , " clock " , NULL ) ;
if ( data ) {
struct clk * clk = clk_get ( NULL , ( const char * ) data ) ;
if ( ! IS_ERR ( clk ) )
pinfo - > clk = clk ;
}
if ( ! pinfo - > clk ) {
data = of_get_property ( np , " fsl,cpm-brg " , & len ) ;
if ( ! data | | len ! = 4 ) {
2018-08-27 20:52:47 -05:00
printk ( KERN_ERR " CPM UART %pOFn has no/invalid "
" fsl,cpm-brg property. \n " , np ) ;
2008-07-28 10:42:16 +02:00
return - EINVAL ;
}
pinfo - > brg = * data ;
2007-07-17 17:59:06 -05:00
}
data = of_get_property ( np , " fsl,cpm-command " , & len ) ;
if ( ! data | | len ! = 4 ) {
2018-08-27 20:52:47 -05:00
printk ( KERN_ERR " CPM UART %pOFn has no/invalid "
" fsl,cpm-command property. \n " , np ) ;
2007-07-17 17:59:06 -05:00
return - EINVAL ;
}
pinfo - > command = * data ;
mem = of_iomap ( np , 0 ) ;
if ( ! mem )
return - ENOMEM ;
if ( of_device_is_compatible ( np , " fsl,cpm1-scc-uart " ) | |
of_device_is_compatible ( np , " fsl,cpm2-scc-uart " ) ) {
pinfo - > sccp = mem ;
2008-04-10 17:01:27 +02:00
pinfo - > sccup = pram = cpm_uart_map_pram ( pinfo , np ) ;
2007-07-17 17:59:06 -05:00
} else if ( of_device_is_compatible ( np , " fsl,cpm1-smc-uart " ) | |
of_device_is_compatible ( np , " fsl,cpm2-smc-uart " ) ) {
pinfo - > flags | = FLAG_SMC ;
pinfo - > smcp = mem ;
2008-04-10 17:01:27 +02:00
pinfo - > smcup = pram = cpm_uart_map_pram ( pinfo , np ) ;
2007-07-17 17:59:06 -05:00
} else {
ret = - ENODEV ;
2008-04-10 17:01:27 +02:00
goto out_mem ;
}
if ( ! pram ) {
ret = - ENOMEM ;
goto out_mem ;
2007-07-17 17:59:06 -05:00
}
pinfo - > tx_nrfifos = TX_NUM_FIFO ;
pinfo - > tx_fifosize = TX_BUF_SIZE ;
pinfo - > rx_nrfifos = RX_NUM_FIFO ;
pinfo - > rx_fifosize = RX_BUF_SIZE ;
pinfo - > port . uartclk = ppc_proc_freq ;
pinfo - > port . mapbase = ( unsigned long ) mem ;
pinfo - > port . type = PORT_CPM ;
2019-12-13 00:06:13 +00:00
pinfo - > port . ops = & cpm_uart_pops ;
pinfo - > port . has_sysrq = IS_ENABLED ( CONFIG_SERIAL_CPM_CONSOLE ) ;
2007-07-17 17:59:06 -05:00
pinfo - > port . iotype = UPIO_MEM ;
2008-07-02 10:58:45 +02:00
pinfo - > port . fifosize = pinfo - > tx_nrfifos * pinfo - > tx_fifosize ;
2007-07-17 17:59:06 -05:00
spin_lock_init ( & pinfo - > port . lock ) ;
2013-08-21 17:59:24 +02:00
for ( i = 0 ; i < NUM_GPIOS ; i + + ) {
2020-03-01 00:18:42 +01:00
struct gpio_desc * gpiod ;
2013-08-21 17:59:24 +02:00
2020-03-01 00:18:42 +01:00
pinfo - > gpios [ i ] = NULL ;
2013-08-21 17:59:24 +02:00
2020-06-12 18:26:07 +00:00
gpiod = devm_gpiod_get_index_optional ( dev , NULL , i , GPIOD_ASIS ) ;
if ( IS_ERR ( gpiod ) ) {
ret = PTR_ERR ( gpiod ) ;
2022-09-30 10:33:56 +02:00
goto out_pram ;
2020-06-12 18:26:07 +00:00
}
2013-08-21 17:59:24 +02:00
2020-03-01 00:18:42 +01:00
if ( gpiod ) {
2013-08-21 17:59:24 +02:00
if ( i = = GPIO_RTS | | i = = GPIO_DTR )
2020-03-01 00:18:42 +01:00
ret = gpiod_direction_output ( gpiod , 0 ) ;
2013-08-21 17:59:24 +02:00
else
2020-03-01 00:18:42 +01:00
ret = gpiod_direction_input ( gpiod ) ;
2013-08-21 17:59:24 +02:00
if ( ret ) {
pr_err ( " can't set direction for gpio #%d: %d \n " ,
i , ret ) ;
continue ;
}
2020-03-01 00:18:42 +01:00
pinfo - > gpios [ i ] = gpiod ;
2013-08-21 17:59:24 +02:00
}
}
2008-07-24 18:36:37 +02:00
2009-04-03 16:15:49 -05:00
# ifdef CONFIG_PPC_EARLY_DEBUG_CPM
2022-05-18 21:54:52 +08:00
# if defined(CONFIG_CONSOLE_POLL) && defined(CONFIG_SERIAL_CPM_CONSOLE)
2020-12-23 09:38:46 +00:00
if ( ! udbg_port )
# endif
udbg_putc = NULL ;
2009-04-03 16:15:49 -05:00
# endif
2007-07-17 17:59:06 -05:00
return cpm_uart_request_port ( & pinfo - > port ) ;
out_pram :
2008-04-10 17:01:27 +02:00
cpm_uart_unmap_pram ( pinfo , pram ) ;
2007-07-17 17:59:06 -05:00
out_mem :
iounmap ( mem ) ;
return ret ;
}
2005-04-16 15:20:36 -07:00
# ifdef CONFIG_SERIAL_CPM_CONSOLE
/*
* Print a string to the serial port trying not to disturb
* any possible real use of the port . . .
*
* Note that this is called with interrupts already disabled
*/
static void cpm_uart_console_write ( struct console * co , const char * s ,
u_int count )
{
2007-07-17 17:59:06 -05:00
struct uart_cpm_port * pinfo = & cpm_uart_ports [ co - > index ] ;
2008-05-20 14:39:17 -05:00
unsigned long flags ;
serial: cpm_uart: Avoid suspicious locking
CHECK drivers/tty/serial/cpm_uart/cpm_uart_core.c
drivers/tty/serial/cpm_uart/cpm_uart_core.c:1271:39: warning: context imbalance in 'cpm_uart_console_write' - unexpected unlock
Allthough 'nolock' is not expected to change, sparse find the following
form suspicious:
if (unlikely(nolock)) {
local_irq_save(flags);
} else {
spin_lock_irqsave(&pinfo->port.lock, flags);
}
cpm_uart_early_write(pinfo, s, count, true);
if (unlikely(nolock)) {
local_irq_restore(flags);
} else {
spin_unlock_irqrestore(&pinfo->port.lock, flags);
}
Rewrite it a more obvious form:
if (unlikely(oops_in_progress)) {
local_irq_save(flags);
cpm_uart_early_write(pinfo, s, count, true);
local_irq_restore(flags);
} else {
spin_lock_irqsave(&pinfo->port.lock, flags);
cpm_uart_early_write(pinfo, s, count, true);
spin_unlock_irqrestore(&pinfo->port.lock, flags);
}
Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
Link: https://lore.kernel.org/r/f7da5cdc9287960185829cfef681a7d8614efa1f.1691068700.git.christophe.leroy@csgroup.eu
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-03 15:56:42 +02:00
if ( unlikely ( oops_in_progress ) ) {
2008-05-20 14:39:17 -05:00
local_irq_save ( flags ) ;
serial: cpm_uart: Avoid suspicious locking
CHECK drivers/tty/serial/cpm_uart/cpm_uart_core.c
drivers/tty/serial/cpm_uart/cpm_uart_core.c:1271:39: warning: context imbalance in 'cpm_uart_console_write' - unexpected unlock
Allthough 'nolock' is not expected to change, sparse find the following
form suspicious:
if (unlikely(nolock)) {
local_irq_save(flags);
} else {
spin_lock_irqsave(&pinfo->port.lock, flags);
}
cpm_uart_early_write(pinfo, s, count, true);
if (unlikely(nolock)) {
local_irq_restore(flags);
} else {
spin_unlock_irqrestore(&pinfo->port.lock, flags);
}
Rewrite it a more obvious form:
if (unlikely(oops_in_progress)) {
local_irq_save(flags);
cpm_uart_early_write(pinfo, s, count, true);
local_irq_restore(flags);
} else {
spin_lock_irqsave(&pinfo->port.lock, flags);
cpm_uart_early_write(pinfo, s, count, true);
spin_unlock_irqrestore(&pinfo->port.lock, flags);
}
Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
Link: https://lore.kernel.org/r/f7da5cdc9287960185829cfef681a7d8614efa1f.1691068700.git.christophe.leroy@csgroup.eu
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-03 15:56:42 +02:00
cpm_uart_early_write ( pinfo , s , count , true ) ;
2008-05-20 14:39:17 -05:00
local_irq_restore ( flags ) ;
} else {
2023-09-14 20:43:40 +02:06
uart_port_lock_irqsave ( & pinfo - > port , & flags ) ;
serial: cpm_uart: Avoid suspicious locking
CHECK drivers/tty/serial/cpm_uart/cpm_uart_core.c
drivers/tty/serial/cpm_uart/cpm_uart_core.c:1271:39: warning: context imbalance in 'cpm_uart_console_write' - unexpected unlock
Allthough 'nolock' is not expected to change, sparse find the following
form suspicious:
if (unlikely(nolock)) {
local_irq_save(flags);
} else {
spin_lock_irqsave(&pinfo->port.lock, flags);
}
cpm_uart_early_write(pinfo, s, count, true);
if (unlikely(nolock)) {
local_irq_restore(flags);
} else {
spin_unlock_irqrestore(&pinfo->port.lock, flags);
}
Rewrite it a more obvious form:
if (unlikely(oops_in_progress)) {
local_irq_save(flags);
cpm_uart_early_write(pinfo, s, count, true);
local_irq_restore(flags);
} else {
spin_lock_irqsave(&pinfo->port.lock, flags);
cpm_uart_early_write(pinfo, s, count, true);
spin_unlock_irqrestore(&pinfo->port.lock, flags);
}
Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
Link: https://lore.kernel.org/r/f7da5cdc9287960185829cfef681a7d8614efa1f.1691068700.git.christophe.leroy@csgroup.eu
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-03 15:56:42 +02:00
cpm_uart_early_write ( pinfo , s , count , true ) ;
2023-09-14 20:43:40 +02:06
uart_port_unlock_irqrestore ( & pinfo - > port , flags ) ;
2008-05-20 14:39:17 -05:00
}
2005-04-16 15:20:36 -07:00
}
2006-04-25 20:26:41 +04:00
2005-04-16 15:20:36 -07:00
static int __init cpm_uart_console_setup ( struct console * co , char * options )
{
int baud = 38400 ;
int bits = 8 ;
int parity = ' n ' ;
int flow = ' n ' ;
int ret ;
2007-07-17 17:59:06 -05:00
struct uart_cpm_port * pinfo ;
struct uart_port * port ;
2017-02-01 15:20:12 -08:00
struct device_node * np ;
2007-07-17 17:59:06 -05:00
int i = 0 ;
if ( co - > index > = UART_NR ) {
printk ( KERN_ERR " cpm_uart: console index %d too high \n " ,
co - > index ) ;
return - ENODEV ;
}
2017-02-01 15:20:12 -08:00
for_each_node_by_type ( np , " serial " ) {
2007-07-17 17:59:06 -05:00
if ( ! of_device_is_compatible ( np , " fsl,cpm1-smc-uart " ) & &
! of_device_is_compatible ( np , " fsl,cpm1-scc-uart " ) & &
! of_device_is_compatible ( np , " fsl,cpm2-smc-uart " ) & &
! of_device_is_compatible ( np , " fsl,cpm2-scc-uart " ) )
2017-02-01 15:20:12 -08:00
continue ;
if ( i + + = = co - > index )
break ;
}
if ( ! np )
return - ENODEV ;
2007-07-17 17:59:06 -05:00
pinfo = & cpm_uart_ports [ co - > index ] ;
pinfo - > flags | = FLAG_CONSOLE ;
port = & pinfo - > port ;
ret = cpm_uart_init_port ( np , pinfo ) ;
of_node_put ( np ) ;
if ( ret )
return ret ;
2005-04-16 15:20:36 -07:00
if ( options ) {
uart_parse_options ( options , & baud , & parity , & bits , & flow ) ;
} else {
2023-08-03 15:56:45 +02:00
baud = get_baudrate ( ) ;
if ( baud = = - 1 )
2005-04-16 15:20:36 -07:00
baud = 9600 ;
}
if ( IS_SMC ( pinfo ) ) {
2008-07-23 21:29:50 -07:00
out_be16 ( & pinfo - > smcup - > smc_brkcr , 0 ) ;
cpm_line_cr_cmd ( pinfo , CPM_CR_STOP_TX ) ;
2007-07-24 15:53:07 -05:00
clrbits8 ( & pinfo - > smcp - > smc_smcm , SMCM_RX | SMCM_TX ) ;
clrbits16 ( & pinfo - > smcp - > smc_smcmr , SMCMR_REN | SMCMR_TEN ) ;
2005-04-16 15:20:36 -07:00
} else {
2008-07-23 21:29:50 -07:00
out_be16 ( & pinfo - > sccup - > scc_brkcr , 0 ) ;
cpm_line_cr_cmd ( pinfo , CPM_CR_GRA_STOP_TX ) ;
2007-07-24 15:53:07 -05:00
clrbits16 ( & pinfo - > sccp - > scc_sccm , UART_SCCM_TX | UART_SCCM_RX ) ;
clrbits32 ( & pinfo - > sccp - > scc_gsmrl , SCC_GSMRL_ENR | SCC_GSMRL_ENT ) ;
2005-04-16 15:20:36 -07:00
}
ret = cpm_uart_allocbuf ( pinfo , 1 ) ;
if ( ret )
return ret ;
cpm_uart_initbd ( pinfo ) ;
if ( IS_SMC ( pinfo ) )
cpm_uart_init_smc ( pinfo ) ;
else
cpm_uart_init_scc ( pinfo ) ;
uart_set_options ( port , co , baud , parity , bits , flow ) ;
2007-07-17 18:09:33 -05:00
cpm_line_cr_cmd ( pinfo , CPM_CR_RESTART_TX ) ;
2005-04-16 15:20:36 -07:00
2020-12-23 09:38:46 +00:00
# ifdef CONFIG_CONSOLE_POLL
if ( ! udbg_port ) {
udbg_port = & pinfo - > port ;
udbg_putc = udbg_cpm_putc ;
udbg_getc = udbg_cpm_getc ;
udbg_getc_poll = udbg_cpm_getc_poll ;
}
# endif
2005-04-16 15:20:36 -07:00
return 0 ;
}
2005-08-09 10:08:02 -07:00
static struct uart_driver cpm_reg ;
2005-04-16 15:20:36 -07:00
static struct console cpm_scc_uart_console = {
2005-08-09 10:08:02 -07:00
. name = " ttyCPM " ,
. write = cpm_uart_console_write ,
. device = uart_console_device ,
. setup = cpm_uart_console_setup ,
. flags = CON_PRINTBUFFER ,
. index = - 1 ,
2005-04-16 15:20:36 -07:00
. data = & cpm_reg ,
} ;
2007-07-24 15:53:07 -05:00
static int __init cpm_uart_console_init ( void )
2005-04-16 15:20:36 -07:00
{
2020-02-13 12:43:42 +01:00
cpm_muram_init ( ) ;
2006-04-25 20:26:41 +04:00
register_console ( & cpm_scc_uart_console ) ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
console_initcall ( cpm_uart_console_init ) ;
# define CPM_UART_CONSOLE &cpm_scc_uart_console
# else
# define CPM_UART_CONSOLE NULL
# endif
static struct uart_driver cpm_reg = {
. owner = THIS_MODULE ,
. driver_name = " ttyCPM " ,
. dev_name = " ttyCPM " ,
. major = SERIAL_CPM_MAJOR ,
. minor = SERIAL_CPM_MINOR ,
. cons = CPM_UART_CONSOLE ,
2007-07-17 17:59:06 -05:00
. nr = UART_NR ,
} ;
static int probe_index ;
2012-11-19 13:21:50 -05:00
static int cpm_uart_probe ( struct platform_device * ofdev )
2007-07-17 17:59:06 -05:00
{
int index = probe_index + + ;
struct uart_cpm_port * pinfo = & cpm_uart_ports [ index ] ;
int ret ;
pinfo - > port . line = index ;
if ( index > = UART_NR )
return - ENODEV ;
2013-05-23 19:39:36 +09:00
platform_set_drvdata ( ofdev , pinfo ) ;
2007-07-17 17:59:06 -05:00
2009-04-03 15:48:44 -05:00
/* initialize the device pointer for the port */
pinfo - > port . dev = & ofdev - > dev ;
2022-09-30 10:33:56 +02:00
pinfo - > port . irq = irq_of_parse_and_map ( ofdev - > dev . of_node , 0 ) ;
if ( ! pinfo - > port . irq )
return - EINVAL ;
2010-04-13 16:12:29 -07:00
ret = cpm_uart_init_port ( ofdev - > dev . of_node , pinfo ) ;
2022-09-30 10:33:56 +02:00
if ( ! ret )
return uart_add_one_port ( & cpm_reg , & pinfo - > port ) ;
2007-07-17 17:59:06 -05:00
2022-09-30 10:33:56 +02:00
irq_dispose_mapping ( pinfo - > port . irq ) ;
return ret ;
2007-07-17 17:59:06 -05:00
}
2012-11-19 13:26:18 -05:00
static int cpm_uart_remove ( struct platform_device * ofdev )
2007-07-17 17:59:06 -05:00
{
2013-05-23 19:39:36 +09:00
struct uart_cpm_port * pinfo = platform_get_drvdata ( ofdev ) ;
2023-05-12 19:38:10 +02:00
uart_remove_one_port ( & cpm_reg , & pinfo - > port ) ;
return 0 ;
2007-07-17 17:59:06 -05:00
}
2015-03-16 20:17:11 +01:00
static const struct of_device_id cpm_uart_match [ ] = {
2007-07-17 17:59:06 -05:00
{
. compatible = " fsl,cpm1-smc-uart " ,
} ,
{
. compatible = " fsl,cpm1-scc-uart " ,
} ,
{
. compatible = " fsl,cpm2-smc-uart " ,
} ,
{
. compatible = " fsl,cpm2-scc-uart " ,
} ,
{ }
2005-04-16 15:20:36 -07:00
} ;
2015-09-18 20:03:14 +02:00
MODULE_DEVICE_TABLE ( of , cpm_uart_match ) ;
2007-07-17 17:59:06 -05:00
2011-02-22 21:10:26 -07:00
static struct platform_driver cpm_uart_driver = {
2010-04-13 16:13:02 -07:00
. driver = {
. name = " cpm_uart " ,
. of_match_table = cpm_uart_match ,
} ,
2007-07-17 17:59:06 -05:00
. probe = cpm_uart_probe ,
. remove = cpm_uart_remove ,
} ;
static int __init cpm_uart_init ( void )
{
int ret = uart_register_driver ( & cpm_reg ) ;
if ( ret )
return ret ;
2011-02-22 21:10:26 -07:00
ret = platform_driver_register ( & cpm_uart_driver ) ;
2007-07-17 17:59:06 -05:00
if ( ret )
uart_unregister_driver ( & cpm_reg ) ;
return ret ;
}
static void __exit cpm_uart_exit ( void )
{
2011-02-22 21:10:26 -07:00
platform_driver_unregister ( & cpm_uart_driver ) ;
2007-07-17 17:59:06 -05:00
uart_unregister_driver ( & cpm_reg ) ;
}
2005-04-16 15:20:36 -07:00
module_init ( cpm_uart_init ) ;
module_exit ( cpm_uart_exit ) ;
MODULE_AUTHOR ( " Kumar Gala/Antoniou Pantelis " ) ;
MODULE_DESCRIPTION ( " CPM SCC/SMC port driver $Revision: 0.01 $ " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS_CHARDEV ( SERIAL_CPM_MAJOR , SERIAL_CPM_MINOR ) ;