2019-06-01 10:08:55 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2012-05-09 15:27:21 +02:00
/**
* ipoctal . c
*
* driver for the GE IP - OCTAL boards
2012-11-16 16:19:58 +01:00
*
* Copyright ( C ) 2009 - 2012 CERN ( www . cern . ch )
* Author : Nicolas Serafini , EIC2 SA
* Author : Samuel Iglesias Gonsalvez < siglesias @ igalia . com >
2012-05-09 15:27:21 +02:00
*/
# include <linux/device.h>
# include <linux/module.h>
# include <linux/interrupt.h>
# include <linux/sched.h>
# include <linux/tty.h>
# include <linux/serial.h>
# include <linux/tty_flip.h>
# include <linux/slab.h>
2012-09-04 17:01:08 +02:00
# include <linux/io.h>
2012-11-16 19:33:45 +01:00
# include <linux/ipack.h>
2012-05-09 15:27:21 +02:00
# include "ipoctal.h"
# include "scc2698.h"
# define IP_OCTAL_ID_SPACE_VECTOR 0x41
# define IP_OCTAL_NB_BLOCKS 4
static const struct tty_operations ipoctal_fops ;
2012-09-12 14:55:26 +02:00
struct ipoctal_channel {
struct ipoctal_stats stats ;
unsigned int nb_bytes ;
wait_queue_head_t queue ;
spinlock_t lock ;
unsigned int pointer_read ;
unsigned int pointer_write ;
struct tty_port tty_port ;
union scc2698_channel __iomem * regs ;
union scc2698_block __iomem * block_regs ;
2012-09-12 14:55:28 +02:00
unsigned int board_id ;
2012-09-12 14:55:29 +02:00
u8 isr_rx_rdy_mask ;
u8 isr_tx_rdy_mask ;
2012-12-10 11:50:07 +01:00
unsigned int rx_enable ;
2012-09-12 14:55:26 +02:00
} ;
2012-05-09 15:27:21 +02:00
struct ipoctal {
struct ipack_device * dev ;
unsigned int board_id ;
2012-09-12 14:55:26 +02:00
struct ipoctal_channel channel [ NR_CHANNELS ] ;
2012-05-09 15:27:21 +02:00
struct tty_driver * tty_drv ;
2012-09-27 12:37:36 +02:00
u8 __iomem * mem8_space ;
2012-09-27 12:37:34 +02:00
u8 __iomem * int_space ;
2012-05-09 15:27:21 +02:00
} ;
2014-09-02 17:31:41 +02:00
static inline struct ipoctal * chan_to_ipoctal ( struct ipoctal_channel * chan ,
unsigned int index )
{
return container_of ( chan , struct ipoctal , channel [ index ] ) ;
}
2014-09-02 17:31:39 +02:00
static void ipoctal_reset_channel ( struct ipoctal_channel * channel )
{
iowrite8 ( CR_DISABLE_RX | CR_DISABLE_TX , & channel - > regs - > w . cr ) ;
channel - > rx_enable = 0 ;
iowrite8 ( CR_CMD_RESET_RX , & channel - > regs - > w . cr ) ;
iowrite8 ( CR_CMD_RESET_TX , & channel - > regs - > w . cr ) ;
iowrite8 ( CR_CMD_RESET_ERR_STATUS , & channel - > regs - > w . cr ) ;
iowrite8 ( CR_CMD_RESET_MR , & channel - > regs - > w . cr ) ;
}
2012-05-09 15:27:21 +02:00
static int ipoctal_port_activate ( struct tty_port * port , struct tty_struct * tty )
{
2012-09-12 14:55:26 +02:00
struct ipoctal_channel * channel ;
2012-05-09 15:27:21 +02:00
2012-09-12 14:55:42 +02:00
channel = dev_get_drvdata ( tty - > dev ) ;
2012-05-09 15:27:21 +02:00
2012-12-10 11:50:03 +01:00
/*
* Enable RX . TX will be enabled when
* there is something to send
*/
2012-09-12 14:55:27 +02:00
iowrite8 ( CR_ENABLE_RX , & channel - > regs - > w . cr ) ;
2012-12-10 11:50:07 +01:00
channel - > rx_enable = 1 ;
2012-05-09 15:27:21 +02:00
return 0 ;
}
static int ipoctal_open ( struct tty_struct * tty , struct file * file )
{
2014-09-02 17:31:41 +02:00
struct ipoctal_channel * channel = dev_get_drvdata ( tty - > dev ) ;
struct ipoctal * ipoctal = chan_to_ipoctal ( channel , tty - > index ) ;
int err ;
2012-05-09 15:27:21 +02:00
2012-09-12 14:55:28 +02:00
tty - > driver_data = channel ;
2012-07-13 13:33:13 +02:00
2014-09-02 17:31:41 +02:00
if ( ! ipack_get_carrier ( ipoctal - > dev ) )
return - EBUSY ;
err = tty_port_open ( & channel - > tty_port , tty , file ) ;
if ( err )
ipack_put_carrier ( ipoctal - > dev ) ;
return err ;
2012-05-09 15:27:21 +02:00
}
static void ipoctal_reset_stats ( struct ipoctal_stats * stats )
{
stats - > tx = 0 ;
stats - > rx = 0 ;
stats - > rcv_break = 0 ;
stats - > framing_err = 0 ;
stats - > overrun_err = 0 ;
stats - > parity_err = 0 ;
}
2012-09-12 14:55:28 +02:00
static void ipoctal_free_channel ( struct ipoctal_channel * channel )
2012-05-09 15:27:21 +02:00
{
2012-09-12 14:55:26 +02:00
ipoctal_reset_stats ( & channel - > stats ) ;
channel - > pointer_read = 0 ;
channel - > pointer_write = 0 ;
channel - > nb_bytes = 0 ;
2012-05-09 15:27:21 +02:00
}
static void ipoctal_close ( struct tty_struct * tty , struct file * filp )
{
2012-09-12 14:55:28 +02:00
struct ipoctal_channel * channel = tty - > driver_data ;
2012-05-09 15:27:21 +02:00
2012-09-12 14:55:26 +02:00
tty_port_close ( & channel - > tty_port , tty , filp ) ;
2012-12-10 11:49:59 +01:00
ipoctal_free_channel ( channel ) ;
2012-05-09 15:27:21 +02:00
}
static int ipoctal_get_icount ( struct tty_struct * tty ,
struct serial_icounter_struct * icount )
{
2012-09-12 14:55:28 +02:00
struct ipoctal_channel * channel = tty - > driver_data ;
2012-05-09 15:27:21 +02:00
icount - > cts = 0 ;
icount - > dsr = 0 ;
icount - > rng = 0 ;
icount - > dcd = 0 ;
2012-09-12 14:55:26 +02:00
icount - > rx = channel - > stats . rx ;
icount - > tx = channel - > stats . tx ;
icount - > frame = channel - > stats . framing_err ;
icount - > parity = channel - > stats . parity_err ;
icount - > brk = channel - > stats . rcv_break ;
2012-05-09 15:27:21 +02:00
return 0 ;
}
2013-01-03 15:53:06 +01:00
static void ipoctal_irq_rx ( struct ipoctal_channel * channel , u8 sr )
2012-05-09 15:27:21 +02:00
{
2013-01-03 15:53:03 +01:00
struct tty_port * port = & channel - > tty_port ;
2012-09-12 14:55:43 +02:00
unsigned char value ;
2012-12-10 11:50:01 +01:00
unsigned char flag ;
2012-09-12 14:55:43 +02:00
u8 isr ;
do {
value = ioread8 ( & channel - > regs - > r . rhr ) ;
2012-12-10 11:50:01 +01:00
flag = TTY_NORMAL ;
2012-09-12 14:55:43 +02:00
/* Error: count statistics */
if ( sr & SR_ERROR ) {
iowrite8 ( CR_CMD_RESET_ERR_STATUS , & channel - > regs - > w . cr ) ;
if ( sr & SR_OVERRUN_ERROR ) {
channel - > stats . overrun_err + + ;
/* Overrun doesn't affect the current character*/
2013-01-03 15:53:03 +01:00
tty_insert_flip_char ( port , 0 , TTY_OVERRUN ) ;
2012-09-12 14:55:43 +02:00
}
if ( sr & SR_PARITY_ERROR ) {
channel - > stats . parity_err + + ;
flag = TTY_PARITY ;
}
if ( sr & SR_FRAMING_ERROR ) {
channel - > stats . framing_err + + ;
flag = TTY_FRAME ;
}
if ( sr & SR_RECEIVED_BREAK ) {
channel - > stats . rcv_break + + ;
flag = TTY_BREAK ;
}
2012-09-12 14:55:30 +02:00
}
2013-01-03 15:53:03 +01:00
tty_insert_flip_char ( port , value , flag ) ;
2012-09-12 14:55:43 +02:00
/* Check if there are more characters in RX FIFO
* If there are more , the isr register for this channel
* has enabled the RxRDY | FFULL bit .
*/
isr = ioread8 ( & channel - > block_regs - > r . isr ) ;
sr = ioread8 ( & channel - > regs - > r . sr ) ;
} while ( isr & channel - > isr_rx_rdy_mask ) ;
2012-09-12 14:55:30 +02:00
2013-01-03 15:53:06 +01:00
tty_flip_buffer_push ( port ) ;
2012-09-12 14:55:30 +02:00
}
static void ipoctal_irq_tx ( struct ipoctal_channel * channel )
{
unsigned char value ;
unsigned int * pointer_write = & channel - > pointer_write ;
2012-12-10 11:49:58 +01:00
if ( channel - > nb_bytes = = 0 )
2012-09-12 14:55:30 +02:00
return ;
2012-05-09 15:27:21 +02:00
2014-06-26 09:46:24 +02:00
spin_lock ( & channel - > lock ) ;
2012-09-12 14:55:30 +02:00
value = channel - > tty_port . xmit_buf [ * pointer_write ] ;
iowrite8 ( value , & channel - > regs - > w . thr ) ;
channel - > stats . tx + + ;
( * pointer_write ) + + ;
* pointer_write = * pointer_write % PAGE_SIZE ;
channel - > nb_bytes - - ;
2014-06-26 09:46:24 +02:00
spin_unlock ( & channel - > lock ) ;
2012-09-12 14:55:30 +02:00
}
2012-05-09 15:27:21 +02:00
2012-09-12 14:55:30 +02:00
static void ipoctal_irq_channel ( struct ipoctal_channel * channel )
{
u8 isr , sr ;
/* The HW is organized in pair of channels. See which register we need
* to read from */
isr = ioread8 ( & channel - > block_regs - > r . isr ) ;
sr = ioread8 ( & channel - > regs - > r . sr ) ;
2014-09-01 13:49:56 +02:00
if ( isr & ( IMR_DELTA_BREAK_A | IMR_DELTA_BREAK_B ) )
iowrite8 ( CR_CMD_RESET_BREAK_CHANGE , & channel - > regs - > w . cr ) ;
2012-12-10 11:50:02 +01:00
if ( ( sr & SR_TX_EMPTY ) & & ( channel - > nb_bytes = = 0 ) ) {
2012-09-12 14:55:30 +02:00
iowrite8 ( CR_DISABLE_TX , & channel - > regs - > w . cr ) ;
2012-12-10 11:50:02 +01:00
/* In case of RS-485, change from TX to RX when finishing TX.
* Half - duplex . */
if ( channel - > board_id = = IPACK1_DEVICE_ID_SBS_OCTAL_485 ) {
iowrite8 ( CR_CMD_NEGATE_RTSN , & channel - > regs - > w . cr ) ;
iowrite8 ( CR_ENABLE_RX , & channel - > regs - > w . cr ) ;
2013-01-18 08:57:21 +01:00
channel - > rx_enable = 1 ;
2012-12-10 11:50:02 +01:00
}
2012-05-09 15:27:21 +02:00
}
2012-09-12 14:55:30 +02:00
/* RX data */
if ( ( isr & channel - > isr_rx_rdy_mask ) & & ( sr & SR_RX_READY ) )
2013-01-03 15:53:06 +01:00
ipoctal_irq_rx ( channel , sr ) ;
2012-09-12 14:55:30 +02:00
/* TX of each character */
if ( ( isr & channel - > isr_tx_rdy_mask ) & & ( sr & SR_TX_READY ) )
ipoctal_irq_tx ( channel ) ;
}
2012-09-12 14:55:38 +02:00
static irqreturn_t ipoctal_irq_handler ( void * arg )
2012-09-12 14:55:30 +02:00
{
unsigned int i ;
struct ipoctal * ipoctal = ( struct ipoctal * ) arg ;
2012-09-13 12:32:20 +02:00
/* Clear the IPack device interrupt */
2012-09-27 12:37:34 +02:00
readw ( ipoctal - > int_space + ACK_INT_REQ0 ) ;
readw ( ipoctal - > int_space + ACK_INT_REQ1 ) ;
2012-09-13 12:32:20 +02:00
2012-12-10 11:50:04 +01:00
/* Check all channels */
for ( i = 0 ; i < NR_CHANNELS ; i + + )
ipoctal_irq_channel ( & ipoctal - > channel [ i ] ) ;
2012-05-09 15:27:21 +02:00
return IRQ_HANDLED ;
}
static const struct tty_port_operations ipoctal_tty_port_ops = {
. dtr_rts = NULL ,
. activate = ipoctal_port_activate ,
} ;
static int ipoctal_inst_slot ( struct ipoctal * ipoctal , unsigned int bus_nr ,
2012-09-13 12:32:21 +02:00
unsigned int slot )
2012-05-09 15:27:21 +02:00
{
2012-09-27 12:37:34 +02:00
int res ;
2012-05-09 15:27:21 +02:00
int i ;
struct tty_driver * tty ;
char name [ 20 ] ;
2012-09-12 14:55:26 +02:00
struct ipoctal_channel * channel ;
2012-09-27 12:37:34 +02:00
struct ipack_region * region ;
void __iomem * addr ;
2012-09-12 14:55:26 +02:00
union scc2698_channel __iomem * chan_regs ;
union scc2698_block __iomem * block_regs ;
2012-05-09 15:27:21 +02:00
2012-09-27 12:37:33 +02:00
ipoctal - > board_id = ipoctal - > dev - > id_device ;
2012-05-09 15:27:21 +02:00
2012-09-27 12:37:34 +02:00
region = & ipoctal - > dev - > region [ IPACK_IO_SPACE ] ;
2020-01-06 09:43:50 +01:00
addr = devm_ioremap ( & ipoctal - > dev - > dev ,
2012-09-27 12:37:34 +02:00
region - > start , region - > size ) ;
if ( ! addr ) {
2012-05-25 13:08:13 +02:00
dev_err ( & ipoctal - > dev - > dev ,
" Unable to map slot [%d:%d] IO space! \n " ,
bus_nr , slot ) ;
2012-09-27 12:37:34 +02:00
return - EADDRNOTAVAIL ;
2012-05-09 15:27:21 +02:00
}
2012-09-27 12:37:34 +02:00
/* Save the virtual address to access the registers easily */
chan_regs =
( union scc2698_channel __iomem * ) addr ;
block_regs =
( union scc2698_block __iomem * ) addr ;
2012-05-09 15:27:21 +02:00
2012-09-27 12:37:34 +02:00
region = & ipoctal - > dev - > region [ IPACK_INT_SPACE ] ;
ipoctal - > int_space =
2020-01-06 09:43:50 +01:00
devm_ioremap ( & ipoctal - > dev - > dev ,
2012-09-27 12:37:34 +02:00
region - > start , region - > size ) ;
if ( ! ipoctal - > int_space ) {
2012-09-13 12:32:20 +02:00
dev_err ( & ipoctal - > dev - > dev ,
" Unable to map slot [%d:%d] INT space! \n " ,
bus_nr , slot ) ;
2012-09-27 12:37:34 +02:00
return - EADDRNOTAVAIL ;
2012-09-13 12:32:20 +02:00
}
2012-09-27 12:37:36 +02:00
region = & ipoctal - > dev - > region [ IPACK_MEM8_SPACE ] ;
ipoctal - > mem8_space =
2020-01-06 09:43:50 +01:00
devm_ioremap ( & ipoctal - > dev - > dev ,
2012-09-27 12:37:34 +02:00
region - > start , 0x8000 ) ;
2013-01-21 14:02:53 +01:00
if ( ! ipoctal - > mem8_space ) {
2012-05-25 13:08:13 +02:00
dev_err ( & ipoctal - > dev - > dev ,
2012-09-27 12:37:36 +02:00
" Unable to map slot [%d:%d] MEM8 space! \n " ,
2012-05-25 13:08:13 +02:00
bus_nr , slot ) ;
2012-09-27 12:37:34 +02:00
return - EADDRNOTAVAIL ;
2012-05-09 15:27:21 +02:00
}
/* Disable RX and TX before touching anything */
for ( i = 0 ; i < NR_CHANNELS ; i + + ) {
2012-09-12 14:55:26 +02:00
struct ipoctal_channel * channel = & ipoctal - > channel [ i ] ;
channel - > regs = chan_regs + i ;
channel - > block_regs = block_regs + ( i > > 1 ) ;
2012-09-12 14:55:28 +02:00
channel - > board_id = ipoctal - > board_id ;
2012-09-12 14:55:29 +02:00
if ( i & 1 ) {
channel - > isr_tx_rdy_mask = ISR_TxRDY_B ;
channel - > isr_rx_rdy_mask = ISR_RxRDY_FFULL_B ;
} else {
channel - > isr_tx_rdy_mask = ISR_TxRDY_A ;
channel - > isr_rx_rdy_mask = ISR_RxRDY_FFULL_A ;
}
2012-09-12 14:55:26 +02:00
2014-09-02 17:31:39 +02:00
ipoctal_reset_channel ( channel ) ;
2012-09-12 14:55:27 +02:00
iowrite8 ( MR1_CHRL_8_BITS | MR1_ERROR_CHAR | MR1_RxINT_RxRDY ,
& channel - > regs - > w . mr ) ; /* mr1 */
iowrite8 ( 0 , & channel - > regs - > w . mr ) ; /* mr2 */
iowrite8 ( TX_CLK_9600 | RX_CLK_9600 , & channel - > regs - > w . csr ) ;
2012-05-09 15:27:21 +02:00
}
for ( i = 0 ; i < IP_OCTAL_NB_BLOCKS ; i + + ) {
2012-09-12 14:55:27 +02:00
iowrite8 ( ACR_BRG_SET2 , & block_regs [ i ] . w . acr ) ;
iowrite8 ( OPCR_MPP_OUTPUT | OPCR_MPOa_RTSN | OPCR_MPOb_RTSN ,
& block_regs [ i ] . w . opcr ) ;
iowrite8 ( IMR_TxRDY_A | IMR_RxRDY_FFULL_A | IMR_DELTA_BREAK_A |
IMR_TxRDY_B | IMR_RxRDY_FFULL_B | IMR_DELTA_BREAK_B ,
& block_regs [ i ] . w . imr ) ;
2012-05-09 15:27:21 +02:00
}
2012-09-13 12:32:21 +02:00
/* Dummy write */
2012-09-27 12:37:36 +02:00
iowrite8 ( 1 , ipoctal - > mem8_space + 1 ) ;
2012-05-09 15:27:21 +02:00
/* Register the TTY device */
/* Each IP-OCTAL channel is a TTY port */
tty = alloc_tty_driver ( NR_CHANNELS ) ;
2012-09-27 12:37:34 +02:00
if ( ! tty )
return - ENOMEM ;
2012-05-09 15:27:21 +02:00
/* Fill struct tty_driver with ipoctal data */
tty - > owner = THIS_MODULE ;
2012-09-12 14:55:41 +02:00
tty - > driver_name = KBUILD_MODNAME ;
sprintf ( name , KBUILD_MODNAME " .%d.%d. " , bus_nr , slot ) ;
2012-05-09 15:27:21 +02:00
tty - > name = name ;
tty - > major = 0 ;
tty - > minor_start = 0 ;
tty - > type = TTY_DRIVER_TYPE_SERIAL ;
tty - > subtype = SERIAL_TYPE_NORMAL ;
tty - > flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV ;
tty - > init_termios = tty_std_termios ;
tty - > init_termios . c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL ;
tty - > init_termios . c_ispeed = 9600 ;
tty - > init_termios . c_ospeed = 9600 ;
tty_set_operations ( tty , & ipoctal_fops ) ;
res = tty_register_driver ( tty ) ;
if ( res ) {
2012-05-25 13:08:13 +02:00
dev_err ( & ipoctal - > dev - > dev , " Can't register tty driver. \n " ) ;
2012-05-09 15:27:21 +02:00
put_tty_driver ( tty ) ;
2012-09-27 12:37:34 +02:00
return res ;
2012-05-09 15:27:21 +02:00
}
/* Save struct tty_driver for use it when uninstalling the device */
ipoctal - > tty_drv = tty ;
for ( i = 0 ; i < NR_CHANNELS ; i + + ) {
2012-09-12 14:55:40 +02:00
struct device * tty_dev ;
2012-09-12 14:55:26 +02:00
channel = & ipoctal - > channel [ i ] ;
tty_port_init ( & channel - > tty_port ) ;
tty_port_alloc_xmit_buf ( & channel - > tty_port ) ;
channel - > tty_port . ops = & ipoctal_tty_port_ops ;
ipoctal_reset_stats ( & channel - > stats ) ;
channel - > nb_bytes = 0 ;
spin_lock_init ( & channel - > lock ) ;
channel - > pointer_read = 0 ;
channel - > pointer_write = 0 ;
TTY merge for 3.7-rc1
As we skipped the merge window for 3.6-rc1 for the tty tree, everything
is now settled down and working properly, so we are ready for 3.7-rc1.
Here's the patchset, it's big, but the large changes are removing a
firmware file and adding a staging tty driver (it depended on the tty
core changes, so it's going through this tree instead of the staging
tree.)
All of these patches have been in the linux-next tree for a while.
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.19 (GNU/Linux)
iEYEABECAAYFAlBp36oACgkQMUfUDdst+yk4WgCdEy13hot8fI2Lqnc7W0LKu7GX
4p8AoLTjzrXhLosxdijskDQ9X1OtjrxU
=S5Ng
-----END PGP SIGNATURE-----
Merge tag 'tty-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull TTY changes from Greg Kroah-Hartman:
"As we skipped the merge window for 3.6-rc1 for the tty tree,
everything is now settled down and working properly, so we are ready
for 3.7-rc1. Here's the patchset, it's big, but the large changes are
removing a firmware file and adding a staging tty driver (it depended
on the tty core changes, so it's going through this tree instead of
the staging tree.)
All of these patches have been in the linux-next tree for a while.
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>"
Fix up more-or-less trivial conflicts in
- drivers/char/pcmcia/synclink_cs.c:
tty NULL dereference fix vs tty_port_cts_enabled() helper function
- drivers/staging/{Kconfig,Makefile}:
add-add conflict (dgrp driver added close to other staging drivers)
- drivers/staging/ipack/devices/ipoctal.c:
"split ipoctal_channel from iopctal" vs "TTY: use tty_port_register_device"
* tag 'tty-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (235 commits)
tty/serial: Add kgdb_nmi driver
tty/serial/amba-pl011: Quiesce interrupts in poll_get_char
tty/serial/amba-pl011: Implement poll_init callback
tty/serial/core: Introduce poll_init callback
kdb: Turn KGDB_KDB=n stubs into static inlines
kdb: Implement disable_nmi command
kernel/debug: Mask KGDB NMI upon entry
serial: pl011: handle corruption at high clock speeds
serial: sccnxp: Make 'default' choice in switch last
serial: sccnxp: Remove mask termios caps for SW flow control
serial: sccnxp: Report actual baudrate back to core
serial: samsung: Add poll_get_char & poll_put_char
Powerpc 8xx CPM_UART setting MAXIDL register proportionaly to baud rate
Powerpc 8xx CPM_UART maxidl should not depend on fifo size
Powerpc 8xx CPM_UART too many interrupts
Powerpc 8xx CPM_UART desynchronisation
serial: set correct baud_base for EXSYS EX-41092 Dual 16950
serial: omap: fix the reciever line error case
8250: blacklist Winbond CIR port
8250_pnp: do pnp probe before legacy probe
...
2012-10-01 12:26:52 -07:00
tty_dev = tty_port_register_device ( & channel - > tty_port , tty , i , NULL ) ;
2012-09-12 14:55:40 +02:00
if ( IS_ERR ( tty_dev ) ) {
dev_err ( & ipoctal - > dev - > dev , " Failed to register tty device. \n " ) ;
2012-11-15 09:49:56 +01:00
tty_port_destroy ( & channel - > tty_port ) ;
2012-09-12 14:55:40 +02:00
continue ;
}
2012-09-12 14:55:42 +02:00
dev_set_drvdata ( tty_dev , channel ) ;
2012-05-09 15:27:21 +02:00
}
2014-07-03 10:53:58 +02:00
/*
* IP - OCTAL has different addresses to copy its IRQ vector .
* Depending of the carrier these addresses are accesible or not .
* More info in the datasheet .
*/
ipoctal - > dev - > bus - > ops - > request_irq ( ipoctal - > dev ,
ipoctal_irq_handler , ipoctal ) ;
2012-05-09 15:27:21 +02:00
return 0 ;
}
2012-09-12 14:55:26 +02:00
static inline int ipoctal_copy_write_buffer ( struct ipoctal_channel * channel ,
2012-05-09 15:27:21 +02:00
const unsigned char * buf ,
int count )
{
unsigned long flags ;
int i ;
2012-09-12 14:55:26 +02:00
unsigned int * pointer_read = & channel - > pointer_read ;
2012-05-09 15:27:21 +02:00
/* Copy the bytes from the user buffer to the internal one */
for ( i = 0 ; i < count ; i + + ) {
2012-09-12 14:55:26 +02:00
if ( i < = ( PAGE_SIZE - channel - > nb_bytes ) ) {
spin_lock_irqsave ( & channel - > lock , flags ) ;
channel - > tty_port . xmit_buf [ * pointer_read ] = buf [ i ] ;
2012-05-09 15:27:21 +02:00
* pointer_read = ( * pointer_read + 1 ) % PAGE_SIZE ;
2012-09-12 14:55:26 +02:00
channel - > nb_bytes + + ;
spin_unlock_irqrestore ( & channel - > lock , flags ) ;
2012-05-09 15:27:21 +02:00
} else {
break ;
}
}
return i ;
}
2012-09-12 14:55:31 +02:00
static int ipoctal_write_tty ( struct tty_struct * tty ,
const unsigned char * buf , int count )
2012-05-09 15:27:21 +02:00
{
2012-09-12 14:55:31 +02:00
struct ipoctal_channel * channel = tty - > driver_data ;
2012-09-13 12:32:23 +02:00
unsigned int char_copied ;
2012-09-12 14:55:31 +02:00
2012-09-13 12:32:23 +02:00
char_copied = ipoctal_copy_write_buffer ( channel , buf , count ) ;
2012-05-09 15:27:21 +02:00
/* As the IP-OCTAL 485 only supports half duplex, do it manually */
2012-09-12 14:55:28 +02:00
if ( channel - > board_id = = IPACK1_DEVICE_ID_SBS_OCTAL_485 ) {
2012-09-12 14:55:27 +02:00
iowrite8 ( CR_DISABLE_RX , & channel - > regs - > w . cr ) ;
2012-12-10 11:50:07 +01:00
channel - > rx_enable = 0 ;
2012-09-12 14:55:27 +02:00
iowrite8 ( CR_CMD_ASSERT_RTSN , & channel - > regs - > w . cr ) ;
2012-05-09 15:27:21 +02:00
}
/*
* Send a packet and then disable TX to avoid failure after several send
* operations
*/
2012-09-12 14:55:27 +02:00
iowrite8 ( CR_ENABLE_TX , & channel - > regs - > w . cr ) ;
2012-09-13 12:32:23 +02:00
return char_copied ;
2012-05-09 15:27:21 +02:00
}
static int ipoctal_write_room ( struct tty_struct * tty )
{
2012-09-12 14:55:28 +02:00
struct ipoctal_channel * channel = tty - > driver_data ;
2012-05-09 15:27:21 +02:00
2012-09-12 14:55:26 +02:00
return PAGE_SIZE - channel - > nb_bytes ;
2012-05-09 15:27:21 +02:00
}
static int ipoctal_chars_in_buffer ( struct tty_struct * tty )
{
2012-09-12 14:55:28 +02:00
struct ipoctal_channel * channel = tty - > driver_data ;
2012-05-09 15:27:21 +02:00
2012-09-12 14:55:26 +02:00
return channel - > nb_bytes ;
2012-05-09 15:27:21 +02:00
}
static void ipoctal_set_termios ( struct tty_struct * tty ,
struct ktermios * old_termios )
{
unsigned int cflag ;
unsigned char mr1 = 0 ;
unsigned char mr2 = 0 ;
unsigned char csr = 0 ;
2012-09-12 14:55:28 +02:00
struct ipoctal_channel * channel = tty - > driver_data ;
2012-05-09 15:27:21 +02:00
speed_t baud ;
2012-07-26 19:00:51 +01:00
cflag = tty - > termios . c_cflag ;
2012-05-09 15:27:21 +02:00
/* Disable and reset everything before change the setup */
2014-09-02 17:31:39 +02:00
ipoctal_reset_channel ( channel ) ;
2012-05-09 15:27:21 +02:00
/* Set Bits per chars */
switch ( cflag & CSIZE ) {
case CS6 :
mr1 | = MR1_CHRL_6_BITS ;
break ;
case CS7 :
mr1 | = MR1_CHRL_7_BITS ;
break ;
case CS8 :
default :
mr1 | = MR1_CHRL_8_BITS ;
/* By default, select CS8 */
2012-07-26 19:00:51 +01:00
tty - > termios . c_cflag = ( cflag & ~ CSIZE ) | CS8 ;
2012-05-09 15:27:21 +02:00
break ;
}
/* Set Parity */
if ( cflag & PARENB )
if ( cflag & PARODD )
mr1 | = MR1_PARITY_ON | MR1_PARITY_ODD ;
else
mr1 | = MR1_PARITY_ON | MR1_PARITY_EVEN ;
else
mr1 | = MR1_PARITY_OFF ;
/* Mark or space parity is not supported */
2012-07-26 19:00:51 +01:00
tty - > termios . c_cflag & = ~ CMSPAR ;
2012-05-09 15:27:21 +02:00
/* Set stop bits */
if ( cflag & CSTOPB )
mr2 | = MR2_STOP_BITS_LENGTH_2 ;
else
mr2 | = MR2_STOP_BITS_LENGTH_1 ;
/* Set the flow control */
2012-09-12 14:55:28 +02:00
switch ( channel - > board_id ) {
2012-09-04 17:01:16 +02:00
case IPACK1_DEVICE_ID_SBS_OCTAL_232 :
2012-05-09 15:27:21 +02:00
if ( cflag & CRTSCTS ) {
mr1 | = MR1_RxRTS_CONTROL_ON ;
mr2 | = MR2_TxRTS_CONTROL_OFF | MR2_CTS_ENABLE_TX_ON ;
} else {
mr1 | = MR1_RxRTS_CONTROL_OFF ;
mr2 | = MR2_TxRTS_CONTROL_OFF | MR2_CTS_ENABLE_TX_OFF ;
}
break ;
2012-09-04 17:01:16 +02:00
case IPACK1_DEVICE_ID_SBS_OCTAL_422 :
2012-05-09 15:27:21 +02:00
mr1 | = MR1_RxRTS_CONTROL_OFF ;
mr2 | = MR2_TxRTS_CONTROL_OFF | MR2_CTS_ENABLE_TX_OFF ;
break ;
2012-09-04 17:01:16 +02:00
case IPACK1_DEVICE_ID_SBS_OCTAL_485 :
2012-05-09 15:27:21 +02:00
mr1 | = MR1_RxRTS_CONTROL_OFF ;
mr2 | = MR2_TxRTS_CONTROL_ON | MR2_CTS_ENABLE_TX_OFF ;
break ;
default :
return ;
break ;
}
baud = tty_get_baud_rate ( tty ) ;
2012-07-26 19:00:51 +01:00
tty_termios_encode_baud_rate ( & tty - > termios , baud , baud ) ;
2012-05-09 15:27:21 +02:00
/* Set baud rate */
2012-07-26 19:00:51 +01:00
switch ( baud ) {
2012-05-09 15:27:21 +02:00
case 75 :
csr | = TX_CLK_75 | RX_CLK_75 ;
break ;
case 110 :
csr | = TX_CLK_110 | RX_CLK_110 ;
break ;
case 150 :
csr | = TX_CLK_150 | RX_CLK_150 ;
break ;
case 300 :
csr | = TX_CLK_300 | RX_CLK_300 ;
break ;
case 600 :
csr | = TX_CLK_600 | RX_CLK_600 ;
break ;
case 1200 :
csr | = TX_CLK_1200 | RX_CLK_1200 ;
break ;
case 1800 :
csr | = TX_CLK_1800 | RX_CLK_1800 ;
break ;
case 2000 :
csr | = TX_CLK_2000 | RX_CLK_2000 ;
break ;
case 2400 :
csr | = TX_CLK_2400 | RX_CLK_2400 ;
break ;
case 4800 :
csr | = TX_CLK_4800 | RX_CLK_4800 ;
break ;
case 9600 :
csr | = TX_CLK_9600 | RX_CLK_9600 ;
break ;
case 19200 :
csr | = TX_CLK_19200 | RX_CLK_19200 ;
break ;
case 38400 :
default :
csr | = TX_CLK_38400 | RX_CLK_38400 ;
/* In case of default, we establish 38400 bps */
2012-07-26 19:00:51 +01:00
tty_termios_encode_baud_rate ( & tty - > termios , 38400 , 38400 ) ;
2012-05-09 15:27:21 +02:00
break ;
}
mr1 | = MR1_ERROR_CHAR ;
mr1 | = MR1_RxINT_RxRDY ;
/* Write the control registers */
2012-09-12 14:55:27 +02:00
iowrite8 ( mr1 , & channel - > regs - > w . mr ) ;
iowrite8 ( mr2 , & channel - > regs - > w . mr ) ;
iowrite8 ( csr , & channel - > regs - > w . csr ) ;
2012-05-09 15:27:21 +02:00
2012-12-10 11:50:07 +01:00
/* Enable again the RX, if it was before */
if ( channel - > rx_enable )
iowrite8 ( CR_ENABLE_RX , & channel - > regs - > w . cr ) ;
2012-05-09 15:27:21 +02:00
}
static void ipoctal_hangup ( struct tty_struct * tty )
{
unsigned long flags ;
2012-09-12 14:55:28 +02:00
struct ipoctal_channel * channel = tty - > driver_data ;
2012-05-09 15:27:21 +02:00
2012-09-12 14:55:28 +02:00
if ( channel = = NULL )
2012-05-09 15:27:21 +02:00
return ;
2012-09-12 14:55:26 +02:00
spin_lock_irqsave ( & channel - > lock , flags ) ;
channel - > nb_bytes = 0 ;
channel - > pointer_read = 0 ;
channel - > pointer_write = 0 ;
spin_unlock_irqrestore ( & channel - > lock , flags ) ;
2012-05-09 15:27:21 +02:00
2012-09-12 14:55:26 +02:00
tty_port_hangup ( & channel - > tty_port ) ;
2012-05-09 15:27:21 +02:00
2014-09-02 17:31:39 +02:00
ipoctal_reset_channel ( channel ) ;
2016-04-09 17:53:25 -07:00
tty_port_set_initialized ( & channel - > tty_port , 0 ) ;
2012-09-12 14:55:26 +02:00
wake_up_interruptible ( & channel - > tty_port . open_wait ) ;
2012-05-09 15:27:21 +02:00
}
2012-12-10 11:50:08 +01:00
static void ipoctal_shutdown ( struct tty_struct * tty )
{
struct ipoctal_channel * channel = tty - > driver_data ;
if ( channel = = NULL )
return ;
2014-09-02 17:31:39 +02:00
ipoctal_reset_channel ( channel ) ;
2016-04-09 17:53:25 -07:00
tty_port_set_initialized ( & channel - > tty_port , 0 ) ;
2012-12-10 11:50:08 +01:00
}
2014-09-02 17:31:41 +02:00
static void ipoctal_cleanup ( struct tty_struct * tty )
{
struct ipoctal_channel * channel = tty - > driver_data ;
struct ipoctal * ipoctal = chan_to_ipoctal ( channel , tty - > index ) ;
/* release the carrier driver */
ipack_put_carrier ( ipoctal - > dev ) ;
}
2012-05-09 15:27:21 +02:00
static const struct tty_operations ipoctal_fops = {
. ioctl = NULL ,
. open = ipoctal_open ,
. close = ipoctal_close ,
. write = ipoctal_write_tty ,
. set_termios = ipoctal_set_termios ,
. write_room = ipoctal_write_room ,
. chars_in_buffer = ipoctal_chars_in_buffer ,
. get_icount = ipoctal_get_icount ,
. hangup = ipoctal_hangup ,
2012-12-10 11:50:08 +01:00
. shutdown = ipoctal_shutdown ,
2014-09-02 17:31:41 +02:00
. cleanup = ipoctal_cleanup ,
2012-05-09 15:27:21 +02:00
} ;
static int ipoctal_probe ( struct ipack_device * dev )
{
int res ;
struct ipoctal * ipoctal ;
ipoctal = kzalloc ( sizeof ( struct ipoctal ) , GFP_KERNEL ) ;
if ( ipoctal = = NULL )
return - ENOMEM ;
ipoctal - > dev = dev ;
2012-09-27 12:37:25 +02:00
res = ipoctal_inst_slot ( ipoctal , dev - > bus - > bus_nr , dev - > slot ) ;
2012-05-09 15:27:21 +02:00
if ( res )
goto out_uninst ;
2012-09-12 14:55:39 +02:00
dev_set_drvdata ( & dev - > dev , ipoctal ) ;
2012-05-09 15:27:21 +02:00
return 0 ;
out_uninst :
kfree ( ipoctal ) ;
return res ;
}
static void __ipoctal_remove ( struct ipoctal * ipoctal )
{
int i ;
2012-09-11 13:35:08 +02:00
ipoctal - > dev - > bus - > ops - > free_irq ( ipoctal - > dev ) ;
2012-05-09 15:27:21 +02:00
for ( i = 0 ; i < NR_CHANNELS ; i + + ) {
2012-09-12 14:55:26 +02:00
struct ipoctal_channel * channel = & ipoctal - > channel [ i ] ;
2012-05-09 15:27:21 +02:00
tty_unregister_device ( ipoctal - > tty_drv , i ) ;
2012-09-12 14:55:26 +02:00
tty_port_free_xmit_buf ( & channel - > tty_port ) ;
2012-11-15 09:49:56 +01:00
tty_port_destroy ( & channel - > tty_port ) ;
2012-05-09 15:27:21 +02:00
}
tty_unregister_driver ( ipoctal - > tty_drv ) ;
put_tty_driver ( ipoctal - > tty_drv ) ;
kfree ( ipoctal ) ;
}
2012-09-12 14:55:39 +02:00
static void ipoctal_remove ( struct ipack_device * idev )
2012-05-09 15:27:21 +02:00
{
2012-09-12 14:55:39 +02:00
__ipoctal_remove ( dev_get_drvdata ( & idev - > dev ) ) ;
2012-05-09 15:27:21 +02:00
}
2012-09-04 17:01:18 +02:00
static DEFINE_IPACK_DEVICE_TABLE ( ipoctal_ids ) = {
{ IPACK_DEVICE ( IPACK_ID_VERSION_1 , IPACK1_VENDOR_ID_SBS ,
IPACK1_DEVICE_ID_SBS_OCTAL_232 ) } ,
{ IPACK_DEVICE ( IPACK_ID_VERSION_1 , IPACK1_VENDOR_ID_SBS ,
IPACK1_DEVICE_ID_SBS_OCTAL_422 ) } ,
{ IPACK_DEVICE ( IPACK_ID_VERSION_1 , IPACK1_VENDOR_ID_SBS ,
IPACK1_DEVICE_ID_SBS_OCTAL_485 ) } ,
{ 0 , } ,
} ;
MODULE_DEVICE_TABLE ( ipack , ipoctal_ids ) ;
2012-09-04 17:01:17 +02:00
static const struct ipack_driver_ops ipoctal_drv_ops = {
2012-09-04 17:01:19 +02:00
. probe = ipoctal_probe ,
2012-05-09 15:27:21 +02:00
. remove = ipoctal_remove ,
} ;
2012-09-04 17:01:17 +02:00
static struct ipack_driver driver = {
. ops = & ipoctal_drv_ops ,
2012-09-04 17:01:18 +02:00
. id_table = ipoctal_ids ,
2012-09-04 17:01:17 +02:00
} ;
2012-05-09 15:27:21 +02:00
static int __init ipoctal_init ( void )
{
2012-05-18 11:10:05 +02:00
return ipack_driver_register ( & driver , THIS_MODULE , KBUILD_MODNAME ) ;
2012-05-09 15:27:21 +02:00
}
static void __exit ipoctal_exit ( void )
{
ipack_driver_unregister ( & driver ) ;
}
MODULE_DESCRIPTION ( " IP-Octal 232, 422 and 485 device driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_init ( ipoctal_init ) ;
module_exit ( ipoctal_exit ) ;