cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

lnbh25.c (4117B)


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