18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci Intersil ISL6423 SEC and LNB Power supply controller 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci Copyright (C) Manu Abraham <abraham.manu@gmail.com> 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci*/ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/delay.h> 108c2ecf20Sopenharmony_ci#include <linux/errno.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/module.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 "isl6423.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic unsigned int verbose; 218c2ecf20Sopenharmony_cimodule_param(verbose, int, 0644); 228c2ecf20Sopenharmony_ciMODULE_PARM_DESC(verbose, "Set Verbosity level"); 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define FE_ERROR 0 258c2ecf20Sopenharmony_ci#define FE_NOTICE 1 268c2ecf20Sopenharmony_ci#define FE_INFO 2 278c2ecf20Sopenharmony_ci#define FE_DEBUG 3 288c2ecf20Sopenharmony_ci#define FE_DEBUGREG 4 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define dprintk(__y, __z, format, arg...) do { \ 318c2ecf20Sopenharmony_ci if (__z) { \ 328c2ecf20Sopenharmony_ci if ((verbose > FE_ERROR) && (verbose > __y)) \ 338c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \ 348c2ecf20Sopenharmony_ci else if ((verbose > FE_NOTICE) && (verbose > __y)) \ 358c2ecf20Sopenharmony_ci printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \ 368c2ecf20Sopenharmony_ci else if ((verbose > FE_INFO) && (verbose > __y)) \ 378c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \ 388c2ecf20Sopenharmony_ci else if ((verbose > FE_DEBUG) && (verbose > __y)) \ 398c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \ 408c2ecf20Sopenharmony_ci } else { \ 418c2ecf20Sopenharmony_ci if (verbose > __y) \ 428c2ecf20Sopenharmony_ci printk(format, ##arg); \ 438c2ecf20Sopenharmony_ci } \ 448c2ecf20Sopenharmony_ci} while (0) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistruct isl6423_dev { 478c2ecf20Sopenharmony_ci const struct isl6423_config *config; 488c2ecf20Sopenharmony_ci struct i2c_adapter *i2c; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci u8 reg_3; 518c2ecf20Sopenharmony_ci u8 reg_4; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci unsigned int verbose; 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic int isl6423_write(struct isl6423_dev *isl6423, u8 reg) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct i2c_adapter *i2c = isl6423->i2c; 598c2ecf20Sopenharmony_ci u8 addr = isl6423->config->addr; 608c2ecf20Sopenharmony_ci int err = 0; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = ®, .len = 1 }; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci dprintk(FE_DEBUG, 1, "write reg %02X", reg); 658c2ecf20Sopenharmony_ci err = i2c_transfer(i2c, &msg, 1); 668c2ecf20Sopenharmony_ci if (err < 0) 678c2ecf20Sopenharmony_ci goto exit; 688c2ecf20Sopenharmony_ci return 0; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ciexit: 718c2ecf20Sopenharmony_ci dprintk(FE_ERROR, 1, "I/O error <%d>", err); 728c2ecf20Sopenharmony_ci return err; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic int isl6423_set_modulation(struct dvb_frontend *fe) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct isl6423_dev *isl6423 = (struct isl6423_dev *) fe->sec_priv; 788c2ecf20Sopenharmony_ci const struct isl6423_config *config = isl6423->config; 798c2ecf20Sopenharmony_ci int err = 0; 808c2ecf20Sopenharmony_ci u8 reg_2 = 0; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci reg_2 = 0x01 << 5; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (config->mod_extern) 858c2ecf20Sopenharmony_ci reg_2 |= (1 << 3); 868c2ecf20Sopenharmony_ci else 878c2ecf20Sopenharmony_ci reg_2 |= (1 << 4); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci err = isl6423_write(isl6423, reg_2); 908c2ecf20Sopenharmony_ci if (err < 0) 918c2ecf20Sopenharmony_ci goto exit; 928c2ecf20Sopenharmony_ci return 0; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ciexit: 958c2ecf20Sopenharmony_ci dprintk(FE_ERROR, 1, "I/O error <%d>", err); 968c2ecf20Sopenharmony_ci return err; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic int isl6423_voltage_boost(struct dvb_frontend *fe, long arg) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct isl6423_dev *isl6423 = (struct isl6423_dev *) fe->sec_priv; 1028c2ecf20Sopenharmony_ci u8 reg_3 = isl6423->reg_3; 1038c2ecf20Sopenharmony_ci u8 reg_4 = isl6423->reg_4; 1048c2ecf20Sopenharmony_ci int err = 0; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (arg) { 1078c2ecf20Sopenharmony_ci /* EN = 1, VSPEN = 1, VBOT = 1 */ 1088c2ecf20Sopenharmony_ci reg_4 |= (1 << 4); 1098c2ecf20Sopenharmony_ci reg_4 |= 0x1; 1108c2ecf20Sopenharmony_ci reg_3 |= (1 << 3); 1118c2ecf20Sopenharmony_ci } else { 1128c2ecf20Sopenharmony_ci /* EN = 1, VSPEN = 1, VBOT = 0 */ 1138c2ecf20Sopenharmony_ci reg_4 |= (1 << 4); 1148c2ecf20Sopenharmony_ci reg_4 &= ~0x1; 1158c2ecf20Sopenharmony_ci reg_3 |= (1 << 3); 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci err = isl6423_write(isl6423, reg_3); 1188c2ecf20Sopenharmony_ci if (err < 0) 1198c2ecf20Sopenharmony_ci goto exit; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci err = isl6423_write(isl6423, reg_4); 1228c2ecf20Sopenharmony_ci if (err < 0) 1238c2ecf20Sopenharmony_ci goto exit; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci isl6423->reg_3 = reg_3; 1268c2ecf20Sopenharmony_ci isl6423->reg_4 = reg_4; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return 0; 1298c2ecf20Sopenharmony_ciexit: 1308c2ecf20Sopenharmony_ci dprintk(FE_ERROR, 1, "I/O error <%d>", err); 1318c2ecf20Sopenharmony_ci return err; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic int isl6423_set_voltage(struct dvb_frontend *fe, 1368c2ecf20Sopenharmony_ci enum fe_sec_voltage voltage) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct isl6423_dev *isl6423 = (struct isl6423_dev *) fe->sec_priv; 1398c2ecf20Sopenharmony_ci u8 reg_3 = isl6423->reg_3; 1408c2ecf20Sopenharmony_ci u8 reg_4 = isl6423->reg_4; 1418c2ecf20Sopenharmony_ci int err = 0; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci switch (voltage) { 1448c2ecf20Sopenharmony_ci case SEC_VOLTAGE_OFF: 1458c2ecf20Sopenharmony_ci /* EN = 0 */ 1468c2ecf20Sopenharmony_ci reg_4 &= ~(1 << 4); 1478c2ecf20Sopenharmony_ci break; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci case SEC_VOLTAGE_13: 1508c2ecf20Sopenharmony_ci /* EN = 1, VSPEN = 1, VTOP = 0, VBOT = 0 */ 1518c2ecf20Sopenharmony_ci reg_4 |= (1 << 4); 1528c2ecf20Sopenharmony_ci reg_4 &= ~0x3; 1538c2ecf20Sopenharmony_ci reg_3 |= (1 << 3); 1548c2ecf20Sopenharmony_ci break; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci case SEC_VOLTAGE_18: 1578c2ecf20Sopenharmony_ci /* EN = 1, VSPEN = 1, VTOP = 1, VBOT = 0 */ 1588c2ecf20Sopenharmony_ci reg_4 |= (1 << 4); 1598c2ecf20Sopenharmony_ci reg_4 |= 0x2; 1608c2ecf20Sopenharmony_ci reg_4 &= ~0x1; 1618c2ecf20Sopenharmony_ci reg_3 |= (1 << 3); 1628c2ecf20Sopenharmony_ci break; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci default: 1658c2ecf20Sopenharmony_ci break; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci err = isl6423_write(isl6423, reg_3); 1688c2ecf20Sopenharmony_ci if (err < 0) 1698c2ecf20Sopenharmony_ci goto exit; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci err = isl6423_write(isl6423, reg_4); 1728c2ecf20Sopenharmony_ci if (err < 0) 1738c2ecf20Sopenharmony_ci goto exit; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci isl6423->reg_3 = reg_3; 1768c2ecf20Sopenharmony_ci isl6423->reg_4 = reg_4; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return 0; 1798c2ecf20Sopenharmony_ciexit: 1808c2ecf20Sopenharmony_ci dprintk(FE_ERROR, 1, "I/O error <%d>", err); 1818c2ecf20Sopenharmony_ci return err; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic int isl6423_set_current(struct dvb_frontend *fe) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct isl6423_dev *isl6423 = (struct isl6423_dev *) fe->sec_priv; 1878c2ecf20Sopenharmony_ci u8 reg_3 = isl6423->reg_3; 1888c2ecf20Sopenharmony_ci const struct isl6423_config *config = isl6423->config; 1898c2ecf20Sopenharmony_ci int err = 0; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci switch (config->current_max) { 1928c2ecf20Sopenharmony_ci case SEC_CURRENT_275m: 1938c2ecf20Sopenharmony_ci /* 275mA */ 1948c2ecf20Sopenharmony_ci /* ISELH = 0, ISELL = 0 */ 1958c2ecf20Sopenharmony_ci reg_3 &= ~0x3; 1968c2ecf20Sopenharmony_ci break; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci case SEC_CURRENT_515m: 1998c2ecf20Sopenharmony_ci /* 515mA */ 2008c2ecf20Sopenharmony_ci /* ISELH = 0, ISELL = 1 */ 2018c2ecf20Sopenharmony_ci reg_3 &= ~0x2; 2028c2ecf20Sopenharmony_ci reg_3 |= 0x1; 2038c2ecf20Sopenharmony_ci break; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci case SEC_CURRENT_635m: 2068c2ecf20Sopenharmony_ci /* 635mA */ 2078c2ecf20Sopenharmony_ci /* ISELH = 1, ISELL = 0 */ 2088c2ecf20Sopenharmony_ci reg_3 &= ~0x1; 2098c2ecf20Sopenharmony_ci reg_3 |= 0x2; 2108c2ecf20Sopenharmony_ci break; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci case SEC_CURRENT_800m: 2138c2ecf20Sopenharmony_ci /* 800mA */ 2148c2ecf20Sopenharmony_ci /* ISELH = 1, ISELL = 1 */ 2158c2ecf20Sopenharmony_ci reg_3 |= 0x3; 2168c2ecf20Sopenharmony_ci break; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci err = isl6423_write(isl6423, reg_3); 2208c2ecf20Sopenharmony_ci if (err < 0) 2218c2ecf20Sopenharmony_ci goto exit; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci switch (config->curlim) { 2248c2ecf20Sopenharmony_ci case SEC_CURRENT_LIM_ON: 2258c2ecf20Sopenharmony_ci /* DCL = 0 */ 2268c2ecf20Sopenharmony_ci reg_3 &= ~0x10; 2278c2ecf20Sopenharmony_ci break; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci case SEC_CURRENT_LIM_OFF: 2308c2ecf20Sopenharmony_ci /* DCL = 1 */ 2318c2ecf20Sopenharmony_ci reg_3 |= 0x10; 2328c2ecf20Sopenharmony_ci break; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci err = isl6423_write(isl6423, reg_3); 2368c2ecf20Sopenharmony_ci if (err < 0) 2378c2ecf20Sopenharmony_ci goto exit; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci isl6423->reg_3 = reg_3; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci return 0; 2428c2ecf20Sopenharmony_ciexit: 2438c2ecf20Sopenharmony_ci dprintk(FE_ERROR, 1, "I/O error <%d>", err); 2448c2ecf20Sopenharmony_ci return err; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic void isl6423_release(struct dvb_frontend *fe) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci isl6423_set_voltage(fe, SEC_VOLTAGE_OFF); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci kfree(fe->sec_priv); 2528c2ecf20Sopenharmony_ci fe->sec_priv = NULL; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistruct dvb_frontend *isl6423_attach(struct dvb_frontend *fe, 2568c2ecf20Sopenharmony_ci struct i2c_adapter *i2c, 2578c2ecf20Sopenharmony_ci const struct isl6423_config *config) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci struct isl6423_dev *isl6423; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci isl6423 = kzalloc(sizeof(struct isl6423_dev), GFP_KERNEL); 2628c2ecf20Sopenharmony_ci if (!isl6423) 2638c2ecf20Sopenharmony_ci return NULL; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci isl6423->config = config; 2668c2ecf20Sopenharmony_ci isl6423->i2c = i2c; 2678c2ecf20Sopenharmony_ci fe->sec_priv = isl6423; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* SR3H = 0, SR3M = 1, SR3L = 0 */ 2708c2ecf20Sopenharmony_ci isl6423->reg_3 = 0x02 << 5; 2718c2ecf20Sopenharmony_ci /* SR4H = 0, SR4M = 1, SR4L = 1 */ 2728c2ecf20Sopenharmony_ci isl6423->reg_4 = 0x03 << 5; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if (isl6423_set_current(fe)) 2758c2ecf20Sopenharmony_ci goto exit; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (isl6423_set_modulation(fe)) 2788c2ecf20Sopenharmony_ci goto exit; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci fe->ops.release_sec = isl6423_release; 2818c2ecf20Sopenharmony_ci fe->ops.set_voltage = isl6423_set_voltage; 2828c2ecf20Sopenharmony_ci fe->ops.enable_high_lnb_voltage = isl6423_voltage_boost; 2838c2ecf20Sopenharmony_ci isl6423->verbose = verbose; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci return fe; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ciexit: 2888c2ecf20Sopenharmony_ci kfree(isl6423); 2898c2ecf20Sopenharmony_ci fe->sec_priv = NULL; 2908c2ecf20Sopenharmony_ci return NULL; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(isl6423_attach); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ISL6423 SEC"); 2958c2ecf20Sopenharmony_ciMODULE_AUTHOR("Manu Abraham"); 2968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 297