1/* 2 * lnbh25.c 3 * 4 * Driver for LNB supply and control IC LNBH25 5 * 6 * Copyright (C) 2014 NetUP Inc. 7 * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru> 8 * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru> 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2 of the License, or 13 * (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 */ 20 21#include <linux/module.h> 22#include <linux/init.h> 23#include <linux/string.h> 24#include <linux/slab.h> 25 26#include "dvb_frontend.h" 27#include "lnbh25.h" 28 29/** 30 * struct lnbh25_priv - LNBH25 driver private data 31 * @i2c: pointer to the I2C adapter structure 32 * @i2c_address: I2C address of LNBH25 SEC chip 33 * @config: Registers configuration: 34 * offset 0: 1st register address, always 0x02 (DATA1) 35 * offset 1: DATA1 register value 36 * offset 2: DATA2 register value 37 */ 38struct lnbh25_priv { 39 struct i2c_adapter *i2c; 40 u8 i2c_address; 41 u8 config[3]; 42}; 43 44#define LNBH25_STATUS_OFL 0x1 45#define LNBH25_STATUS_VMON 0x4 46#define LNBH25_VSEL_13 0x03 47#define LNBH25_VSEL_18 0x0a 48 49static int lnbh25_read_vmon(struct lnbh25_priv *priv) 50{ 51 int i, ret; 52 u8 addr = 0x00; 53 u8 status[6]; 54 struct i2c_msg msg[2] = { 55 { 56 .addr = priv->i2c_address, 57 .flags = 0, 58 .len = 1, 59 .buf = &addr 60 }, { 61 .addr = priv->i2c_address, 62 .flags = I2C_M_RD, 63 .len = sizeof(status), 64 .buf = status 65 } 66 }; 67 68 for (i = 0; i < 2; i++) { 69 ret = i2c_transfer(priv->i2c, &msg[i], 1); 70 if (ret >= 0 && ret != 1) 71 ret = -EIO; 72 if (ret < 0) { 73 dev_dbg(&priv->i2c->dev, 74 "%s(): I2C transfer %d failed (%d)\n", 75 __func__, i, ret); 76 return ret; 77 } 78 } 79 print_hex_dump_bytes("lnbh25_read_vmon: ", 80 DUMP_PREFIX_OFFSET, status, sizeof(status)); 81 if ((status[0] & (LNBH25_STATUS_OFL | LNBH25_STATUS_VMON)) != 0) { 82 dev_err(&priv->i2c->dev, 83 "%s(): voltage in failure state, status reg 0x%x\n", 84 __func__, status[0]); 85 return -EIO; 86 } 87 return 0; 88} 89 90static int lnbh25_set_voltage(struct dvb_frontend *fe, 91 enum fe_sec_voltage voltage) 92{ 93 int ret; 94 u8 data1_reg; 95 const char *vsel; 96 struct lnbh25_priv *priv = fe->sec_priv; 97 struct i2c_msg msg = { 98 .addr = priv->i2c_address, 99 .flags = 0, 100 .len = sizeof(priv->config), 101 .buf = priv->config 102 }; 103 104 switch (voltage) { 105 case SEC_VOLTAGE_OFF: 106 data1_reg = 0x00; 107 vsel = "Off"; 108 break; 109 case SEC_VOLTAGE_13: 110 data1_reg = LNBH25_VSEL_13; 111 vsel = "13V"; 112 break; 113 case SEC_VOLTAGE_18: 114 data1_reg = LNBH25_VSEL_18; 115 vsel = "18V"; 116 break; 117 default: 118 return -EINVAL; 119 } 120 priv->config[1] = data1_reg; 121 dev_dbg(&priv->i2c->dev, 122 "%s(): %s, I2C 0x%x write [ %02x %02x %02x ]\n", 123 __func__, vsel, priv->i2c_address, 124 priv->config[0], priv->config[1], priv->config[2]); 125 ret = i2c_transfer(priv->i2c, &msg, 1); 126 if (ret >= 0 && ret != 1) 127 ret = -EIO; 128 if (ret < 0) { 129 dev_err(&priv->i2c->dev, "%s(): I2C transfer error (%d)\n", 130 __func__, ret); 131 return ret; 132 } 133 if (voltage != SEC_VOLTAGE_OFF) { 134 msleep(120); 135 ret = lnbh25_read_vmon(priv); 136 } else { 137 msleep(20); 138 ret = 0; 139 } 140 return ret; 141} 142 143static void lnbh25_release(struct dvb_frontend *fe) 144{ 145 struct lnbh25_priv *priv = fe->sec_priv; 146 147 dev_dbg(&priv->i2c->dev, "%s()\n", __func__); 148 lnbh25_set_voltage(fe, SEC_VOLTAGE_OFF); 149 kfree(fe->sec_priv); 150 fe->sec_priv = NULL; 151} 152 153struct dvb_frontend *lnbh25_attach(struct dvb_frontend *fe, 154 struct lnbh25_config *cfg, 155 struct i2c_adapter *i2c) 156{ 157 struct lnbh25_priv *priv; 158 159 dev_dbg(&i2c->dev, "%s()\n", __func__); 160 priv = kzalloc(sizeof(struct lnbh25_priv), GFP_KERNEL); 161 if (!priv) 162 return NULL; 163 priv->i2c_address = (cfg->i2c_address >> 1); 164 priv->i2c = i2c; 165 priv->config[0] = 0x02; 166 priv->config[1] = 0x00; 167 priv->config[2] = cfg->data2_config; 168 fe->sec_priv = priv; 169 if (lnbh25_set_voltage(fe, SEC_VOLTAGE_OFF)) { 170 dev_err(&i2c->dev, 171 "%s(): no LNBH25 found at I2C addr 0x%02x\n", 172 __func__, priv->i2c_address); 173 kfree(priv); 174 fe->sec_priv = NULL; 175 return NULL; 176 } 177 178 fe->ops.release_sec = lnbh25_release; 179 fe->ops.set_voltage = lnbh25_set_voltage; 180 181 dev_err(&i2c->dev, "%s(): attached at I2C addr 0x%02x\n", 182 __func__, priv->i2c_address); 183 return fe; 184} 185EXPORT_SYMBOL(lnbh25_attach); 186 187MODULE_DESCRIPTION("ST LNBH25 driver"); 188MODULE_AUTHOR("info@netup.ru"); 189MODULE_LICENSE("GPL"); 190