18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Driver for LNB supply and control IC STMicroelectronics LNBH29 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright (c) 2018 Socionext Inc. 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h> 128c2ecf20Sopenharmony_ci#include "lnbh29.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/** 158c2ecf20Sopenharmony_ci * struct lnbh29_priv - LNBH29 driver private data 168c2ecf20Sopenharmony_ci * @i2c: Pointer to the I2C adapter structure 178c2ecf20Sopenharmony_ci * @i2c_address: I2C address of LNBH29 chip 188c2ecf20Sopenharmony_ci * @config: Registers configuration 198c2ecf20Sopenharmony_ci * offset 0: 1st register address, always 0x01 (DATA) 208c2ecf20Sopenharmony_ci * offset 1: DATA register value 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_cistruct lnbh29_priv { 238c2ecf20Sopenharmony_ci struct i2c_adapter *i2c; 248c2ecf20Sopenharmony_ci u8 i2c_address; 258c2ecf20Sopenharmony_ci u8 config[2]; 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define LNBH29_STATUS_OLF BIT(0) 298c2ecf20Sopenharmony_ci#define LNBH29_STATUS_OTF BIT(1) 308c2ecf20Sopenharmony_ci#define LNBH29_STATUS_VMON BIT(2) 318c2ecf20Sopenharmony_ci#define LNBH29_STATUS_PNG BIT(3) 328c2ecf20Sopenharmony_ci#define LNBH29_STATUS_PDO BIT(4) 338c2ecf20Sopenharmony_ci#define LNBH29_VSEL_MASK GENMASK(2, 0) 348c2ecf20Sopenharmony_ci#define LNBH29_VSEL_0 0x00 358c2ecf20Sopenharmony_ci/* Min: 13.188V, Typ: 13.667V, Max:14V */ 368c2ecf20Sopenharmony_ci#define LNBH29_VSEL_13 0x03 378c2ecf20Sopenharmony_ci/* Min: 18.158V, Typ: 18.817V, Max:19.475V */ 388c2ecf20Sopenharmony_ci#define LNBH29_VSEL_18 0x07 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int lnbh29_read_vmon(struct lnbh29_priv *priv) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci u8 addr = 0x00; 438c2ecf20Sopenharmony_ci u8 status[2]; 448c2ecf20Sopenharmony_ci int ret; 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 ret = i2c_transfer(priv->i2c, msg, 2); 608c2ecf20Sopenharmony_ci if (ret >= 0 && ret != 2) 618c2ecf20Sopenharmony_ci ret = -EIO; 628c2ecf20Sopenharmony_ci if (ret < 0) { 638c2ecf20Sopenharmony_ci dev_dbg(&priv->i2c->dev, "LNBH29 I2C transfer failed (%d)\n", 648c2ecf20Sopenharmony_ci ret); 658c2ecf20Sopenharmony_ci return ret; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (status[0] & (LNBH29_STATUS_OLF | LNBH29_STATUS_VMON)) { 698c2ecf20Sopenharmony_ci dev_err(&priv->i2c->dev, 708c2ecf20Sopenharmony_ci "LNBH29 voltage in failure state, status reg 0x%x\n", 718c2ecf20Sopenharmony_ci status[0]); 728c2ecf20Sopenharmony_ci return -EIO; 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci return 0; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic int lnbh29_set_voltage(struct dvb_frontend *fe, 798c2ecf20Sopenharmony_ci enum fe_sec_voltage voltage) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct lnbh29_priv *priv = fe->sec_priv; 828c2ecf20Sopenharmony_ci u8 data_reg; 838c2ecf20Sopenharmony_ci int ret; 848c2ecf20Sopenharmony_ci struct i2c_msg msg = { 858c2ecf20Sopenharmony_ci .addr = priv->i2c_address, 868c2ecf20Sopenharmony_ci .flags = 0, 878c2ecf20Sopenharmony_ci .len = sizeof(priv->config), 888c2ecf20Sopenharmony_ci .buf = priv->config 898c2ecf20Sopenharmony_ci }; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci switch (voltage) { 928c2ecf20Sopenharmony_ci case SEC_VOLTAGE_OFF: 938c2ecf20Sopenharmony_ci data_reg = LNBH29_VSEL_0; 948c2ecf20Sopenharmony_ci break; 958c2ecf20Sopenharmony_ci case SEC_VOLTAGE_13: 968c2ecf20Sopenharmony_ci data_reg = LNBH29_VSEL_13; 978c2ecf20Sopenharmony_ci break; 988c2ecf20Sopenharmony_ci case SEC_VOLTAGE_18: 998c2ecf20Sopenharmony_ci data_reg = LNBH29_VSEL_18; 1008c2ecf20Sopenharmony_ci break; 1018c2ecf20Sopenharmony_ci default: 1028c2ecf20Sopenharmony_ci return -EINVAL; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci priv->config[1] &= ~LNBH29_VSEL_MASK; 1058c2ecf20Sopenharmony_ci priv->config[1] |= data_reg; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci ret = i2c_transfer(priv->i2c, &msg, 1); 1088c2ecf20Sopenharmony_ci if (ret >= 0 && ret != 1) 1098c2ecf20Sopenharmony_ci ret = -EIO; 1108c2ecf20Sopenharmony_ci if (ret < 0) { 1118c2ecf20Sopenharmony_ci dev_err(&priv->i2c->dev, "LNBH29 I2C transfer error (%d)\n", 1128c2ecf20Sopenharmony_ci ret); 1138c2ecf20Sopenharmony_ci return ret; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* Soft-start time (Vout 0V to 18V) is Typ. 6ms. */ 1178c2ecf20Sopenharmony_ci usleep_range(6000, 20000); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (voltage == SEC_VOLTAGE_OFF) 1208c2ecf20Sopenharmony_ci return 0; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return lnbh29_read_vmon(priv); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic void lnbh29_release(struct dvb_frontend *fe) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci lnbh29_set_voltage(fe, SEC_VOLTAGE_OFF); 1288c2ecf20Sopenharmony_ci kfree(fe->sec_priv); 1298c2ecf20Sopenharmony_ci fe->sec_priv = NULL; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistruct dvb_frontend *lnbh29_attach(struct dvb_frontend *fe, 1338c2ecf20Sopenharmony_ci struct lnbh29_config *cfg, 1348c2ecf20Sopenharmony_ci struct i2c_adapter *i2c) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct lnbh29_priv *priv; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci priv = kzalloc(sizeof(*priv), GFP_KERNEL); 1398c2ecf20Sopenharmony_ci if (!priv) 1408c2ecf20Sopenharmony_ci return NULL; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci priv->i2c_address = (cfg->i2c_address >> 1); 1438c2ecf20Sopenharmony_ci priv->i2c = i2c; 1448c2ecf20Sopenharmony_ci priv->config[0] = 0x01; 1458c2ecf20Sopenharmony_ci priv->config[1] = cfg->data_config; 1468c2ecf20Sopenharmony_ci fe->sec_priv = priv; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (lnbh29_set_voltage(fe, SEC_VOLTAGE_OFF)) { 1498c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "no LNBH29 found at I2C addr 0x%02x\n", 1508c2ecf20Sopenharmony_ci priv->i2c_address); 1518c2ecf20Sopenharmony_ci kfree(priv); 1528c2ecf20Sopenharmony_ci fe->sec_priv = NULL; 1538c2ecf20Sopenharmony_ci return NULL; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci fe->ops.release_sec = lnbh29_release; 1578c2ecf20Sopenharmony_ci fe->ops.set_voltage = lnbh29_set_voltage; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci dev_info(&i2c->dev, "LNBH29 attached at I2C addr 0x%02x\n", 1608c2ecf20Sopenharmony_ci priv->i2c_address); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return fe; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lnbh29_attach); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ciMODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>"); 1678c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("STMicroelectronics LNBH29 driver"); 1688c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 169