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