1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2e025273bSKozlov Sergey /*
3e025273bSKozlov Sergey  * lnbh25.c
4e025273bSKozlov Sergey  *
5e025273bSKozlov Sergey  * Driver for LNB supply and control IC LNBH25
6e025273bSKozlov Sergey  *
7e025273bSKozlov Sergey  * Copyright (C) 2014 NetUP Inc.
8e025273bSKozlov Sergey  * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
9e025273bSKozlov Sergey  * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru>
10e025273bSKozlov Sergey  */
11e025273bSKozlov Sergey 
12e025273bSKozlov Sergey #include <linux/module.h>
13e025273bSKozlov Sergey #include <linux/init.h>
14e025273bSKozlov Sergey #include <linux/string.h>
15e025273bSKozlov Sergey #include <linux/slab.h>
16e025273bSKozlov Sergey 
17fada1935SMauro Carvalho Chehab #include <media/dvb_frontend.h>
18e025273bSKozlov Sergey #include "lnbh25.h"
19e025273bSKozlov Sergey 
20e025273bSKozlov Sergey /**
21e025273bSKozlov Sergey  * struct lnbh25_priv - LNBH25 driver private data
22e025273bSKozlov Sergey  * @i2c:		pointer to the I2C adapter structure
23e025273bSKozlov Sergey  * @i2c_address:	I2C address of LNBH25 SEC chip
24e025273bSKozlov Sergey  * @config:		Registers configuration:
25e025273bSKozlov Sergey  *			offset 0: 1st register address, always 0x02 (DATA1)
26e025273bSKozlov Sergey  *			offset 1: DATA1 register value
27e025273bSKozlov Sergey  *			offset 2: DATA2 register value
28e025273bSKozlov Sergey  */
29e025273bSKozlov Sergey struct lnbh25_priv {
30e025273bSKozlov Sergey 	struct i2c_adapter	*i2c;
31e025273bSKozlov Sergey 	u8			i2c_address;
32e025273bSKozlov Sergey 	u8			config[3];
33e025273bSKozlov Sergey };
34e025273bSKozlov Sergey 
35e025273bSKozlov Sergey #define LNBH25_STATUS_OFL	0x1
36e025273bSKozlov Sergey #define LNBH25_STATUS_VMON	0x4
37e025273bSKozlov Sergey #define LNBH25_VSEL_13		0x03
38e025273bSKozlov Sergey #define LNBH25_VSEL_18		0x0a
39e025273bSKozlov Sergey 
lnbh25_read_vmon(struct lnbh25_priv * priv)40e025273bSKozlov Sergey static int lnbh25_read_vmon(struct lnbh25_priv *priv)
41e025273bSKozlov Sergey {
42e025273bSKozlov Sergey 	int i, ret;
43e025273bSKozlov Sergey 	u8 addr = 0x00;
44e025273bSKozlov Sergey 	u8 status[6];
45e025273bSKozlov Sergey 	struct i2c_msg msg[2] = {
46e025273bSKozlov Sergey 		{
47e025273bSKozlov Sergey 			.addr = priv->i2c_address,
48e025273bSKozlov Sergey 			.flags = 0,
49e025273bSKozlov Sergey 			.len = 1,
50e025273bSKozlov Sergey 			.buf = &addr
51e025273bSKozlov Sergey 		}, {
52e025273bSKozlov Sergey 			.addr = priv->i2c_address,
53e025273bSKozlov Sergey 			.flags = I2C_M_RD,
54e025273bSKozlov Sergey 			.len = sizeof(status),
55e025273bSKozlov Sergey 			.buf = status
56e025273bSKozlov Sergey 		}
57e025273bSKozlov Sergey 	};
58e025273bSKozlov Sergey 
59e025273bSKozlov Sergey 	for (i = 0; i < 2; i++) {
60e025273bSKozlov Sergey 		ret = i2c_transfer(priv->i2c, &msg[i], 1);
61e025273bSKozlov Sergey 		if (ret >= 0 && ret != 1)
62e025273bSKozlov Sergey 			ret = -EIO;
63e025273bSKozlov Sergey 		if (ret < 0) {
64e025273bSKozlov Sergey 			dev_dbg(&priv->i2c->dev,
65e025273bSKozlov Sergey 				"%s(): I2C transfer %d failed (%d)\n",
66e025273bSKozlov Sergey 				__func__, i, ret);
67e025273bSKozlov Sergey 			return ret;
68e025273bSKozlov Sergey 		}
69e025273bSKozlov Sergey 	}
70e1bdf024SDaniel Scheller 	dev_dbg(&priv->i2c->dev, "%s(): %*ph\n",
71e1bdf024SDaniel Scheller 		__func__, (int) sizeof(status), status);
72e025273bSKozlov Sergey 	if ((status[0] & (LNBH25_STATUS_OFL | LNBH25_STATUS_VMON)) != 0) {
73e025273bSKozlov Sergey 		dev_err(&priv->i2c->dev,
74e025273bSKozlov Sergey 			"%s(): voltage in failure state, status reg 0x%x\n",
75e025273bSKozlov Sergey 			__func__, status[0]);
76e025273bSKozlov Sergey 		return -EIO;
77e025273bSKozlov Sergey 	}
78e025273bSKozlov Sergey 	return 0;
79e025273bSKozlov Sergey }
80e025273bSKozlov Sergey 
lnbh25_set_voltage(struct dvb_frontend * fe,enum fe_sec_voltage voltage)81e025273bSKozlov Sergey static int lnbh25_set_voltage(struct dvb_frontend *fe,
82e025273bSKozlov Sergey 			      enum fe_sec_voltage voltage)
83e025273bSKozlov Sergey {
84e025273bSKozlov Sergey 	int ret;
85e025273bSKozlov Sergey 	u8 data1_reg;
86e025273bSKozlov Sergey 	const char *vsel;
87e025273bSKozlov Sergey 	struct lnbh25_priv *priv = fe->sec_priv;
88e025273bSKozlov Sergey 	struct i2c_msg msg = {
89e025273bSKozlov Sergey 		.addr = priv->i2c_address,
90e025273bSKozlov Sergey 		.flags = 0,
91e025273bSKozlov Sergey 		.len = sizeof(priv->config),
92e025273bSKozlov Sergey 		.buf = priv->config
93e025273bSKozlov Sergey 	};
94e025273bSKozlov Sergey 
95e025273bSKozlov Sergey 	switch (voltage) {
96e025273bSKozlov Sergey 	case SEC_VOLTAGE_OFF:
97e025273bSKozlov Sergey 		data1_reg = 0x00;
98e025273bSKozlov Sergey 		vsel = "Off";
99e025273bSKozlov Sergey 		break;
100e025273bSKozlov Sergey 	case SEC_VOLTAGE_13:
101e025273bSKozlov Sergey 		data1_reg = LNBH25_VSEL_13;
102e025273bSKozlov Sergey 		vsel = "13V";
103e025273bSKozlov Sergey 		break;
104e025273bSKozlov Sergey 	case SEC_VOLTAGE_18:
105e025273bSKozlov Sergey 		data1_reg = LNBH25_VSEL_18;
106e025273bSKozlov Sergey 		vsel = "18V";
107e025273bSKozlov Sergey 		break;
108e025273bSKozlov Sergey 	default:
109e025273bSKozlov Sergey 		return -EINVAL;
110e025273bSKozlov Sergey 	}
111e025273bSKozlov Sergey 	priv->config[1] = data1_reg;
112e025273bSKozlov Sergey 	dev_dbg(&priv->i2c->dev,
113e025273bSKozlov Sergey 		"%s(): %s, I2C 0x%x write [ %02x %02x %02x ]\n",
114e025273bSKozlov Sergey 		__func__, vsel, priv->i2c_address,
115e025273bSKozlov Sergey 		priv->config[0], priv->config[1], priv->config[2]);
116e025273bSKozlov Sergey 	ret = i2c_transfer(priv->i2c, &msg, 1);
117e025273bSKozlov Sergey 	if (ret >= 0 && ret != 1)
118e025273bSKozlov Sergey 		ret = -EIO;
119e025273bSKozlov Sergey 	if (ret < 0) {
120e025273bSKozlov Sergey 		dev_err(&priv->i2c->dev, "%s(): I2C transfer error (%d)\n",
121e025273bSKozlov Sergey 			__func__, ret);
122e025273bSKozlov Sergey 		return ret;
123e025273bSKozlov Sergey 	}
124e025273bSKozlov Sergey 	if (voltage != SEC_VOLTAGE_OFF) {
125e025273bSKozlov Sergey 		msleep(120);
126e025273bSKozlov Sergey 		ret = lnbh25_read_vmon(priv);
127e025273bSKozlov Sergey 	} else {
128e025273bSKozlov Sergey 		msleep(20);
129e025273bSKozlov Sergey 		ret = 0;
130e025273bSKozlov Sergey 	}
131e025273bSKozlov Sergey 	return ret;
132e025273bSKozlov Sergey }
133e025273bSKozlov Sergey 
lnbh25_release(struct dvb_frontend * fe)134e025273bSKozlov Sergey static void lnbh25_release(struct dvb_frontend *fe)
135e025273bSKozlov Sergey {
136e025273bSKozlov Sergey 	struct lnbh25_priv *priv = fe->sec_priv;
137e025273bSKozlov Sergey 
138e025273bSKozlov Sergey 	dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
139e025273bSKozlov Sergey 	lnbh25_set_voltage(fe, SEC_VOLTAGE_OFF);
140e025273bSKozlov Sergey 	kfree(fe->sec_priv);
141e025273bSKozlov Sergey 	fe->sec_priv = NULL;
142e025273bSKozlov Sergey }
143e025273bSKozlov Sergey 
lnbh25_attach(struct dvb_frontend * fe,struct lnbh25_config * cfg,struct i2c_adapter * i2c)144e025273bSKozlov Sergey struct dvb_frontend *lnbh25_attach(struct dvb_frontend *fe,
145e025273bSKozlov Sergey 				   struct lnbh25_config *cfg,
146e025273bSKozlov Sergey 				   struct i2c_adapter *i2c)
147e025273bSKozlov Sergey {
148e025273bSKozlov Sergey 	struct lnbh25_priv *priv;
149e025273bSKozlov Sergey 
150e025273bSKozlov Sergey 	dev_dbg(&i2c->dev, "%s()\n", __func__);
151e025273bSKozlov Sergey 	priv = kzalloc(sizeof(struct lnbh25_priv), GFP_KERNEL);
152e025273bSKozlov Sergey 	if (!priv)
153e025273bSKozlov Sergey 		return NULL;
154e025273bSKozlov Sergey 	priv->i2c_address = (cfg->i2c_address >> 1);
155e025273bSKozlov Sergey 	priv->i2c = i2c;
156e025273bSKozlov Sergey 	priv->config[0] = 0x02;
157e025273bSKozlov Sergey 	priv->config[1] = 0x00;
158e025273bSKozlov Sergey 	priv->config[2] = cfg->data2_config;
159e025273bSKozlov Sergey 	fe->sec_priv = priv;
160e025273bSKozlov Sergey 	if (lnbh25_set_voltage(fe, SEC_VOLTAGE_OFF)) {
161e025273bSKozlov Sergey 		dev_err(&i2c->dev,
162e025273bSKozlov Sergey 			"%s(): no LNBH25 found at I2C addr 0x%02x\n",
163e025273bSKozlov Sergey 			__func__, priv->i2c_address);
164e025273bSKozlov Sergey 		kfree(priv);
165e025273bSKozlov Sergey 		fe->sec_priv = NULL;
166e025273bSKozlov Sergey 		return NULL;
167e025273bSKozlov Sergey 	}
168e025273bSKozlov Sergey 
169e025273bSKozlov Sergey 	fe->ops.release_sec = lnbh25_release;
170e025273bSKozlov Sergey 	fe->ops.set_voltage = lnbh25_set_voltage;
171e025273bSKozlov Sergey 
172e1bdf024SDaniel Scheller 	dev_info(&i2c->dev, "%s(): attached at I2C addr 0x%02x\n",
173e025273bSKozlov Sergey 		__func__, priv->i2c_address);
174e025273bSKozlov Sergey 	return fe;
175e025273bSKozlov Sergey }
176*86495af1SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(lnbh25_attach);
177e025273bSKozlov Sergey 
178e025273bSKozlov Sergey MODULE_DESCRIPTION("ST LNBH25 driver");
179e025273bSKozlov Sergey MODULE_AUTHOR("info@netup.ru");
180e025273bSKozlov Sergey MODULE_LICENSE("GPL");
181