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 
20 static unsigned int verbose;
21 module_param(verbose, int, 0644);
22 MODULE_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 
46 struct 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 
56 static 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 
70 exit:
71 	dprintk(FE_ERROR, 1, "I/O error <%d>", err);
72 	return err;
73 }
74 
75 static 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 
94 exit:
95 	dprintk(FE_ERROR, 1, "I/O error <%d>", err);
96 	return err;
97 }
98 
99 static 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;
129 exit:
130 	dprintk(FE_ERROR, 1, "I/O error <%d>", err);
131 	return err;
132 }
133 
134 
135 static 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;
179 exit:
180 	dprintk(FE_ERROR, 1, "I/O error <%d>", err);
181 	return err;
182 }
183 
184 static 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;
242 exit:
243 	dprintk(FE_ERROR, 1, "I/O error <%d>", err);
244 	return err;
245 }
246 
247 static 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 
255 struct 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 
287 exit:
288 	kfree(isl6423);
289 	fe->sec_priv = NULL;
290 	return NULL;
291 }
292 EXPORT_SYMBOL(isl6423_attach);
293 
294 MODULE_DESCRIPTION("ISL6423 SEC");
295 MODULE_AUTHOR("Manu Abraham");
296 MODULE_LICENSE("GPL");
297