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