2015-07-28 17:33:02 +03:00
/*
* lnbh25 . c
*
* Driver for LNB supply and control IC LNBH25
*
* Copyright ( C ) 2014 NetUP Inc .
* Copyright ( C ) 2014 Sergey Kozlov < serjk @ netup . ru >
* Copyright ( C ) 2014 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 <linux/module.h>
# include <linux/init.h>
# include <linux/string.h>
# include <linux/slab.h>
2017-12-28 21:03:51 +03:00
# include <media/dvb_frontend.h>
2015-07-28 17:33:02 +03:00
# include "lnbh25.h"
/**
* struct lnbh25_priv - LNBH25 driver private data
* @ i2c : pointer to the I2C adapter structure
* @ i2c_address : I2C address of LNBH25 SEC chip
* @ config : Registers configuration :
* offset 0 : 1 st register address , always 0x02 ( DATA1 )
* offset 1 : DATA1 register value
* offset 2 : DATA2 register value
*/
struct lnbh25_priv {
struct i2c_adapter * i2c ;
u8 i2c_address ;
u8 config [ 3 ] ;
} ;
# define LNBH25_STATUS_OFL 0x1
# define LNBH25_STATUS_VMON 0x4
# define LNBH25_VSEL_13 0x03
# define LNBH25_VSEL_18 0x0a
static int lnbh25_read_vmon ( struct lnbh25_priv * priv )
{
int i , ret ;
u8 addr = 0x00 ;
u8 status [ 6 ] ;
struct i2c_msg msg [ 2 ] = {
{
. addr = priv - > i2c_address ,
. flags = 0 ,
. len = 1 ,
. buf = & addr
} , {
. addr = priv - > i2c_address ,
. flags = I2C_M_RD ,
. len = sizeof ( status ) ,
. buf = status
}
} ;
for ( i = 0 ; i < 2 ; i + + ) {
ret = i2c_transfer ( priv - > i2c , & msg [ i ] , 1 ) ;
if ( ret > = 0 & & ret ! = 1 )
ret = - EIO ;
if ( ret < 0 ) {
dev_dbg ( & priv - > i2c - > dev ,
" %s(): I2C transfer %d failed (%d) \n " ,
__func__ , i , ret ) ;
return ret ;
}
}
2017-06-20 22:57:24 +03:00
dev_dbg ( & priv - > i2c - > dev , " %s(): %*ph \n " ,
__func__ , ( int ) sizeof ( status ) , status ) ;
2015-07-28 17:33:02 +03:00
if ( ( status [ 0 ] & ( LNBH25_STATUS_OFL | LNBH25_STATUS_VMON ) ) ! = 0 ) {
dev_err ( & priv - > i2c - > dev ,
" %s(): voltage in failure state, status reg 0x%x \n " ,
__func__ , status [ 0 ] ) ;
return - EIO ;
}
return 0 ;
}
static int lnbh25_set_voltage ( struct dvb_frontend * fe ,
enum fe_sec_voltage voltage )
{
int ret ;
u8 data1_reg ;
const char * vsel ;
struct lnbh25_priv * priv = fe - > sec_priv ;
struct i2c_msg msg = {
. addr = priv - > i2c_address ,
. flags = 0 ,
. len = sizeof ( priv - > config ) ,
. buf = priv - > config
} ;
switch ( voltage ) {
case SEC_VOLTAGE_OFF :
data1_reg = 0x00 ;
vsel = " Off " ;
break ;
case SEC_VOLTAGE_13 :
data1_reg = LNBH25_VSEL_13 ;
vsel = " 13V " ;
break ;
case SEC_VOLTAGE_18 :
data1_reg = LNBH25_VSEL_18 ;
vsel = " 18V " ;
break ;
default :
return - EINVAL ;
}
priv - > config [ 1 ] = data1_reg ;
dev_dbg ( & priv - > i2c - > dev ,
" %s(): %s, I2C 0x%x write [ %02x %02x %02x ] \n " ,
__func__ , vsel , priv - > i2c_address ,
priv - > config [ 0 ] , priv - > config [ 1 ] , priv - > config [ 2 ] ) ;
ret = i2c_transfer ( priv - > i2c , & msg , 1 ) ;
if ( ret > = 0 & & ret ! = 1 )
ret = - EIO ;
if ( ret < 0 ) {
dev_err ( & priv - > i2c - > dev , " %s(): I2C transfer error (%d) \n " ,
__func__ , ret ) ;
return ret ;
}
if ( voltage ! = SEC_VOLTAGE_OFF ) {
msleep ( 120 ) ;
ret = lnbh25_read_vmon ( priv ) ;
} else {
msleep ( 20 ) ;
ret = 0 ;
}
return ret ;
}
static void lnbh25_release ( struct dvb_frontend * fe )
{
struct lnbh25_priv * priv = fe - > sec_priv ;
dev_dbg ( & priv - > i2c - > dev , " %s() \n " , __func__ ) ;
lnbh25_set_voltage ( fe , SEC_VOLTAGE_OFF ) ;
kfree ( fe - > sec_priv ) ;
fe - > sec_priv = NULL ;
}
struct dvb_frontend * lnbh25_attach ( struct dvb_frontend * fe ,
struct lnbh25_config * cfg ,
struct i2c_adapter * i2c )
{
struct lnbh25_priv * priv ;
dev_dbg ( & i2c - > dev , " %s() \n " , __func__ ) ;
priv = kzalloc ( sizeof ( struct lnbh25_priv ) , GFP_KERNEL ) ;
if ( ! priv )
return NULL ;
priv - > i2c_address = ( cfg - > i2c_address > > 1 ) ;
priv - > i2c = i2c ;
priv - > config [ 0 ] = 0x02 ;
priv - > config [ 1 ] = 0x00 ;
priv - > config [ 2 ] = cfg - > data2_config ;
fe - > sec_priv = priv ;
if ( lnbh25_set_voltage ( fe , SEC_VOLTAGE_OFF ) ) {
dev_err ( & i2c - > dev ,
" %s(): no LNBH25 found at I2C addr 0x%02x \n " ,
__func__ , priv - > i2c_address ) ;
kfree ( priv ) ;
fe - > sec_priv = NULL ;
return NULL ;
}
fe - > ops . release_sec = lnbh25_release ;
fe - > ops . set_voltage = lnbh25_set_voltage ;
2017-06-20 22:57:24 +03:00
dev_info ( & i2c - > dev , " %s(): attached at I2C addr 0x%02x \n " ,
2015-07-28 17:33:02 +03:00
__func__ , priv - > i2c_address ) ;
return fe ;
}
EXPORT_SYMBOL ( lnbh25_attach ) ;
MODULE_DESCRIPTION ( " ST LNBH25 driver " ) ;
MODULE_AUTHOR ( " info@netup.ru " ) ;
MODULE_LICENSE ( " GPL " ) ;