2010-10-26 17:13:52 +04:00
/****************************************************************************
*
* Driver for the IFX 6 x60 spi modem .
*
* Copyright ( C ) 2008 Option International
* Copyright ( C ) 2008 Filip Aben < f . aben @ option . com >
* Denis Joseph Barrow < d . barow @ option . com >
* Jan Dumon < j . dumon @ option . com >
*
* Copyright ( C ) 2009 , 2010 Intel Corp
2011-02-02 23:56:58 +03:00
* Russ Gorby < russ . gorby @ intel . com >
2010-10-26 17:13:52 +04:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 ,
* USA
*
* Driver modified by Intel from Option gtm501l_spi . c
*
* Notes
* o The driver currently assumes a single device only . If you need to
* change this then look for saved_ifx_dev and add a device lookup
* o The driver is intended to be big - endian safe but has never been
* tested that way ( no suitable hardware ) . There are a couple of FIXME
* notes by areas that may need addressing
* o Some of the GPIO naming / setup assumptions may need revisiting if
* you need to use this driver for another platform .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2011-06-16 15:01:34 +04:00
# include <linux/dma-mapping.h>
2010-10-26 17:13:52 +04:00
# include <linux/module.h>
# include <linux/termios.h>
# include <linux/tty.h>
# include <linux/device.h>
# include <linux/spi/spi.h>
# include <linux/kfifo.h>
# include <linux/tty_flip.h>
# include <linux/timer.h>
# include <linux/serial.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/rfkill.h>
# include <linux/fs.h>
# include <linux/ip.h>
# include <linux/dmapool.h>
# include <linux/gpio.h>
# include <linux/sched.h>
# include <linux/time.h>
# include <linux/wait.h>
# include <linux/pm.h>
# include <linux/pm_runtime.h>
# include <linux/spi/ifx_modem.h>
2010-11-12 13:46:23 +03:00
# include <linux/delay.h>
2012-11-23 15:07:27 +04:00
# include <linux/reboot.h>
2010-10-26 17:13:52 +04:00
# include "ifx6x60.h"
# define IFX_SPI_MORE_MASK 0x10
2012-11-06 07:13:59 +04:00
# define IFX_SPI_MORE_BIT 4 /* bit position in u8 */
# define IFX_SPI_CTS_BIT 6 /* bit position in u8 */
2011-02-07 23:02:31 +03:00
# define IFX_SPI_MODE SPI_MODE_1
2010-10-26 17:13:52 +04:00
# define IFX_SPI_TTY_ID 0
# define IFX_SPI_TIMEOUT_SEC 2
# define IFX_SPI_HEADER_0 (-1)
# define IFX_SPI_HEADER_F (-2)
2012-11-23 15:07:27 +04:00
# define PO_POST_DELAY 200
# define IFX_MDM_RST_PMU 4
2010-10-26 17:13:52 +04:00
/* forward reference */
static void ifx_spi_handle_srdy ( struct ifx_spi_device * ifx_dev ) ;
2012-11-23 15:07:27 +04:00
static int ifx_modem_reboot_callback ( struct notifier_block * nfb ,
unsigned long event , void * data ) ;
static int ifx_modem_power_off ( struct ifx_spi_device * ifx_dev ) ;
2010-10-26 17:13:52 +04:00
/* local variables */
2011-02-07 23:02:29 +03:00
static int spi_bpw = 16 ; /* 8, 16 or 32 bit word length */
2010-10-26 17:13:52 +04:00
static struct tty_driver * tty_drv ;
static struct ifx_spi_device * saved_ifx_dev ;
static struct lock_class_key ifx_spi_key ;
2012-11-23 15:07:27 +04:00
static struct notifier_block ifx_modem_reboot_notifier_block = {
. notifier_call = ifx_modem_reboot_callback ,
} ;
static int ifx_modem_power_off ( struct ifx_spi_device * ifx_dev )
{
gpio_set_value ( IFX_MDM_RST_PMU , 1 ) ;
msleep ( PO_POST_DELAY ) ;
return 0 ;
}
static int ifx_modem_reboot_callback ( struct notifier_block * nfb ,
unsigned long event , void * data )
{
if ( saved_ifx_dev )
ifx_modem_power_off ( saved_ifx_dev ) ;
else
pr_warn ( " no ifx modem active; \n " ) ;
return NOTIFY_OK ;
}
2010-10-26 17:13:52 +04:00
/* GPIO/GPE settings */
/**
* mrdy_set_high - set MRDY GPIO
* @ ifx : device we are controlling
*
*/
static inline void mrdy_set_high ( struct ifx_spi_device * ifx )
{
gpio_set_value ( ifx - > gpio . mrdy , 1 ) ;
}
/**
* mrdy_set_low - clear MRDY GPIO
* @ ifx : device we are controlling
*
*/
static inline void mrdy_set_low ( struct ifx_spi_device * ifx )
{
gpio_set_value ( ifx - > gpio . mrdy , 0 ) ;
}
/**
* ifx_spi_power_state_set
* @ ifx_dev : our SPI device
* @ val : bits to set
*
* Set bit in power status and signal power system if status becomes non - 0
*/
static void
ifx_spi_power_state_set ( struct ifx_spi_device * ifx_dev , unsigned char val )
{
unsigned long flags ;
spin_lock_irqsave ( & ifx_dev - > power_lock , flags ) ;
/*
* if power status is already non - 0 , just update , else
* tell power system
*/
if ( ! ifx_dev - > power_status )
pm_runtime_get ( & ifx_dev - > spi_dev - > dev ) ;
ifx_dev - > power_status | = val ;
spin_unlock_irqrestore ( & ifx_dev - > power_lock , flags ) ;
}
/**
* ifx_spi_power_state_clear - clear power bit
* @ ifx_dev : our SPI device
* @ val : bits to clear
*
* clear bit in power status and signal power system if status becomes 0
*/
static void
ifx_spi_power_state_clear ( struct ifx_spi_device * ifx_dev , unsigned char val )
{
unsigned long flags ;
spin_lock_irqsave ( & ifx_dev - > power_lock , flags ) ;
if ( ifx_dev - > power_status ) {
ifx_dev - > power_status & = ~ val ;
if ( ! ifx_dev - > power_status )
pm_runtime_put ( & ifx_dev - > spi_dev - > dev ) ;
}
spin_unlock_irqrestore ( & ifx_dev - > power_lock , flags ) ;
}
/**
2012-10-25 05:02:32 +04:00
* swap_buf_8
2010-10-26 17:13:52 +04:00
* @ buf : our buffer
* @ len : number of bytes ( not words ) in the buffer
* @ end : end of buffer
*
* Swap the contents of a buffer into big endian format
*/
2012-10-25 05:02:32 +04:00
static inline void swap_buf_8 ( unsigned char * buf , int len , void * end )
{
/* don't swap buffer if SPI word width is 8 bits */
return ;
}
/**
* swap_buf_16
* @ buf : our buffer
* @ len : number of bytes ( not words ) in the buffer
* @ end : end of buffer
*
* Swap the contents of a buffer into big endian format
*/
static inline void swap_buf_16 ( unsigned char * buf , int len , void * end )
2010-10-26 17:13:52 +04:00
{
int n ;
2012-10-25 05:02:32 +04:00
u16 * buf_16 = ( u16 * ) buf ;
2010-10-26 17:13:52 +04:00
len = ( ( len + 1 ) > > 1 ) ;
2012-10-25 05:02:32 +04:00
if ( ( void * ) & buf_16 [ len ] > end ) {
pr_err ( " swap_buf_16: swap exceeds boundary (%p > %p)! " ,
& buf_16 [ len ] , end ) ;
2010-10-26 17:13:52 +04:00
return ;
}
for ( n = 0 ; n < len ; n + + ) {
2012-10-25 05:02:32 +04:00
* buf_16 = cpu_to_be16 ( * buf_16 ) ;
buf_16 + + ;
}
}
/**
* swap_buf_32
* @ buf : our buffer
* @ len : number of bytes ( not words ) in the buffer
* @ end : end of buffer
*
* Swap the contents of a buffer into big endian format
*/
static inline void swap_buf_32 ( unsigned char * buf , int len , void * end )
{
int n ;
u32 * buf_32 = ( u32 * ) buf ;
len = ( len + 3 ) > > 2 ;
if ( ( void * ) & buf_32 [ len ] > end ) {
pr_err ( " swap_buf_32: swap exceeds boundary (%p > %p)! \n " ,
& buf_32 [ len ] , end ) ;
return ;
}
for ( n = 0 ; n < len ; n + + ) {
* buf_32 = cpu_to_be32 ( * buf_32 ) ;
buf_32 + + ;
2010-10-26 17:13:52 +04:00
}
}
/**
* mrdy_assert - assert MRDY line
* @ ifx_dev : our SPI device
*
* Assert mrdy and set timer to wait for SRDY interrupt , if SRDY is low
* now .
*
* FIXME : Can SRDY even go high as we are running this code ?
*/
static void mrdy_assert ( struct ifx_spi_device * ifx_dev )
{
int val = gpio_get_value ( ifx_dev - > gpio . srdy ) ;
if ( ! val ) {
if ( ! test_and_set_bit ( IFX_SPI_STATE_TIMER_PENDING ,
& ifx_dev - > flags ) ) {
2012-10-22 18:23:07 +04:00
mod_timer ( & ifx_dev - > spi_timer , jiffies + IFX_SPI_TIMEOUT_SEC * HZ ) ;
2010-10-26 17:13:52 +04:00
}
}
ifx_spi_power_state_set ( ifx_dev , IFX_SPI_POWER_DATA_PENDING ) ;
mrdy_set_high ( ifx_dev ) ;
}
/**
* ifx_spi_timeout - SPI timeout
* @ arg : our SPI device
*
* The SPI has timed out : hang up the tty . Users will then see a hangup
* and error events .
*/
static void ifx_spi_timeout ( unsigned long arg )
{
struct ifx_spi_device * ifx_dev = ( struct ifx_spi_device * ) arg ;
dev_warn ( & ifx_dev - > spi_dev - > dev , " *** SPI Timeout *** " ) ;
2013-03-07 16:12:30 +04:00
tty_port_tty_hangup ( & ifx_dev - > tty_port , false ) ;
2010-10-26 17:13:52 +04:00
mrdy_set_low ( ifx_dev ) ;
clear_bit ( IFX_SPI_STATE_TIMER_PENDING , & ifx_dev - > flags ) ;
}
/* char/tty operations */
/**
* ifx_spi_tiocmget - get modem lines
* @ tty : our tty device
* @ filp : file handle issuing the request
*
* Map the signal state into Linux modem flags and report the value
* in Linux terms
*/
2011-02-14 19:26:14 +03:00
static int ifx_spi_tiocmget ( struct tty_struct * tty )
2010-10-26 17:13:52 +04:00
{
unsigned int value ;
struct ifx_spi_device * ifx_dev = tty - > driver_data ;
value =
( test_bit ( IFX_SPI_RTS , & ifx_dev - > signal_state ) ? TIOCM_RTS : 0 ) |
( test_bit ( IFX_SPI_DTR , & ifx_dev - > signal_state ) ? TIOCM_DTR : 0 ) |
( test_bit ( IFX_SPI_CTS , & ifx_dev - > signal_state ) ? TIOCM_CTS : 0 ) |
( test_bit ( IFX_SPI_DSR , & ifx_dev - > signal_state ) ? TIOCM_DSR : 0 ) |
( test_bit ( IFX_SPI_DCD , & ifx_dev - > signal_state ) ? TIOCM_CAR : 0 ) |
( test_bit ( IFX_SPI_RI , & ifx_dev - > signal_state ) ? TIOCM_RNG : 0 ) ;
return value ;
}
/**
* ifx_spi_tiocmset - set modem bits
* @ tty : the tty structure
* @ set : bits to set
* @ clear : bits to clear
*
* The IFX6x60 only supports DTR and RTS . Set them accordingly
* and flag that an update to the modem is needed .
*
* FIXME : do we need to kick the tranfers when we do this ?
*/
2011-02-14 19:26:50 +03:00
static int ifx_spi_tiocmset ( struct tty_struct * tty ,
2010-10-26 17:13:52 +04:00
unsigned int set , unsigned int clear )
{
struct ifx_spi_device * ifx_dev = tty - > driver_data ;
if ( set & TIOCM_RTS )
set_bit ( IFX_SPI_RTS , & ifx_dev - > signal_state ) ;
if ( set & TIOCM_DTR )
set_bit ( IFX_SPI_DTR , & ifx_dev - > signal_state ) ;
if ( clear & TIOCM_RTS )
clear_bit ( IFX_SPI_RTS , & ifx_dev - > signal_state ) ;
if ( clear & TIOCM_DTR )
clear_bit ( IFX_SPI_DTR , & ifx_dev - > signal_state ) ;
set_bit ( IFX_SPI_UPDATE , & ifx_dev - > signal_state ) ;
return 0 ;
}
/**
* ifx_spi_open - called on tty open
* @ tty : our tty device
* @ filp : file handle being associated with the tty
*
* Open the tty interface . We let the tty_port layer do all the work
* for us .
*
* FIXME : Remove single device assumption and saved_ifx_dev
*/
static int ifx_spi_open ( struct tty_struct * tty , struct file * filp )
{
return tty_port_open ( & saved_ifx_dev - > tty_port , tty , filp ) ;
}
/**
* ifx_spi_close - called when our tty closes
* @ tty : the tty being closed
* @ filp : the file handle being closed
*
* Perform the close of the tty . We use the tty_port layer to do all
* our hard work .
*/
static void ifx_spi_close ( struct tty_struct * tty , struct file * filp )
{
struct ifx_spi_device * ifx_dev = tty - > driver_data ;
tty_port_close ( & ifx_dev - > tty_port , tty , filp ) ;
/* FIXME: should we do an ifx_spi_reset here ? */
}
/**
* ifx_decode_spi_header - decode received header
* @ buffer : the received data
* @ length : decoded length
* @ more : decoded more flag
* @ received_cts : status of cts we received
*
* Note how received_cts is handled - - if header is all F it is left
* the same as it was , if header is all 0 it is set to 0 otherwise it is
* taken from the incoming header .
*
* FIXME : endianness
*/
static int ifx_spi_decode_spi_header ( unsigned char * buffer , int * length ,
unsigned char * more , unsigned char * received_cts )
{
u16 h1 ;
u16 h2 ;
u16 * in_buffer = ( u16 * ) buffer ;
h1 = * in_buffer ;
h2 = * ( in_buffer + 1 ) ;
if ( h1 = = 0 & & h2 = = 0 ) {
* received_cts = 0 ;
return IFX_SPI_HEADER_0 ;
} else if ( h1 = = 0xffff & & h2 = = 0xffff ) {
/* spi_slave_cts remains as it was */
return IFX_SPI_HEADER_F ;
}
* length = h1 & 0xfff ; /* upper bits of byte are flags */
* more = ( buffer [ 1 ] > > IFX_SPI_MORE_BIT ) & 1 ;
* received_cts = ( buffer [ 3 ] > > IFX_SPI_CTS_BIT ) & 1 ;
return 0 ;
}
/**
* ifx_setup_spi_header - set header fields
* @ txbuffer : pointer to start of SPI buffer
* @ tx_count : bytes
* @ more : indicate if more to follow
*
* Format up an SPI header for a transfer
*
* FIXME : endianness ?
*/
static void ifx_spi_setup_spi_header ( unsigned char * txbuffer , int tx_count ,
unsigned char more )
{
* ( u16 * ) ( txbuffer ) = tx_count ;
* ( u16 * ) ( txbuffer + 2 ) = IFX_SPI_PAYLOAD_SIZE ;
txbuffer [ 1 ] | = ( more < < IFX_SPI_MORE_BIT ) & IFX_SPI_MORE_MASK ;
}
/**
* ifx_spi_prepare_tx_buffer - prepare transmit frame
* @ ifx_dev : our SPI device
*
* The transmit buffr needs a header and various other bits of
* information followed by as much data as we can pull from the FIFO
* and transfer . This function formats up a suitable buffer in the
* ifx_dev - > tx_buffer
*
* FIXME : performance - should we wake the tty when the queue is half
* empty ?
*/
static int ifx_spi_prepare_tx_buffer ( struct ifx_spi_device * ifx_dev )
{
int temp_count ;
int queue_length ;
int tx_count ;
unsigned char * tx_buffer ;
tx_buffer = ifx_dev - > tx_buffer ;
/* make room for required SPI header */
tx_buffer + = IFX_SPI_HEADER_OVERHEAD ;
tx_count = IFX_SPI_HEADER_OVERHEAD ;
/* clear to signal no more data if this turns out to be the
* last buffer sent in a sequence */
ifx_dev - > spi_more = 0 ;
/* if modem cts is set, just send empty buffer */
if ( ! ifx_dev - > spi_slave_cts ) {
/* see if there's tx data */
queue_length = kfifo_len ( & ifx_dev - > tx_fifo ) ;
if ( queue_length ! = 0 ) {
/* data to mux -- see if there's room for it */
temp_count = min ( queue_length , IFX_SPI_PAYLOAD_SIZE ) ;
temp_count = kfifo_out_locked ( & ifx_dev - > tx_fifo ,
tx_buffer , temp_count ,
& ifx_dev - > fifo_lock ) ;
/* update buffer pointer and data count in message */
tx_buffer + = temp_count ;
tx_count + = temp_count ;
if ( temp_count = = queue_length )
/* poke port to get more data */
2013-03-07 16:12:29 +04:00
tty_port_tty_wakeup ( & ifx_dev - > tty_port ) ;
2010-10-26 17:13:52 +04:00
else /* more data in port, use next SPI message */
ifx_dev - > spi_more = 1 ;
}
}
/* have data and info for header -- set up SPI header in buffer */
/* spi header needs payload size, not entire buffer size */
ifx_spi_setup_spi_header ( ifx_dev - > tx_buffer ,
tx_count - IFX_SPI_HEADER_OVERHEAD ,
ifx_dev - > spi_more ) ;
/* swap actual data in the buffer */
2012-10-25 05:02:32 +04:00
ifx_dev - > swap_buf ( ( ifx_dev - > tx_buffer ) , tx_count ,
2010-10-26 17:13:52 +04:00
& ifx_dev - > tx_buffer [ IFX_SPI_TRANSFER_SIZE ] ) ;
return tx_count ;
}
/**
* ifx_spi_write - line discipline write
* @ tty : our tty device
* @ buf : pointer to buffer to write ( kernel space )
* @ count : size of buffer
*
* Write the characters we have been given into the FIFO . If the device
* is not active then activate it , when the SRDY line is asserted back
* this will commence I / O
*/
static int ifx_spi_write ( struct tty_struct * tty , const unsigned char * buf ,
int count )
{
struct ifx_spi_device * ifx_dev = tty - > driver_data ;
unsigned char * tmp_buf = ( unsigned char * ) buf ;
2012-11-07 21:04:04 +04:00
unsigned long flags ;
bool is_fifo_empty ;
2012-11-26 23:07:25 +04:00
int tx_count ;
2012-11-07 21:04:04 +04:00
spin_lock_irqsave ( & ifx_dev - > fifo_lock , flags ) ;
is_fifo_empty = kfifo_is_empty ( & ifx_dev - > tx_fifo ) ;
2012-11-26 23:07:25 +04:00
tx_count = kfifo_in ( & ifx_dev - > tx_fifo , tmp_buf , count ) ;
2012-11-07 21:04:04 +04:00
spin_unlock_irqrestore ( & ifx_dev - > fifo_lock , flags ) ;
if ( is_fifo_empty )
mrdy_assert ( ifx_dev ) ;
2010-10-26 17:13:52 +04:00
return tx_count ;
}
/**
* ifx_spi_chars_in_buffer - line discipline helper
* @ tty : our tty device
*
* Report how much data we can accept before we drop bytes . As we use
* a simple FIFO this is nice and easy .
*/
static int ifx_spi_write_room ( struct tty_struct * tty )
{
struct ifx_spi_device * ifx_dev = tty - > driver_data ;
return IFX_SPI_FIFO_SIZE - kfifo_len ( & ifx_dev - > tx_fifo ) ;
}
/**
* ifx_spi_chars_in_buffer - line discipline helper
* @ tty : our tty device
*
* Report how many characters we have buffered . In our case this is the
* number of bytes sitting in our transmit FIFO .
*/
static int ifx_spi_chars_in_buffer ( struct tty_struct * tty )
{
struct ifx_spi_device * ifx_dev = tty - > driver_data ;
return kfifo_len ( & ifx_dev - > tx_fifo ) ;
}
/**
* ifx_port_hangup
* @ port : our tty port
*
* tty port hang up . Called when tty_hangup processing is invoked either
* by loss of carrier , or by software ( eg vhangup ) . Serialized against
* activate / shutdown by the tty layer .
*/
static void ifx_spi_hangup ( struct tty_struct * tty )
{
struct ifx_spi_device * ifx_dev = tty - > driver_data ;
tty_port_hangup ( & ifx_dev - > tty_port ) ;
}
/**
* ifx_port_activate
* @ port : our tty port
*
* tty port activate method - called for first open . Serialized
* with hangup and shutdown by the tty layer .
*/
static int ifx_port_activate ( struct tty_port * port , struct tty_struct * tty )
{
struct ifx_spi_device * ifx_dev =
container_of ( port , struct ifx_spi_device , tty_port ) ;
/* clear any old data; can't do this in 'close' */
kfifo_reset ( & ifx_dev - > tx_fifo ) ;
2012-10-31 12:54:07 +04:00
/* clear any flag which may be set in port shutdown procedure */
clear_bit ( IFX_SPI_STATE_IO_IN_PROGRESS , & ifx_dev - > flags ) ;
clear_bit ( IFX_SPI_STATE_IO_READY , & ifx_dev - > flags ) ;
2010-10-26 17:13:52 +04:00
/* put port data into this tty */
tty - > driver_data = ifx_dev ;
/* allows flip string push from int context */
2013-01-03 18:53:05 +04:00
port - > low_latency = 1 ;
2010-10-26 17:13:52 +04:00
2012-10-31 12:54:07 +04:00
/* set flag to allows data transfer */
set_bit ( IFX_SPI_STATE_IO_AVAILABLE , & ifx_dev - > flags ) ;
2010-10-26 17:13:52 +04:00
return 0 ;
}
/**
* ifx_port_shutdown
* @ port : our tty port
*
* tty port shutdown method - called for last port close . Serialized
* with hangup and activate by the tty layer .
*/
static void ifx_port_shutdown ( struct tty_port * port )
{
struct ifx_spi_device * ifx_dev =
container_of ( port , struct ifx_spi_device , tty_port ) ;
2012-10-31 12:54:07 +04:00
clear_bit ( IFX_SPI_STATE_IO_AVAILABLE , & ifx_dev - > flags ) ;
2010-10-26 17:13:52 +04:00
mrdy_set_low ( ifx_dev ) ;
2012-12-12 07:40:56 +04:00
del_timer ( & ifx_dev - > spi_timer ) ;
2010-10-26 17:13:52 +04:00
clear_bit ( IFX_SPI_STATE_TIMER_PENDING , & ifx_dev - > flags ) ;
tasklet_kill ( & ifx_dev - > io_work_tasklet ) ;
}
static const struct tty_port_operations ifx_tty_port_ops = {
. activate = ifx_port_activate ,
. shutdown = ifx_port_shutdown ,
} ;
static const struct tty_operations ifx_spi_serial_ops = {
. open = ifx_spi_open ,
. close = ifx_spi_close ,
. write = ifx_spi_write ,
. hangup = ifx_spi_hangup ,
. write_room = ifx_spi_write_room ,
. chars_in_buffer = ifx_spi_chars_in_buffer ,
. tiocmget = ifx_spi_tiocmget ,
. tiocmset = ifx_spi_tiocmset ,
} ;
/**
* ifx_spi_insert_fip_string - queue received data
* @ ifx_ser : our SPI device
* @ chars : buffer we have received
* @ size : number of chars reeived
*
* Queue bytes to the tty assuming the tty side is currently open . If
* not the discard the data .
*/
static void ifx_spi_insert_flip_string ( struct ifx_spi_device * ifx_dev ,
unsigned char * chars , size_t size )
{
2013-01-03 18:53:04 +04:00
tty_insert_flip_string ( & ifx_dev - > tty_port , chars , size ) ;
2013-01-03 18:53:06 +04:00
tty_flip_buffer_push ( & ifx_dev - > tty_port ) ;
2010-10-26 17:13:52 +04:00
}
/**
* ifx_spi_complete - SPI transfer completed
* @ ctx : our SPI device
*
* An SPI transfer has completed . Process any received data and kick off
* any further transmits we can commence .
*/
static void ifx_spi_complete ( void * ctx )
{
struct ifx_spi_device * ifx_dev = ctx ;
int length ;
int actual_length ;
unsigned char more ;
unsigned char cts ;
int local_write_pending = 0 ;
int queue_length ;
int srdy ;
int decode_result ;
mrdy_set_low ( ifx_dev ) ;
if ( ! ifx_dev - > spi_msg . status ) {
/* check header validity, get comm flags */
2012-10-25 05:02:32 +04:00
ifx_dev - > swap_buf ( ifx_dev - > rx_buffer , IFX_SPI_HEADER_OVERHEAD ,
2010-10-26 17:13:52 +04:00
& ifx_dev - > rx_buffer [ IFX_SPI_HEADER_OVERHEAD ] ) ;
decode_result = ifx_spi_decode_spi_header ( ifx_dev - > rx_buffer ,
& length , & more , & cts ) ;
if ( decode_result = = IFX_SPI_HEADER_0 ) {
dev_dbg ( & ifx_dev - > spi_dev - > dev ,
" ignore input: invalid header 0 " ) ;
ifx_dev - > spi_slave_cts = 0 ;
goto complete_exit ;
} else if ( decode_result = = IFX_SPI_HEADER_F ) {
dev_dbg ( & ifx_dev - > spi_dev - > dev ,
" ignore input: invalid header F " ) ;
goto complete_exit ;
}
ifx_dev - > spi_slave_cts = cts ;
actual_length = min ( ( unsigned int ) length ,
ifx_dev - > spi_msg . actual_length ) ;
2012-10-25 05:02:32 +04:00
ifx_dev - > swap_buf (
( ifx_dev - > rx_buffer + IFX_SPI_HEADER_OVERHEAD ) ,
2010-10-26 17:13:52 +04:00
actual_length ,
& ifx_dev - > rx_buffer [ IFX_SPI_TRANSFER_SIZE ] ) ;
ifx_spi_insert_flip_string (
ifx_dev ,
ifx_dev - > rx_buffer + IFX_SPI_HEADER_OVERHEAD ,
( size_t ) actual_length ) ;
} else {
dev_dbg ( & ifx_dev - > spi_dev - > dev , " SPI transfer error %d " ,
ifx_dev - > spi_msg . status ) ;
}
complete_exit :
if ( ifx_dev - > write_pending ) {
ifx_dev - > write_pending = 0 ;
local_write_pending = 1 ;
}
clear_bit ( IFX_SPI_STATE_IO_IN_PROGRESS , & ( ifx_dev - > flags ) ) ;
queue_length = kfifo_len ( & ifx_dev - > tx_fifo ) ;
srdy = gpio_get_value ( ifx_dev - > gpio . srdy ) ;
if ( ! srdy )
ifx_spi_power_state_clear ( ifx_dev , IFX_SPI_POWER_SRDY ) ;
/* schedule output if there is more to do */
if ( test_and_clear_bit ( IFX_SPI_STATE_IO_READY , & ifx_dev - > flags ) )
tasklet_schedule ( & ifx_dev - > io_work_tasklet ) ;
else {
if ( more | | ifx_dev - > spi_more | | queue_length > 0 | |
local_write_pending ) {
if ( ifx_dev - > spi_slave_cts ) {
if ( more )
mrdy_assert ( ifx_dev ) ;
} else
mrdy_assert ( ifx_dev ) ;
} else {
/*
* poke line discipline driver if any for more data
* may or may not get more data to write
* for now , say not busy
*/
ifx_spi_power_state_clear ( ifx_dev ,
IFX_SPI_POWER_DATA_PENDING ) ;
2013-03-07 16:12:29 +04:00
tty_port_tty_wakeup ( & ifx_dev - > tty_port ) ;
2010-10-26 17:13:52 +04:00
}
}
}
/**
* ifx_spio_io - I / O tasklet
* @ data : our SPI device
*
* Queue data for transmission if possible and then kick off the
* transfer .
*/
static void ifx_spi_io ( unsigned long data )
{
int retval ;
struct ifx_spi_device * ifx_dev = ( struct ifx_spi_device * ) data ;
2012-10-31 12:54:07 +04:00
if ( ! test_and_set_bit ( IFX_SPI_STATE_IO_IN_PROGRESS , & ifx_dev - > flags ) & &
test_bit ( IFX_SPI_STATE_IO_AVAILABLE , & ifx_dev - > flags ) ) {
2010-10-26 17:13:52 +04:00
if ( ifx_dev - > gpio . unack_srdy_int_nb > 0 )
ifx_dev - > gpio . unack_srdy_int_nb - - ;
ifx_spi_prepare_tx_buffer ( ifx_dev ) ;
spi_message_init ( & ifx_dev - > spi_msg ) ;
INIT_LIST_HEAD ( & ifx_dev - > spi_msg . queue ) ;
ifx_dev - > spi_msg . context = ifx_dev ;
ifx_dev - > spi_msg . complete = ifx_spi_complete ;
/* set up our spi transfer */
/* note len is BYTES, not transfers */
ifx_dev - > spi_xfer . len = IFX_SPI_TRANSFER_SIZE ;
ifx_dev - > spi_xfer . cs_change = 0 ;
2011-02-07 23:02:30 +03:00
ifx_dev - > spi_xfer . speed_hz = ifx_dev - > spi_dev - > max_speed_hz ;
2010-10-26 17:13:52 +04:00
/* ifx_dev->spi_xfer.speed_hz = 390625; */
2013-01-16 09:14:20 +04:00
ifx_dev - > spi_xfer . bits_per_word =
ifx_dev - > spi_dev - > bits_per_word ;
2010-10-26 17:13:52 +04:00
ifx_dev - > spi_xfer . tx_buf = ifx_dev - > tx_buffer ;
ifx_dev - > spi_xfer . rx_buf = ifx_dev - > rx_buffer ;
/*
* setup dma pointers
*/
2011-02-02 23:56:58 +03:00
if ( ifx_dev - > use_dma ) {
2010-10-26 17:13:52 +04:00
ifx_dev - > spi_msg . is_dma_mapped = 1 ;
ifx_dev - > tx_dma = ifx_dev - > tx_bus ;
ifx_dev - > rx_dma = ifx_dev - > rx_bus ;
ifx_dev - > spi_xfer . tx_dma = ifx_dev - > tx_dma ;
ifx_dev - > spi_xfer . rx_dma = ifx_dev - > rx_dma ;
} else {
ifx_dev - > spi_msg . is_dma_mapped = 0 ;
ifx_dev - > tx_dma = ( dma_addr_t ) 0 ;
ifx_dev - > rx_dma = ( dma_addr_t ) 0 ;
ifx_dev - > spi_xfer . tx_dma = ( dma_addr_t ) 0 ;
ifx_dev - > spi_xfer . rx_dma = ( dma_addr_t ) 0 ;
}
spi_message_add_tail ( & ifx_dev - > spi_xfer , & ifx_dev - > spi_msg ) ;
/* Assert MRDY. This may have already been done by the write
* routine .
*/
mrdy_assert ( ifx_dev ) ;
retval = spi_async ( ifx_dev - > spi_dev , & ifx_dev - > spi_msg ) ;
if ( retval ) {
clear_bit ( IFX_SPI_STATE_IO_IN_PROGRESS ,
& ifx_dev - > flags ) ;
tasklet_schedule ( & ifx_dev - > io_work_tasklet ) ;
return ;
}
} else
ifx_dev - > write_pending = 1 ;
}
/**
* ifx_spi_free_port - free up the tty side
* @ ifx_dev : IFX device going away
*
* Unregister and free up a port when the device goes away
*/
static void ifx_spi_free_port ( struct ifx_spi_device * ifx_dev )
{
if ( ifx_dev - > tty_dev )
tty_unregister_device ( tty_drv , ifx_dev - > minor ) ;
2012-11-15 12:49:56 +04:00
tty_port_destroy ( & ifx_dev - > tty_port ) ;
2010-10-26 17:13:52 +04:00
kfifo_free ( & ifx_dev - > tx_fifo ) ;
}
/**
* ifx_spi_create_port - create a new port
* @ ifx_dev : our spi device
*
* Allocate and initialise the tty port that goes with this interface
* and add it to the tty layer so that it can be opened .
*/
static int ifx_spi_create_port ( struct ifx_spi_device * ifx_dev )
{
int ret = 0 ;
struct tty_port * pport = & ifx_dev - > tty_port ;
spin_lock_init ( & ifx_dev - > fifo_lock ) ;
lockdep_set_class_and_subclass ( & ifx_dev - > fifo_lock ,
& ifx_spi_key , 0 ) ;
if ( kfifo_alloc ( & ifx_dev - > tx_fifo , IFX_SPI_FIFO_SIZE , GFP_KERNEL ) ) {
ret = - ENOMEM ;
goto error_ret ;
}
tty_port_init ( pport ) ;
2011-02-07 23:02:27 +03:00
pport - > ops = & ifx_tty_port_ops ;
2010-10-26 17:13:52 +04:00
ifx_dev - > minor = IFX_SPI_TTY_ID ;
2012-08-07 23:47:47 +04:00
ifx_dev - > tty_dev = tty_port_register_device ( pport , tty_drv ,
ifx_dev - > minor , & ifx_dev - > spi_dev - > dev ) ;
2010-10-26 17:13:52 +04:00
if ( IS_ERR ( ifx_dev - > tty_dev ) ) {
dev_dbg ( & ifx_dev - > spi_dev - > dev ,
" %s: registering tty device failed " , __func__ ) ;
ret = PTR_ERR ( ifx_dev - > tty_dev ) ;
2012-11-15 12:49:56 +04:00
goto error_port ;
2010-10-26 17:13:52 +04:00
}
return 0 ;
2012-11-15 12:49:56 +04:00
error_port :
tty_port_destroy ( pport ) ;
2010-10-26 17:13:52 +04:00
error_ret :
ifx_spi_free_port ( ifx_dev ) ;
return ret ;
}
/**
* ifx_spi_handle_srdy - handle SRDY
* @ ifx_dev : device asserting SRDY
*
* Check our device state and see what we need to kick off when SRDY
* is asserted . This usually means killing the timer and firing off the
* I / O processing .
*/
static void ifx_spi_handle_srdy ( struct ifx_spi_device * ifx_dev )
{
if ( test_bit ( IFX_SPI_STATE_TIMER_PENDING , & ifx_dev - > flags ) ) {
2012-10-19 17:51:30 +04:00
del_timer ( & ifx_dev - > spi_timer ) ;
2010-10-26 17:13:52 +04:00
clear_bit ( IFX_SPI_STATE_TIMER_PENDING , & ifx_dev - > flags ) ;
}
ifx_spi_power_state_set ( ifx_dev , IFX_SPI_POWER_SRDY ) ;
if ( ! test_bit ( IFX_SPI_STATE_IO_IN_PROGRESS , & ifx_dev - > flags ) )
tasklet_schedule ( & ifx_dev - > io_work_tasklet ) ;
else
set_bit ( IFX_SPI_STATE_IO_READY , & ifx_dev - > flags ) ;
}
/**
* ifx_spi_srdy_interrupt - SRDY asserted
* @ irq : our IRQ number
* @ dev : our ifx device
*
* The modem asserted SRDY . Handle the srdy event
*/
static irqreturn_t ifx_spi_srdy_interrupt ( int irq , void * dev )
{
struct ifx_spi_device * ifx_dev = dev ;
ifx_dev - > gpio . unack_srdy_int_nb + + ;
ifx_spi_handle_srdy ( ifx_dev ) ;
return IRQ_HANDLED ;
}
/**
* ifx_spi_reset_interrupt - Modem has changed reset state
* @ irq : interrupt number
* @ dev : our device pointer
*
* The modem has either entered or left reset state . Check the GPIO
* line to see which .
*
* FIXME : review locking on MR_INPROGRESS versus
* parallel unsolicited reset / solicited reset
*/
static irqreturn_t ifx_spi_reset_interrupt ( int irq , void * dev )
{
struct ifx_spi_device * ifx_dev = dev ;
int val = gpio_get_value ( ifx_dev - > gpio . reset_out ) ;
int solreset = test_bit ( MR_START , & ifx_dev - > mdm_reset_state ) ;
if ( val = = 0 ) {
/* entered reset */
set_bit ( MR_INPROGRESS , & ifx_dev - > mdm_reset_state ) ;
if ( ! solreset ) {
/* unsolicited reset */
2013-03-07 16:12:30 +04:00
tty_port_tty_hangup ( & ifx_dev - > tty_port , false ) ;
2010-10-26 17:13:52 +04:00
}
} else {
/* exited reset */
clear_bit ( MR_INPROGRESS , & ifx_dev - > mdm_reset_state ) ;
if ( solreset ) {
set_bit ( MR_COMPLETE , & ifx_dev - > mdm_reset_state ) ;
wake_up ( & ifx_dev - > mdm_reset_wait ) ;
}
}
return IRQ_HANDLED ;
}
/**
* ifx_spi_free_device - free device
* @ ifx_dev : device to free
*
* Free the IFX device
*/
static void ifx_spi_free_device ( struct ifx_spi_device * ifx_dev )
{
ifx_spi_free_port ( ifx_dev ) ;
dma_free_coherent ( & ifx_dev - > spi_dev - > dev ,
IFX_SPI_TRANSFER_SIZE ,
ifx_dev - > tx_buffer ,
ifx_dev - > tx_bus ) ;
dma_free_coherent ( & ifx_dev - > spi_dev - > dev ,
IFX_SPI_TRANSFER_SIZE ,
ifx_dev - > rx_buffer ,
ifx_dev - > rx_bus ) ;
}
/**
* ifx_spi_reset - reset modem
* @ ifx_dev : modem to reset
*
* Perform a reset on the modem
*/
static int ifx_spi_reset ( struct ifx_spi_device * ifx_dev )
{
int ret ;
/*
* set up modem power , reset
*
* delays are required on some platforms for the modem
* to reset properly
*/
set_bit ( MR_START , & ifx_dev - > mdm_reset_state ) ;
gpio_set_value ( ifx_dev - > gpio . po , 0 ) ;
gpio_set_value ( ifx_dev - > gpio . reset , 0 ) ;
msleep ( 25 ) ;
gpio_set_value ( ifx_dev - > gpio . reset , 1 ) ;
msleep ( 1 ) ;
gpio_set_value ( ifx_dev - > gpio . po , 1 ) ;
msleep ( 1 ) ;
gpio_set_value ( ifx_dev - > gpio . po , 0 ) ;
ret = wait_event_timeout ( ifx_dev - > mdm_reset_wait ,
test_bit ( MR_COMPLETE ,
& ifx_dev - > mdm_reset_state ) ,
IFX_RESET_TIMEOUT ) ;
if ( ! ret )
dev_warn ( & ifx_dev - > spi_dev - > dev , " Modem reset timeout: (state:%lx) " ,
ifx_dev - > mdm_reset_state ) ;
ifx_dev - > mdm_reset_state = 0 ;
return ret ;
}
/**
* ifx_spi_spi_probe - probe callback
* @ spi : our possible matching SPI device
*
* Probe for a 6 x60 modem on SPI bus . Perform any needed device and
* GPIO setup .
*
* FIXME :
* - Support for multiple devices
* - Split out MID specific GPIO handling eventually
*/
static int ifx_spi_spi_probe ( struct spi_device * spi )
{
int ret ;
int srdy ;
2011-02-02 23:56:58 +03:00
struct ifx_modem_platform_data * pl_data ;
2010-10-26 17:13:52 +04:00
struct ifx_spi_device * ifx_dev ;
if ( saved_ifx_dev ) {
dev_dbg ( & spi - > dev , " ignoring subsequent detection " ) ;
return - ENODEV ;
}
2013-07-30 12:06:57 +04:00
pl_data = ( struct ifx_modem_platform_data * ) dev_get_platdata ( & spi - > dev ) ;
2011-02-02 23:56:58 +03:00
if ( ! pl_data ) {
dev_err ( & spi - > dev , " missing platform data! " ) ;
return - ENODEV ;
}
2010-10-26 17:13:52 +04:00
/* initialize structure to hold our device variables */
ifx_dev = kzalloc ( sizeof ( struct ifx_spi_device ) , GFP_KERNEL ) ;
if ( ! ifx_dev ) {
dev_err ( & spi - > dev , " spi device allocation failed " ) ;
return - ENOMEM ;
}
saved_ifx_dev = ifx_dev ;
ifx_dev - > spi_dev = spi ;
clear_bit ( IFX_SPI_STATE_IO_IN_PROGRESS , & ifx_dev - > flags ) ;
spin_lock_init ( & ifx_dev - > write_lock ) ;
spin_lock_init ( & ifx_dev - > power_lock ) ;
ifx_dev - > power_status = 0 ;
init_timer ( & ifx_dev - > spi_timer ) ;
ifx_dev - > spi_timer . function = ifx_spi_timeout ;
ifx_dev - > spi_timer . data = ( unsigned long ) ifx_dev ;
2011-02-02 23:56:58 +03:00
ifx_dev - > modem = pl_data - > modem_type ;
ifx_dev - > use_dma = pl_data - > use_dma ;
ifx_dev - > max_hz = pl_data - > max_hz ;
2011-02-07 23:02:31 +03:00
/* initialize spi mode, etc */
2011-02-07 23:02:30 +03:00
spi - > max_speed_hz = ifx_dev - > max_hz ;
2011-02-07 23:02:31 +03:00
spi - > mode = IFX_SPI_MODE | ( SPI_LOOP & spi - > mode ) ;
spi - > bits_per_word = spi_bpw ;
ret = spi_setup ( spi ) ;
if ( ret ) {
dev_err ( & spi - > dev , " SPI setup wasn't successful %d " , ret ) ;
return - ENODEV ;
}
2010-10-26 17:13:52 +04:00
2012-10-25 05:02:32 +04:00
/* init swap_buf function according to word width configuration */
if ( spi - > bits_per_word = = 32 )
ifx_dev - > swap_buf = swap_buf_32 ;
else if ( spi - > bits_per_word = = 16 )
ifx_dev - > swap_buf = swap_buf_16 ;
else
ifx_dev - > swap_buf = swap_buf_8 ;
2010-10-26 17:13:52 +04:00
/* ensure SPI protocol flags are initialized to enable transfer */
ifx_dev - > spi_more = 0 ;
ifx_dev - > spi_slave_cts = 0 ;
/*initialize transfer and dma buffers */
2011-02-07 23:02:28 +03:00
ifx_dev - > tx_buffer = dma_alloc_coherent ( ifx_dev - > spi_dev - > dev . parent ,
2010-10-26 17:13:52 +04:00
IFX_SPI_TRANSFER_SIZE ,
& ifx_dev - > tx_bus ,
GFP_KERNEL ) ;
if ( ! ifx_dev - > tx_buffer ) {
dev_err ( & spi - > dev , " DMA-TX buffer allocation failed " ) ;
ret = - ENOMEM ;
goto error_ret ;
}
2011-02-07 23:02:28 +03:00
ifx_dev - > rx_buffer = dma_alloc_coherent ( ifx_dev - > spi_dev - > dev . parent ,
2010-10-26 17:13:52 +04:00
IFX_SPI_TRANSFER_SIZE ,
& ifx_dev - > rx_bus ,
GFP_KERNEL ) ;
if ( ! ifx_dev - > rx_buffer ) {
dev_err ( & spi - > dev , " DMA-RX buffer allocation failed " ) ;
ret = - ENOMEM ;
goto error_ret ;
}
/* initialize waitq for modem reset */
init_waitqueue_head ( & ifx_dev - > mdm_reset_wait ) ;
spi_set_drvdata ( spi , ifx_dev ) ;
tasklet_init ( & ifx_dev - > io_work_tasklet , ifx_spi_io ,
( unsigned long ) ifx_dev ) ;
set_bit ( IFX_SPI_STATE_PRESENT , & ifx_dev - > flags ) ;
/* create our tty port */
ret = ifx_spi_create_port ( ifx_dev ) ;
if ( ret ! = 0 ) {
dev_err ( & spi - > dev , " create default tty port failed " ) ;
goto error_ret ;
}
2011-02-02 23:56:58 +03:00
ifx_dev - > gpio . reset = pl_data - > rst_pmu ;
ifx_dev - > gpio . po = pl_data - > pwr_on ;
ifx_dev - > gpio . mrdy = pl_data - > mrdy ;
ifx_dev - > gpio . srdy = pl_data - > srdy ;
ifx_dev - > gpio . reset_out = pl_data - > rst_out ;
2010-10-26 17:13:52 +04:00
dev_info ( & spi - > dev , " gpios %d, %d, %d, %d, %d " ,
ifx_dev - > gpio . reset , ifx_dev - > gpio . po , ifx_dev - > gpio . mrdy ,
ifx_dev - > gpio . srdy , ifx_dev - > gpio . reset_out ) ;
/* Configure gpios */
ret = gpio_request ( ifx_dev - > gpio . reset , " ifxModem " ) ;
if ( ret < 0 ) {
dev_err ( & spi - > dev , " Unable to allocate GPIO%d (RESET) " ,
ifx_dev - > gpio . reset ) ;
goto error_ret ;
}
ret + = gpio_direction_output ( ifx_dev - > gpio . reset , 0 ) ;
ret + = gpio_export ( ifx_dev - > gpio . reset , 1 ) ;
if ( ret ) {
dev_err ( & spi - > dev , " Unable to configure GPIO%d (RESET) " ,
ifx_dev - > gpio . reset ) ;
ret = - EBUSY ;
goto error_ret2 ;
}
ret = gpio_request ( ifx_dev - > gpio . po , " ifxModem " ) ;
ret + = gpio_direction_output ( ifx_dev - > gpio . po , 0 ) ;
ret + = gpio_export ( ifx_dev - > gpio . po , 1 ) ;
if ( ret ) {
dev_err ( & spi - > dev , " Unable to configure GPIO%d (ON) " ,
ifx_dev - > gpio . po ) ;
ret = - EBUSY ;
goto error_ret3 ;
}
ret = gpio_request ( ifx_dev - > gpio . mrdy , " ifxModem " ) ;
if ( ret < 0 ) {
dev_err ( & spi - > dev , " Unable to allocate GPIO%d (MRDY) " ,
ifx_dev - > gpio . mrdy ) ;
goto error_ret3 ;
}
ret + = gpio_export ( ifx_dev - > gpio . mrdy , 1 ) ;
ret + = gpio_direction_output ( ifx_dev - > gpio . mrdy , 0 ) ;
if ( ret ) {
dev_err ( & spi - > dev , " Unable to configure GPIO%d (MRDY) " ,
ifx_dev - > gpio . mrdy ) ;
ret = - EBUSY ;
goto error_ret4 ;
}
ret = gpio_request ( ifx_dev - > gpio . srdy , " ifxModem " ) ;
if ( ret < 0 ) {
dev_err ( & spi - > dev , " Unable to allocate GPIO%d (SRDY) " ,
ifx_dev - > gpio . srdy ) ;
ret = - EBUSY ;
goto error_ret4 ;
}
ret + = gpio_export ( ifx_dev - > gpio . srdy , 1 ) ;
ret + = gpio_direction_input ( ifx_dev - > gpio . srdy ) ;
if ( ret ) {
dev_err ( & spi - > dev , " Unable to configure GPIO%d (SRDY) " ,
ifx_dev - > gpio . srdy ) ;
ret = - EBUSY ;
goto error_ret5 ;
}
ret = gpio_request ( ifx_dev - > gpio . reset_out , " ifxModem " ) ;
if ( ret < 0 ) {
dev_err ( & spi - > dev , " Unable to allocate GPIO%d (RESET_OUT) " ,
ifx_dev - > gpio . reset_out ) ;
goto error_ret5 ;
}
ret + = gpio_export ( ifx_dev - > gpio . reset_out , 1 ) ;
ret + = gpio_direction_input ( ifx_dev - > gpio . reset_out ) ;
if ( ret ) {
dev_err ( & spi - > dev , " Unable to configure GPIO%d (RESET_OUT) " ,
ifx_dev - > gpio . reset_out ) ;
ret = - EBUSY ;
goto error_ret6 ;
}
ret = request_irq ( gpio_to_irq ( ifx_dev - > gpio . reset_out ) ,
ifx_spi_reset_interrupt ,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING , DRVNAME ,
( void * ) ifx_dev ) ;
if ( ret ) {
dev_err ( & spi - > dev , " Unable to get irq %x \n " ,
gpio_to_irq ( ifx_dev - > gpio . reset_out ) ) ;
goto error_ret6 ;
}
ret = ifx_spi_reset ( ifx_dev ) ;
ret = request_irq ( gpio_to_irq ( ifx_dev - > gpio . srdy ) ,
ifx_spi_srdy_interrupt ,
IRQF_TRIGGER_RISING , DRVNAME ,
( void * ) ifx_dev ) ;
if ( ret ) {
dev_err ( & spi - > dev , " Unable to get irq %x " ,
gpio_to_irq ( ifx_dev - > gpio . srdy ) ) ;
2010-11-19 21:42:03 +03:00
goto error_ret7 ;
2010-10-26 17:13:52 +04:00
}
/* set pm runtime power state and register with power system */
pm_runtime_set_active ( & spi - > dev ) ;
pm_runtime_enable ( & spi - > dev ) ;
/* handle case that modem is already signaling SRDY */
/* no outgoing tty open at this point, this just satisfies the
* modem ' s read and should reset communication properly
*/
srdy = gpio_get_value ( ifx_dev - > gpio . srdy ) ;
if ( srdy ) {
mrdy_assert ( ifx_dev ) ;
ifx_spi_handle_srdy ( ifx_dev ) ;
} else
mrdy_set_low ( ifx_dev ) ;
return 0 ;
2010-11-19 21:42:03 +03:00
error_ret7 :
free_irq ( gpio_to_irq ( ifx_dev - > gpio . reset_out ) , ( void * ) ifx_dev ) ;
2010-10-26 17:13:52 +04:00
error_ret6 :
gpio_free ( ifx_dev - > gpio . srdy ) ;
error_ret5 :
gpio_free ( ifx_dev - > gpio . mrdy ) ;
error_ret4 :
gpio_free ( ifx_dev - > gpio . reset ) ;
error_ret3 :
gpio_free ( ifx_dev - > gpio . po ) ;
error_ret2 :
gpio_free ( ifx_dev - > gpio . reset_out ) ;
error_ret :
ifx_spi_free_device ( ifx_dev ) ;
saved_ifx_dev = NULL ;
return ret ;
}
/**
* ifx_spi_spi_remove - SPI device was removed
* @ spi : SPI device
*
* FIXME : We should be shutting the device down here not in
* the module unload path .
*/
static int ifx_spi_spi_remove ( struct spi_device * spi )
{
struct ifx_spi_device * ifx_dev = spi_get_drvdata ( spi ) ;
/* stop activity */
tasklet_kill ( & ifx_dev - > io_work_tasklet ) ;
/* free irq */
free_irq ( gpio_to_irq ( ifx_dev - > gpio . reset_out ) , ( void * ) ifx_dev ) ;
free_irq ( gpio_to_irq ( ifx_dev - > gpio . srdy ) , ( void * ) ifx_dev ) ;
gpio_free ( ifx_dev - > gpio . srdy ) ;
gpio_free ( ifx_dev - > gpio . mrdy ) ;
gpio_free ( ifx_dev - > gpio . reset ) ;
gpio_free ( ifx_dev - > gpio . po ) ;
gpio_free ( ifx_dev - > gpio . reset_out ) ;
/* free allocations */
ifx_spi_free_device ( ifx_dev ) ;
saved_ifx_dev = NULL ;
return 0 ;
}
/**
* ifx_spi_spi_shutdown - called on SPI shutdown
* @ spi : SPI device
*
* No action needs to be taken here
*/
static void ifx_spi_spi_shutdown ( struct spi_device * spi )
{
2012-11-23 15:07:27 +04:00
struct ifx_spi_device * ifx_dev = spi_get_drvdata ( spi ) ;
ifx_modem_power_off ( ifx_dev ) ;
2010-10-26 17:13:52 +04:00
}
/*
* various suspends and resumes have nothing to do
* no hardware to save state for
*/
/**
* ifx_spi_pm_suspend - suspend modem on system suspend
* @ dev : device being suspended
*
* Suspend the modem . No action needed on Intel MID platforms , may
* need extending for other systems .
*/
static int ifx_spi_pm_suspend ( struct device * dev )
{
return 0 ;
}
/**
* ifx_spi_pm_resume - resume modem on system resume
* @ dev : device being suspended
*
* Allow the modem to resume . No action needed .
*
* FIXME : do we need to reset anything here ?
*/
static int ifx_spi_pm_resume ( struct device * dev )
{
return 0 ;
}
/**
* ifx_spi_pm_runtime_resume - suspend modem
* @ dev : device being suspended
*
* Allow the modem to resume . No action needed .
*/
static int ifx_spi_pm_runtime_resume ( struct device * dev )
{
return 0 ;
}
/**
* ifx_spi_pm_runtime_suspend - suspend modem
* @ dev : device being suspended
*
* Allow the modem to suspend and thus suspend to continue up the
* device tree .
*/
static int ifx_spi_pm_runtime_suspend ( struct device * dev )
{
return 0 ;
}
/**
* ifx_spi_pm_runtime_idle - check if modem idle
* @ dev : our device
*
* Check conditions and queue runtime suspend if idle .
*/
static int ifx_spi_pm_runtime_idle ( struct device * dev )
{
struct spi_device * spi = to_spi_device ( dev ) ;
struct ifx_spi_device * ifx_dev = spi_get_drvdata ( spi ) ;
if ( ! ifx_dev - > power_status )
pm_runtime_suspend ( dev ) ;
return 0 ;
}
static const struct dev_pm_ops ifx_spi_pm = {
. resume = ifx_spi_pm_resume ,
. suspend = ifx_spi_pm_suspend ,
. runtime_resume = ifx_spi_pm_runtime_resume ,
. runtime_suspend = ifx_spi_pm_runtime_suspend ,
. runtime_idle = ifx_spi_pm_runtime_idle
} ;
static const struct spi_device_id ifx_id_table [ ] = {
{ " ifx6160 " , 0 } ,
{ " ifx6260 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( spi , ifx_id_table ) ;
/* spi operations */
2012-08-07 09:12:47 +04:00
static struct spi_driver ifx_spi_driver = {
2010-10-26 17:13:52 +04:00
. driver = {
2011-02-07 23:02:32 +03:00
. name = DRVNAME ,
2010-10-26 17:13:52 +04:00
. pm = & ifx_spi_pm ,
. owner = THIS_MODULE } ,
. probe = ifx_spi_spi_probe ,
. shutdown = ifx_spi_spi_shutdown ,
2012-11-19 22:21:34 +04:00
. remove = ifx_spi_spi_remove ,
2010-10-26 17:13:52 +04:00
. id_table = ifx_id_table
} ;
/**
* ifx_spi_exit - module exit
*
* Unload the module .
*/
static void __exit ifx_spi_exit ( void )
{
/* unregister */
tty_unregister_driver ( tty_drv ) ;
2012-11-23 15:07:27 +04:00
put_tty_driver ( tty_drv ) ;
2011-02-07 23:02:32 +03:00
spi_unregister_driver ( ( void * ) & ifx_spi_driver ) ;
2012-11-23 15:07:27 +04:00
unregister_reboot_notifier ( & ifx_modem_reboot_notifier_block ) ;
2010-10-26 17:13:52 +04:00
}
/**
* ifx_spi_init - module entry point
*
* Initialise the SPI and tty interfaces for the IFX SPI driver
* We need to initialize upper - edge spi driver after the tty
* driver because otherwise the spi probe will race
*/
static int __init ifx_spi_init ( void )
{
int result ;
tty_drv = alloc_tty_driver ( 1 ) ;
if ( ! tty_drv ) {
pr_err ( " %s: alloc_tty_driver failed " , DRVNAME ) ;
return - ENOMEM ;
}
tty_drv - > driver_name = DRVNAME ;
tty_drv - > name = TTYNAME ;
tty_drv - > minor_start = IFX_SPI_TTY_ID ;
tty_drv - > type = TTY_DRIVER_TYPE_SERIAL ;
tty_drv - > subtype = SERIAL_TYPE_NORMAL ;
tty_drv - > flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV ;
tty_drv - > init_termios = tty_std_termios ;
tty_set_operations ( tty_drv , & ifx_spi_serial_ops ) ;
result = tty_register_driver ( tty_drv ) ;
if ( result ) {
pr_err ( " %s: tty_register_driver failed(%d) " ,
DRVNAME , result ) ;
2012-11-23 15:07:27 +04:00
goto err_free_tty ;
2010-10-26 17:13:52 +04:00
}
2011-02-07 23:02:32 +03:00
result = spi_register_driver ( ( void * ) & ifx_spi_driver ) ;
2010-10-26 17:13:52 +04:00
if ( result ) {
pr_err ( " %s: spi_register_driver failed(%d) " ,
DRVNAME , result ) ;
2012-11-23 15:07:27 +04:00
goto err_unreg_tty ;
}
result = register_reboot_notifier ( & ifx_modem_reboot_notifier_block ) ;
if ( result ) {
pr_err ( " %s: register ifx modem reboot notifier failed(%d) " ,
DRVNAME , result ) ;
goto err_unreg_spi ;
2010-10-26 17:13:52 +04:00
}
2012-11-23 15:07:27 +04:00
return 0 ;
err_unreg_spi :
spi_unregister_driver ( ( void * ) & ifx_spi_driver ) ;
err_unreg_tty :
tty_unregister_driver ( tty_drv ) ;
err_free_tty :
put_tty_driver ( tty_drv ) ;
2010-10-26 17:13:52 +04:00
return result ;
}
module_init ( ifx_spi_init ) ;
module_exit ( ifx_spi_exit ) ;
MODULE_AUTHOR ( " Intel " ) ;
MODULE_DESCRIPTION ( " IFX6x60 spi driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_INFO ( Version , " 0.1-IFX6x60 " ) ;