18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * lnbh25.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Driver for LNB supply and control IC LNBH25 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2014 NetUP Inc. 88c2ecf20Sopenharmony_ci * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru> 98c2ecf20Sopenharmony_ci * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/string.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h> 188c2ecf20Sopenharmony_ci#include "lnbh25.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/** 218c2ecf20Sopenharmony_ci * struct lnbh25_priv - LNBH25 driver private data 228c2ecf20Sopenharmony_ci * @i2c: pointer to the I2C adapter structure 238c2ecf20Sopenharmony_ci * @i2c_address: I2C address of LNBH25 SEC chip 248c2ecf20Sopenharmony_ci * @config: Registers configuration: 258c2ecf20Sopenharmony_ci * offset 0: 1st register address, always 0x02 (DATA1) 268c2ecf20Sopenharmony_ci * offset 1: DATA1 register value 278c2ecf20Sopenharmony_ci * offset 2: DATA2 register value 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_cistruct lnbh25_priv { 308c2ecf20Sopenharmony_ci struct i2c_adapter *i2c; 318c2ecf20Sopenharmony_ci u8 i2c_address; 328c2ecf20Sopenharmony_ci u8 config[3]; 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define LNBH25_STATUS_OFL 0x1 368c2ecf20Sopenharmony_ci#define LNBH25_STATUS_VMON 0x4 378c2ecf20Sopenharmony_ci#define LNBH25_VSEL_13 0x03 388c2ecf20Sopenharmony_ci#define LNBH25_VSEL_18 0x0a 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int lnbh25_read_vmon(struct lnbh25_priv *priv) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci int i, ret; 438c2ecf20Sopenharmony_ci u8 addr = 0x00; 448c2ecf20Sopenharmony_ci u8 status[6]; 458c2ecf20Sopenharmony_ci struct i2c_msg msg[2] = { 468c2ecf20Sopenharmony_ci { 478c2ecf20Sopenharmony_ci .addr = priv->i2c_address, 488c2ecf20Sopenharmony_ci .flags = 0, 498c2ecf20Sopenharmony_ci .len = 1, 508c2ecf20Sopenharmony_ci .buf = &addr 518c2ecf20Sopenharmony_ci }, { 528c2ecf20Sopenharmony_ci .addr = priv->i2c_address, 538c2ecf20Sopenharmony_ci .flags = I2C_M_RD, 548c2ecf20Sopenharmony_ci .len = sizeof(status), 558c2ecf20Sopenharmony_ci .buf = status 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci }; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 608c2ecf20Sopenharmony_ci ret = i2c_transfer(priv->i2c, &msg[i], 1); 618c2ecf20Sopenharmony_ci if (ret >= 0 && ret != 1) 628c2ecf20Sopenharmony_ci ret = -EIO; 638c2ecf20Sopenharmony_ci if (ret < 0) { 648c2ecf20Sopenharmony_ci dev_dbg(&priv->i2c->dev, 658c2ecf20Sopenharmony_ci "%s(): I2C transfer %d failed (%d)\n", 668c2ecf20Sopenharmony_ci __func__, i, ret); 678c2ecf20Sopenharmony_ci return ret; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci dev_dbg(&priv->i2c->dev, "%s(): %*ph\n", 718c2ecf20Sopenharmony_ci __func__, (int) sizeof(status), status); 728c2ecf20Sopenharmony_ci if ((status[0] & (LNBH25_STATUS_OFL | LNBH25_STATUS_VMON)) != 0) { 738c2ecf20Sopenharmony_ci dev_err(&priv->i2c->dev, 748c2ecf20Sopenharmony_ci "%s(): voltage in failure state, status reg 0x%x\n", 758c2ecf20Sopenharmony_ci __func__, status[0]); 768c2ecf20Sopenharmony_ci return -EIO; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci return 0; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic int lnbh25_set_voltage(struct dvb_frontend *fe, 828c2ecf20Sopenharmony_ci enum fe_sec_voltage voltage) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci int ret; 858c2ecf20Sopenharmony_ci u8 data1_reg; 868c2ecf20Sopenharmony_ci const char *vsel; 878c2ecf20Sopenharmony_ci struct lnbh25_priv *priv = fe->sec_priv; 888c2ecf20Sopenharmony_ci struct i2c_msg msg = { 898c2ecf20Sopenharmony_ci .addr = priv->i2c_address, 908c2ecf20Sopenharmony_ci .flags = 0, 918c2ecf20Sopenharmony_ci .len = sizeof(priv->config), 928c2ecf20Sopenharmony_ci .buf = priv->config 938c2ecf20Sopenharmony_ci }; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci switch (voltage) { 968c2ecf20Sopenharmony_ci case SEC_VOLTAGE_OFF: 978c2ecf20Sopenharmony_ci data1_reg = 0x00; 988c2ecf20Sopenharmony_ci vsel = "Off"; 998c2ecf20Sopenharmony_ci break; 1008c2ecf20Sopenharmony_ci case SEC_VOLTAGE_13: 1018c2ecf20Sopenharmony_ci data1_reg = LNBH25_VSEL_13; 1028c2ecf20Sopenharmony_ci vsel = "13V"; 1038c2ecf20Sopenharmony_ci break; 1048c2ecf20Sopenharmony_ci case SEC_VOLTAGE_18: 1058c2ecf20Sopenharmony_ci data1_reg = LNBH25_VSEL_18; 1068c2ecf20Sopenharmony_ci vsel = "18V"; 1078c2ecf20Sopenharmony_ci break; 1088c2ecf20Sopenharmony_ci default: 1098c2ecf20Sopenharmony_ci return -EINVAL; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci priv->config[1] = data1_reg; 1128c2ecf20Sopenharmony_ci dev_dbg(&priv->i2c->dev, 1138c2ecf20Sopenharmony_ci "%s(): %s, I2C 0x%x write [ %02x %02x %02x ]\n", 1148c2ecf20Sopenharmony_ci __func__, vsel, priv->i2c_address, 1158c2ecf20Sopenharmony_ci priv->config[0], priv->config[1], priv->config[2]); 1168c2ecf20Sopenharmony_ci ret = i2c_transfer(priv->i2c, &msg, 1); 1178c2ecf20Sopenharmony_ci if (ret >= 0 && ret != 1) 1188c2ecf20Sopenharmony_ci ret = -EIO; 1198c2ecf20Sopenharmony_ci if (ret < 0) { 1208c2ecf20Sopenharmony_ci dev_err(&priv->i2c->dev, "%s(): I2C transfer error (%d)\n", 1218c2ecf20Sopenharmony_ci __func__, ret); 1228c2ecf20Sopenharmony_ci return ret; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci if (voltage != SEC_VOLTAGE_OFF) { 1258c2ecf20Sopenharmony_ci msleep(120); 1268c2ecf20Sopenharmony_ci ret = lnbh25_read_vmon(priv); 1278c2ecf20Sopenharmony_ci } else { 1288c2ecf20Sopenharmony_ci msleep(20); 1298c2ecf20Sopenharmony_ci ret = 0; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci return ret; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic void lnbh25_release(struct dvb_frontend *fe) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct lnbh25_priv *priv = fe->sec_priv; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci dev_dbg(&priv->i2c->dev, "%s()\n", __func__); 1398c2ecf20Sopenharmony_ci lnbh25_set_voltage(fe, SEC_VOLTAGE_OFF); 1408c2ecf20Sopenharmony_ci kfree(fe->sec_priv); 1418c2ecf20Sopenharmony_ci fe->sec_priv = NULL; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistruct dvb_frontend *lnbh25_attach(struct dvb_frontend *fe, 1458c2ecf20Sopenharmony_ci struct lnbh25_config *cfg, 1468c2ecf20Sopenharmony_ci struct i2c_adapter *i2c) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct lnbh25_priv *priv; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci dev_dbg(&i2c->dev, "%s()\n", __func__); 1518c2ecf20Sopenharmony_ci priv = kzalloc(sizeof(struct lnbh25_priv), GFP_KERNEL); 1528c2ecf20Sopenharmony_ci if (!priv) 1538c2ecf20Sopenharmony_ci return NULL; 1548c2ecf20Sopenharmony_ci priv->i2c_address = (cfg->i2c_address >> 1); 1558c2ecf20Sopenharmony_ci priv->i2c = i2c; 1568c2ecf20Sopenharmony_ci priv->config[0] = 0x02; 1578c2ecf20Sopenharmony_ci priv->config[1] = 0x00; 1588c2ecf20Sopenharmony_ci priv->config[2] = cfg->data2_config; 1598c2ecf20Sopenharmony_ci fe->sec_priv = priv; 1608c2ecf20Sopenharmony_ci if (lnbh25_set_voltage(fe, SEC_VOLTAGE_OFF)) { 1618c2ecf20Sopenharmony_ci dev_err(&i2c->dev, 1628c2ecf20Sopenharmony_ci "%s(): no LNBH25 found at I2C addr 0x%02x\n", 1638c2ecf20Sopenharmony_ci __func__, priv->i2c_address); 1648c2ecf20Sopenharmony_ci kfree(priv); 1658c2ecf20Sopenharmony_ci fe->sec_priv = NULL; 1668c2ecf20Sopenharmony_ci return NULL; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci fe->ops.release_sec = lnbh25_release; 1708c2ecf20Sopenharmony_ci fe->ops.set_voltage = lnbh25_set_voltage; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci dev_info(&i2c->dev, "%s(): attached at I2C addr 0x%02x\n", 1738c2ecf20Sopenharmony_ci __func__, priv->i2c_address); 1748c2ecf20Sopenharmony_ci return fe; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lnbh25_attach); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ST LNBH25 driver"); 1798c2ecf20Sopenharmony_ciMODULE_AUTHOR("info@netup.ru"); 1808c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 181