1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3	Intersil ISL6423 SEC and LNB Power supply controller
4
5	Copyright (C) Manu Abraham <abraham.manu@gmail.com>
6
7*/
8
9#include <linux/delay.h>
10#include <linux/errno.h>
11#include <linux/init.h>
12#include <linux/kernel.h>
13#include <linux/module.h>
14#include <linux/string.h>
15#include <linux/slab.h>
16
17#include <media/dvb_frontend.h>
18#include "isl6423.h"
19
20static unsigned int verbose;
21module_param(verbose, int, 0644);
22MODULE_PARM_DESC(verbose, "Set Verbosity level");
23
24#define FE_ERROR				0
25#define FE_NOTICE				1
26#define FE_INFO					2
27#define FE_DEBUG				3
28#define FE_DEBUGREG				4
29
30#define dprintk(__y, __z, format, arg...) do {						\
31	if (__z) {									\
32		if	((verbose > FE_ERROR) && (verbose > __y))			\
33			printk(KERN_ERR "%s: " format "\n", __func__ , ##arg);		\
34		else if	((verbose > FE_NOTICE) && (verbose > __y))			\
35			printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg);	\
36		else if ((verbose > FE_INFO) && (verbose > __y))			\
37			printk(KERN_INFO "%s: " format "\n", __func__ , ##arg);		\
38		else if ((verbose > FE_DEBUG) && (verbose > __y))			\
39			printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg);	\
40	} else {									\
41		if (verbose > __y)							\
42			printk(format, ##arg);						\
43	}										\
44} while (0)
45
46struct isl6423_dev {
47	const struct isl6423_config	*config;
48	struct i2c_adapter		*i2c;
49
50	u8 reg_3;
51	u8 reg_4;
52
53	unsigned int verbose;
54};
55
56static int isl6423_write(struct isl6423_dev *isl6423, u8 reg)
57{
58	struct i2c_adapter *i2c = isl6423->i2c;
59	u8 addr			= isl6423->config->addr;
60	int err = 0;
61
62	struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = &reg, .len = 1 };
63
64	dprintk(FE_DEBUG, 1, "write reg %02X", reg);
65	err = i2c_transfer(i2c, &msg, 1);
66	if (err < 0)
67		goto exit;
68	return 0;
69
70exit:
71	dprintk(FE_ERROR, 1, "I/O error <%d>", err);
72	return err;
73}
74
75static int isl6423_set_modulation(struct dvb_frontend *fe)
76{
77	struct isl6423_dev *isl6423		= (struct isl6423_dev *) fe->sec_priv;
78	const struct isl6423_config *config	= isl6423->config;
79	int err = 0;
80	u8 reg_2 = 0;
81
82	reg_2 = 0x01 << 5;
83
84	if (config->mod_extern)
85		reg_2 |= (1 << 3);
86	else
87		reg_2 |= (1 << 4);
88
89	err = isl6423_write(isl6423, reg_2);
90	if (err < 0)
91		goto exit;
92	return 0;
93
94exit:
95	dprintk(FE_ERROR, 1, "I/O error <%d>", err);
96	return err;
97}
98
99static int isl6423_voltage_boost(struct dvb_frontend *fe, long arg)
100{
101	struct isl6423_dev *isl6423 = (struct isl6423_dev *) fe->sec_priv;
102	u8 reg_3 = isl6423->reg_3;
103	u8 reg_4 = isl6423->reg_4;
104	int err = 0;
105
106	if (arg) {
107		/* EN = 1, VSPEN = 1, VBOT = 1 */
108		reg_4 |= (1 << 4);
109		reg_4 |= 0x1;
110		reg_3 |= (1 << 3);
111	} else {
112		/* EN = 1, VSPEN = 1, VBOT = 0 */
113		reg_4 |= (1 << 4);
114		reg_4 &= ~0x1;
115		reg_3 |= (1 << 3);
116	}
117	err = isl6423_write(isl6423, reg_3);
118	if (err < 0)
119		goto exit;
120
121	err = isl6423_write(isl6423, reg_4);
122	if (err < 0)
123		goto exit;
124
125	isl6423->reg_3 = reg_3;
126	isl6423->reg_4 = reg_4;
127
128	return 0;
129exit:
130	dprintk(FE_ERROR, 1, "I/O error <%d>", err);
131	return err;
132}
133
134
135static int isl6423_set_voltage(struct dvb_frontend *fe,
136			       enum fe_sec_voltage voltage)
137{
138	struct isl6423_dev *isl6423 = (struct isl6423_dev *) fe->sec_priv;
139	u8 reg_3 = isl6423->reg_3;
140	u8 reg_4 = isl6423->reg_4;
141	int err = 0;
142
143	switch (voltage) {
144	case SEC_VOLTAGE_OFF:
145		/* EN = 0 */
146		reg_4 &= ~(1 << 4);
147		break;
148
149	case SEC_VOLTAGE_13:
150		/* EN = 1, VSPEN = 1, VTOP = 0, VBOT = 0 */
151		reg_4 |= (1 << 4);
152		reg_4 &= ~0x3;
153		reg_3 |= (1 << 3);
154		break;
155
156	case SEC_VOLTAGE_18:
157		/* EN = 1, VSPEN = 1, VTOP = 1, VBOT = 0 */
158		reg_4 |= (1 << 4);
159		reg_4 |=  0x2;
160		reg_4 &= ~0x1;
161		reg_3 |= (1 << 3);
162		break;
163
164	default:
165		break;
166	}
167	err = isl6423_write(isl6423, reg_3);
168	if (err < 0)
169		goto exit;
170
171	err = isl6423_write(isl6423, reg_4);
172	if (err < 0)
173		goto exit;
174
175	isl6423->reg_3 = reg_3;
176	isl6423->reg_4 = reg_4;
177
178	return 0;
179exit:
180	dprintk(FE_ERROR, 1, "I/O error <%d>", err);
181	return err;
182}
183
184static int isl6423_set_current(struct dvb_frontend *fe)
185{
186	struct isl6423_dev *isl6423		= (struct isl6423_dev *) fe->sec_priv;
187	u8 reg_3 = isl6423->reg_3;
188	const struct isl6423_config *config	= isl6423->config;
189	int err = 0;
190
191	switch (config->current_max) {
192	case SEC_CURRENT_275m:
193		/* 275mA */
194		/* ISELH = 0, ISELL = 0 */
195		reg_3 &= ~0x3;
196		break;
197
198	case SEC_CURRENT_515m:
199		/* 515mA */
200		/* ISELH = 0, ISELL = 1 */
201		reg_3 &= ~0x2;
202		reg_3 |=  0x1;
203		break;
204
205	case SEC_CURRENT_635m:
206		/* 635mA */
207		/* ISELH = 1, ISELL = 0 */
208		reg_3 &= ~0x1;
209		reg_3 |=  0x2;
210		break;
211
212	case SEC_CURRENT_800m:
213		/* 800mA */
214		/* ISELH = 1, ISELL = 1 */
215		reg_3 |= 0x3;
216		break;
217	}
218
219	err = isl6423_write(isl6423, reg_3);
220	if (err < 0)
221		goto exit;
222
223	switch (config->curlim) {
224	case SEC_CURRENT_LIM_ON:
225		/* DCL = 0 */
226		reg_3 &= ~0x10;
227		break;
228
229	case SEC_CURRENT_LIM_OFF:
230		/* DCL = 1 */
231		reg_3 |= 0x10;
232		break;
233	}
234
235	err = isl6423_write(isl6423, reg_3);
236	if (err < 0)
237		goto exit;
238
239	isl6423->reg_3 = reg_3;
240
241	return 0;
242exit:
243	dprintk(FE_ERROR, 1, "I/O error <%d>", err);
244	return err;
245}
246
247static void isl6423_release(struct dvb_frontend *fe)
248{
249	isl6423_set_voltage(fe, SEC_VOLTAGE_OFF);
250
251	kfree(fe->sec_priv);
252	fe->sec_priv = NULL;
253}
254
255struct dvb_frontend *isl6423_attach(struct dvb_frontend *fe,
256				    struct i2c_adapter *i2c,
257				    const struct isl6423_config *config)
258{
259	struct isl6423_dev *isl6423;
260
261	isl6423 = kzalloc(sizeof(struct isl6423_dev), GFP_KERNEL);
262	if (!isl6423)
263		return NULL;
264
265	isl6423->config	= config;
266	isl6423->i2c	= i2c;
267	fe->sec_priv	= isl6423;
268
269	/* SR3H = 0, SR3M = 1, SR3L = 0 */
270	isl6423->reg_3 = 0x02 << 5;
271	/* SR4H = 0, SR4M = 1, SR4L = 1 */
272	isl6423->reg_4 = 0x03 << 5;
273
274	if (isl6423_set_current(fe))
275		goto exit;
276
277	if (isl6423_set_modulation(fe))
278		goto exit;
279
280	fe->ops.release_sec		= isl6423_release;
281	fe->ops.set_voltage		= isl6423_set_voltage;
282	fe->ops.enable_high_lnb_voltage = isl6423_voltage_boost;
283	isl6423->verbose		= verbose;
284
285	return fe;
286
287exit:
288	kfree(isl6423);
289	fe->sec_priv = NULL;
290	return NULL;
291}
292EXPORT_SYMBOL_GPL(isl6423_attach);
293
294MODULE_DESCRIPTION("ISL6423 SEC");
295MODULE_AUTHOR("Manu Abraham");
296MODULE_LICENSE("GPL");
297