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