2009-03-03 11:06:00 -03:00
/*
* cimax2 . c
*
* CIMax2 ( R ) SP2 driver in conjunction with NetUp Dual DVB - S2 CI card
*
* Copyright ( C ) 2009 NetUP Inc .
* Copyright ( C ) 2009 Igor M . Liplianin < liplianin @ netup . ru >
* Copyright ( C ) 2009 Abylay Ospan < aospan @ netup . ru >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* 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 .
*/
# include "cx23885.h"
2012-10-27 11:29:23 -03:00
# include "cimax2.h"
2009-03-03 11:06:00 -03:00
# include "dvb_ca_en50221.h"
2013-11-02 06:17:47 -03:00
/* Max transfer size done by I2C transfer functions */
# define MAX_XFER_SIZE 64
2009-03-03 11:06:00 -03:00
/**** Bit definitions for MC417_RWD and MC417_OEN registers ***
bits 31 - 16
+ - - - - - - - - - - - +
| Reserved |
+ - - - - - - - - - - - +
bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8
+ - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - +
| WR # | RD # | | ACK # | ADHI | ADLO | CS1 # | CS0 # |
+ - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - +
bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
+ - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - +
| DATA7 | DATA6 | DATA5 | DATA4 | DATA3 | DATA2 | DATA1 | DATA0 |
+ - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - + - - - - - - - +
* * */
/* MC417 */
# define NETUP_DATA 0x000000ff
# define NETUP_WR 0x00008000
# define NETUP_RD 0x00004000
# define NETUP_ACK 0x00001000
# define NETUP_ADHI 0x00000800
# define NETUP_ADLO 0x00000400
# define NETUP_CS1 0x00000200
# define NETUP_CS0 0x00000100
# define NETUP_EN_ALL 0x00001000
# define NETUP_CTRL_OFF (NETUP_CS1 | NETUP_CS0 | NETUP_WR | NETUP_RD)
# define NETUP_CI_CTL 0x04
# define NETUP_CI_RD 1
2009-12-12 12:16:56 -03:00
# define NETUP_IRQ_DETAM 0x1
# define NETUP_IRQ_IRQAM 0x4
2009-03-03 11:06:00 -03:00
static unsigned int ci_dbg ;
module_param ( ci_dbg , int , 0644 ) ;
MODULE_PARM_DESC ( ci_dbg , " Enable CI debugging " ) ;
2010-03-06 15:46:39 -03:00
static unsigned int ci_irq_enable ;
module_param ( ci_irq_enable , int , 0644 ) ;
MODULE_PARM_DESC ( ci_irq_enable , " Enable IRQ from CAM " ) ;
2009-03-03 11:06:00 -03:00
# define ci_dbg_print(args...) \
do { \
if ( ci_dbg ) \
printk ( KERN_DEBUG args ) ; \
} while ( 0 )
2010-03-06 15:46:39 -03:00
# define ci_irq_flags() (ci_irq_enable ? NETUP_IRQ_IRQAM : 0)
2009-03-03 11:06:00 -03:00
/* stores all private variables for communication with CI */
struct netup_ci_state {
struct dvb_ca_en50221 ca ;
struct mutex ca_mutex ;
struct i2c_adapter * i2c_adap ;
u8 ci_i2c_addr ;
int status ;
struct work_struct work ;
void * priv ;
2009-12-12 12:16:56 -03:00
u8 current_irq_mode ;
int current_ci_flag ;
unsigned long next_status_checked_time ;
2009-03-03 11:06:00 -03:00
} ;
2012-10-27 11:29:23 -03:00
static int netup_read_i2c ( struct i2c_adapter * i2c_adap , u8 addr , u8 reg ,
2009-03-03 11:06:00 -03:00
u8 * buf , int len )
{
int ret ;
struct i2c_msg msg [ ] = {
{
. addr = addr ,
. flags = 0 ,
. buf = & reg ,
. len = 1
} , {
. addr = addr ,
. flags = I2C_M_RD ,
. buf = buf ,
. len = len
}
} ;
ret = i2c_transfer ( i2c_adap , msg , 2 ) ;
if ( ret ! = 2 ) {
ci_dbg_print ( " %s: i2c read error, Reg = 0x%02x, Status = %d \n " ,
__func__ , reg , ret ) ;
return - 1 ;
}
ci_dbg_print ( " %s: i2c read Addr=0x%04x, Reg = 0x%02x, data = %02x \n " ,
__func__ , addr , reg , buf [ 0 ] ) ;
return 0 ;
}
2012-10-27 11:29:23 -03:00
static int netup_write_i2c ( struct i2c_adapter * i2c_adap , u8 addr , u8 reg ,
2009-03-03 11:06:00 -03:00
u8 * buf , int len )
{
int ret ;
2013-11-02 06:17:47 -03:00
u8 buffer [ MAX_XFER_SIZE ] ;
2009-03-03 11:06:00 -03:00
struct i2c_msg msg = {
. addr = addr ,
. flags = 0 ,
. buf = & buffer [ 0 ] ,
. len = len + 1
} ;
2013-11-02 06:17:47 -03:00
if ( 1 + len > sizeof ( buffer ) ) {
printk ( KERN_WARNING
" %s: i2c wr reg=%04x: len=%d is too big! \n " ,
KBUILD_MODNAME , reg , len ) ;
return - EINVAL ;
}
2009-03-03 11:06:00 -03:00
buffer [ 0 ] = reg ;
memcpy ( & buffer [ 1 ] , buf , len ) ;
ret = i2c_transfer ( i2c_adap , & msg , 1 ) ;
if ( ret ! = 1 ) {
ci_dbg_print ( " %s: i2c write error, Reg=[0x%02x], Status=%d \n " ,
__func__ , reg , ret ) ;
return - 1 ;
}
return 0 ;
}
2012-10-27 11:29:23 -03:00
static int netup_ci_get_mem ( struct cx23885_dev * dev )
2009-03-03 11:06:00 -03:00
{
int mem ;
unsigned long timeout = jiffies + msecs_to_jiffies ( 1 ) ;
for ( ; ; ) {
mem = cx_read ( MC417_RWD ) ;
if ( ( mem & NETUP_ACK ) = = 0 )
break ;
if ( time_after ( jiffies , timeout ) )
break ;
udelay ( 1 ) ;
}
cx_set ( MC417_RWD , NETUP_CTRL_OFF ) ;
return mem & 0xff ;
}
2012-10-27 11:29:23 -03:00
static int netup_ci_op_cam ( struct dvb_ca_en50221 * en50221 , int slot ,
2009-03-17 18:13:52 -03:00
u8 flag , u8 read , int addr , u8 data )
2009-03-03 11:06:00 -03:00
{
struct netup_ci_state * state = en50221 - > data ;
struct cx23885_tsport * port = state - > priv ;
struct cx23885_dev * dev = port - > dev ;
u8 store ;
int mem ;
int ret ;
if ( 0 ! = slot )
return - EINVAL ;
2009-12-12 12:16:56 -03:00
if ( state - > current_ci_flag ! = flag ) {
ret = netup_read_i2c ( state - > i2c_adap , state - > ci_i2c_addr ,
0 , & store , 1 ) ;
if ( ret ! = 0 )
return ret ;
2009-03-03 11:06:00 -03:00
2009-12-12 12:16:56 -03:00
store & = ~ 0x0c ;
store | = flag ;
2009-03-03 11:06:00 -03:00
2009-12-12 12:16:56 -03:00
ret = netup_write_i2c ( state - > i2c_adap , state - > ci_i2c_addr ,
0 , & store , 1 ) ;
if ( ret ! = 0 )
return ret ;
2012-09-28 05:37:22 -03:00
}
2009-12-12 12:16:56 -03:00
state - > current_ci_flag = flag ;
2009-03-03 11:06:00 -03:00
2009-09-16 13:08:06 -03:00
mutex_lock ( & dev - > gpio_lock ) ;
2009-03-03 11:06:00 -03:00
/* write addr */
cx_write ( MC417_OEN , NETUP_EN_ALL ) ;
cx_write ( MC417_RWD , NETUP_CTRL_OFF |
NETUP_ADLO | ( 0xff & addr ) ) ;
cx_clear ( MC417_RWD , NETUP_ADLO ) ;
cx_write ( MC417_RWD , NETUP_CTRL_OFF |
NETUP_ADHI | ( 0xff & ( addr > > 8 ) ) ) ;
cx_clear ( MC417_RWD , NETUP_ADHI ) ;
2009-09-16 13:08:06 -03:00
if ( read ) { /* data in */
2009-03-03 11:06:00 -03:00
cx_write ( MC417_OEN , NETUP_EN_ALL | NETUP_DATA ) ;
2009-09-16 13:08:06 -03:00
} else /* data out */
2009-03-03 11:06:00 -03:00
cx_write ( MC417_RWD , NETUP_CTRL_OFF | data ) ;
/* choose chip */
cx_clear ( MC417_RWD ,
( state - > ci_i2c_addr = = 0x40 ) ? NETUP_CS0 : NETUP_CS1 ) ;
/* read/write */
cx_clear ( MC417_RWD , ( read ) ? NETUP_RD : NETUP_WR ) ;
mem = netup_ci_get_mem ( dev ) ;
2009-09-16 13:08:06 -03:00
mutex_unlock ( & dev - > gpio_lock ) ;
2009-03-03 11:06:00 -03:00
if ( ! read )
if ( mem < 0 )
return - EREMOTEIO ;
2009-12-12 12:16:56 -03:00
ci_dbg_print ( " %s: %s: chipaddr=[0x%x] addr=[0x%02x], %s=%x \n " , __func__ ,
( read ) ? " read " : " write " , state - > ci_i2c_addr , addr ,
2009-03-03 11:06:00 -03:00
( flag = = NETUP_CI_CTL ) ? " ctl " : " mem " ,
( read ) ? mem : data ) ;
if ( read )
return mem ;
return 0 ;
}
int netup_ci_read_attribute_mem ( struct dvb_ca_en50221 * en50221 ,
int slot , int addr )
{
return netup_ci_op_cam ( en50221 , slot , 0 , NETUP_CI_RD , addr , 0 ) ;
}
int netup_ci_write_attribute_mem ( struct dvb_ca_en50221 * en50221 ,
int slot , int addr , u8 data )
{
return netup_ci_op_cam ( en50221 , slot , 0 , 0 , addr , data ) ;
}
2012-10-27 11:29:23 -03:00
int netup_ci_read_cam_ctl ( struct dvb_ca_en50221 * en50221 , int slot ,
u8 addr )
2009-03-03 11:06:00 -03:00
{
return netup_ci_op_cam ( en50221 , slot , NETUP_CI_CTL ,
NETUP_CI_RD , addr , 0 ) ;
}
int netup_ci_write_cam_ctl ( struct dvb_ca_en50221 * en50221 , int slot ,
u8 addr , u8 data )
{
return netup_ci_op_cam ( en50221 , slot , NETUP_CI_CTL , 0 , addr , data ) ;
}
int netup_ci_slot_reset ( struct dvb_ca_en50221 * en50221 , int slot )
{
struct netup_ci_state * state = en50221 - > data ;
u8 buf = 0x80 ;
int ret ;
if ( 0 ! = slot )
return - EINVAL ;
udelay ( 500 ) ;
ret = netup_write_i2c ( state - > i2c_adap , state - > ci_i2c_addr ,
0 , & buf , 1 ) ;
if ( ret ! = 0 )
return ret ;
udelay ( 500 ) ;
buf = 0x00 ;
ret = netup_write_i2c ( state - > i2c_adap , state - > ci_i2c_addr ,
0 , & buf , 1 ) ;
msleep ( 1000 ) ;
dvb_ca_en50221_camready_irq ( & state - > ca , 0 ) ;
return 0 ;
}
int netup_ci_slot_shutdown ( struct dvb_ca_en50221 * en50221 , int slot )
{
/* not implemented */
return 0 ;
}
2012-10-27 11:29:23 -03:00
static int netup_ci_set_irq ( struct dvb_ca_en50221 * en50221 , u8 irq_mode )
2009-12-12 12:16:56 -03:00
{
struct netup_ci_state * state = en50221 - > data ;
int ret ;
if ( irq_mode = = state - > current_irq_mode )
return 0 ;
ci_dbg_print ( " %s: chipaddr=[0x%x] setting ci IRQ to [0x%x] \n " ,
__func__ , state - > ci_i2c_addr , irq_mode ) ;
ret = netup_write_i2c ( state - > i2c_adap , state - > ci_i2c_addr ,
0x1b , & irq_mode , 1 ) ;
if ( ret ! = 0 )
return ret ;
state - > current_irq_mode = irq_mode ;
return 0 ;
}
2009-03-03 11:06:00 -03:00
int netup_ci_slot_ts_ctl ( struct dvb_ca_en50221 * en50221 , int slot )
{
struct netup_ci_state * state = en50221 - > data ;
2009-12-12 12:16:56 -03:00
u8 buf ;
2009-03-03 11:06:00 -03:00
if ( 0 ! = slot )
return - EINVAL ;
2009-12-12 12:16:56 -03:00
netup_read_i2c ( state - > i2c_adap , state - > ci_i2c_addr ,
0 , & buf , 1 ) ;
buf | = 0x60 ;
2009-03-03 11:06:00 -03:00
return netup_write_i2c ( state - > i2c_adap , state - > ci_i2c_addr ,
0 , & buf , 1 ) ;
}
/* work handler */
static void netup_read_ci_status ( struct work_struct * work )
{
struct netup_ci_state * state =
container_of ( work , struct netup_ci_state , work ) ;
u8 buf [ 33 ] ;
int ret ;
2009-12-12 12:16:56 -03:00
/* CAM module IRQ processing. fast operation */
dvb_ca_en50221_frda_irq ( & state - > ca , 0 ) ;
2009-03-03 11:06:00 -03:00
2009-12-12 12:16:56 -03:00
/* CAM module INSERT/REMOVE processing. slow operation because of i2c
* transfers */
if ( time_after ( jiffies , state - > next_status_checked_time )
| | ! state - > status ) {
ret = netup_read_i2c ( state - > i2c_adap , state - > ci_i2c_addr ,
0 , & buf [ 0 ] , 33 ) ;
state - > next_status_checked_time = jiffies
+ msecs_to_jiffies ( 1000 ) ;
if ( ret ! = 0 )
return ;
2009-03-03 11:06:00 -03:00
2009-12-12 12:16:56 -03:00
ci_dbg_print ( " %s: Slot Status Addr=[0x%04x], "
" Reg=[0x%02x], data=%02x, "
" TS config = %02x \n " , __func__ ,
state - > ci_i2c_addr , 0 , buf [ 0 ] ,
buf [ 0 ] ) ;
2009-03-03 11:06:00 -03:00
2009-12-12 12:16:56 -03:00
if ( buf [ 0 ] & 1 )
state - > status = DVB_CA_EN50221_POLL_CAM_PRESENT |
DVB_CA_EN50221_POLL_CAM_READY ;
else
state - > status = 0 ;
2010-12-31 02:04:38 -03:00
}
2009-03-03 11:06:00 -03:00
}
/* CI irq handler */
int netup_ci_slot_status ( struct cx23885_dev * dev , u32 pci_status )
{
struct cx23885_tsport * port = NULL ;
struct netup_ci_state * state = NULL ;
2010-12-31 02:04:38 -03:00
ci_dbg_print ( " %s: \n " , __func__ ) ;
if ( 0 = = ( pci_status & ( PCI_MSK_GPIO0 | PCI_MSK_GPIO1 ) ) )
2009-03-03 11:06:00 -03:00
return 0 ;
2010-12-31 02:04:38 -03:00
if ( pci_status & PCI_MSK_GPIO0 ) {
port = & dev - > ts1 ;
state = port - > port_priv ;
schedule_work ( & state - > work ) ;
ci_dbg_print ( " %s: Wakeup CI0 \n " , __func__ ) ;
}
2009-03-03 11:06:00 -03:00
2010-12-31 02:04:38 -03:00
if ( pci_status & PCI_MSK_GPIO1 ) {
port = & dev - > ts2 ;
state = port - > port_priv ;
schedule_work ( & state - > work ) ;
ci_dbg_print ( " %s: Wakeup CI1 \n " , __func__ ) ;
}
2009-03-03 11:06:00 -03:00
return 1 ;
}
2012-10-27 11:29:23 -03:00
int netup_poll_ci_slot_status ( struct dvb_ca_en50221 * en50221 ,
int slot , int open )
2009-03-03 11:06:00 -03:00
{
struct netup_ci_state * state = en50221 - > data ;
if ( 0 ! = slot )
return - EINVAL ;
2010-03-06 15:46:39 -03:00
netup_ci_set_irq ( en50221 , open ? ( NETUP_IRQ_DETAM | ci_irq_flags ( ) )
2009-12-12 12:16:56 -03:00
: NETUP_IRQ_DETAM ) ;
2009-03-03 11:06:00 -03:00
return state - > status ;
}
int netup_ci_init ( struct cx23885_tsport * port )
{
struct netup_ci_state * state ;
u8 cimax_init [ 34 ] = {
0x00 , /* module A control*/
0x00 , /* auto select mask high A */
0x00 , /* auto select mask low A */
0x00 , /* auto select pattern high A */
0x00 , /* auto select pattern low A */
0x44 , /* memory access time A */
0x00 , /* invert input A */
0x00 , /* RFU */
0x00 , /* RFU */
0x00 , /* module B control*/
0x00 , /* auto select mask high B */
0x00 , /* auto select mask low B */
0x00 , /* auto select pattern high B */
0x00 , /* auto select pattern low B */
0x44 , /* memory access time B */
0x00 , /* invert input B */
0x00 , /* RFU */
0x00 , /* RFU */
0x00 , /* auto select mask high Ext */
0x00 , /* auto select mask low Ext */
0x00 , /* auto select pattern high Ext */
0x00 , /* auto select pattern low Ext */
0x00 , /* RFU */
0x02 , /* destination - module A */
0x01 , /* power on (use it like store place) */
0x00 , /* RFU */
0x00 , /* int status read only */
2010-03-06 15:46:39 -03:00
ci_irq_flags ( ) | NETUP_IRQ_DETAM , /* DETAM, IRQAM unmasked */
2009-12-12 12:16:56 -03:00
0x05 , /* EXTINT=active-high, INT=push-pull */
2009-03-03 11:06:00 -03:00
0x00 , /* USCG1 */
0x04 , /* ack active low */
0x00 , /* LOCK = 0 */
0x33 , /* serial mode, rising in, rising out, MSB first*/
2011-03-30 22:57:33 -03:00
0x31 , /* synchronization */
2009-03-03 11:06:00 -03:00
} ;
int ret ;
ci_dbg_print ( " %s \n " , __func__ ) ;
state = kzalloc ( sizeof ( struct netup_ci_state ) , GFP_KERNEL ) ;
if ( ! state ) {
ci_dbg_print ( " %s: Unable create CI structure! \n " , __func__ ) ;
ret = - ENOMEM ;
goto err ;
}
port - > port_priv = state ;
switch ( port - > nr ) {
case 1 :
state - > ci_i2c_addr = 0x40 ;
break ;
case 2 :
state - > ci_i2c_addr = 0x41 ;
break ;
}
state - > i2c_adap = & port - > dev - > i2c_bus [ 0 ] . i2c_adap ;
state - > ca . owner = THIS_MODULE ;
state - > ca . read_attribute_mem = netup_ci_read_attribute_mem ;
state - > ca . write_attribute_mem = netup_ci_write_attribute_mem ;
state - > ca . read_cam_control = netup_ci_read_cam_ctl ;
state - > ca . write_cam_control = netup_ci_write_cam_ctl ;
state - > ca . slot_reset = netup_ci_slot_reset ;
state - > ca . slot_shutdown = netup_ci_slot_shutdown ;
state - > ca . slot_ts_enable = netup_ci_slot_ts_ctl ;
state - > ca . poll_slot_status = netup_poll_ci_slot_status ;
state - > ca . data = state ;
state - > priv = port ;
2010-03-06 15:46:39 -03:00
state - > current_irq_mode = ci_irq_flags ( ) | NETUP_IRQ_DETAM ;
2009-03-03 11:06:00 -03:00
ret = netup_write_i2c ( state - > i2c_adap , state - > ci_i2c_addr ,
0 , & cimax_init [ 0 ] , 34 ) ;
/* lock registers */
ret | = netup_write_i2c ( state - > i2c_adap , state - > ci_i2c_addr ,
0x1f , & cimax_init [ 0x18 ] , 1 ) ;
/* power on slots */
ret | = netup_write_i2c ( state - > i2c_adap , state - > ci_i2c_addr ,
0x18 , & cimax_init [ 0x18 ] , 1 ) ;
if ( 0 ! = ret )
goto err ;
ret = dvb_ca_en50221_init ( & port - > frontends . adapter ,
& state - > ca ,
/* flags */ 0 ,
/* n_slots */ 1 ) ;
if ( 0 ! = ret )
goto err ;
INIT_WORK ( & state - > work , netup_read_ci_status ) ;
2009-07-20 12:27:27 -03:00
schedule_work ( & state - > work ) ;
2009-03-03 11:06:00 -03:00
ci_dbg_print ( " %s: CI initialized! \n " , __func__ ) ;
return 0 ;
err :
ci_dbg_print ( " %s: Cannot initialize CI: Error %d. \n " , __func__ , ret ) ;
kfree ( state ) ;
return ret ;
}
void netup_ci_exit ( struct cx23885_tsport * port )
{
struct netup_ci_state * state ;
if ( NULL = = port )
return ;
state = ( struct netup_ci_state * ) port - > port_priv ;
if ( NULL = = state )
return ;
if ( NULL = = state - > ca . data )
return ;
dvb_ca_en50221_release ( & state - > ca ) ;
kfree ( state ) ;
}