162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Driver for Silicon Labs C8051F300 microcontroller.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * It is used for LNB power control in TeVii S470,
662306a36Sopenharmony_ci * TBS 6920 PCIe DVB-S2 cards.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Microcontroller connected to cx23885 GPIO pins:
962306a36Sopenharmony_ci * GPIO0 - data		- P0.3 F300
1062306a36Sopenharmony_ci * GPIO1 - reset	- P0.2 F300
1162306a36Sopenharmony_ci * GPIO2 - clk		- P0.1 F300
1262306a36Sopenharmony_ci * GPIO3 - busy		- P0.0 F300
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * Copyright (C) 2009 Igor M. Liplianin <liplianin@me.by>
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "cx23885.h"
1862306a36Sopenharmony_ci#include "cx23885-f300.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define F300_DATA	GPIO_0
2162306a36Sopenharmony_ci#define F300_RESET	GPIO_1
2262306a36Sopenharmony_ci#define F300_CLK	GPIO_2
2362306a36Sopenharmony_ci#define F300_BUSY	GPIO_3
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic void f300_set_line(struct cx23885_dev *dev, u32 line, u8 lvl)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	cx23885_gpio_enable(dev, line, 1);
2862306a36Sopenharmony_ci	if (lvl == 1)
2962306a36Sopenharmony_ci		cx23885_gpio_set(dev, line);
3062306a36Sopenharmony_ci	else
3162306a36Sopenharmony_ci		cx23885_gpio_clear(dev, line);
3262306a36Sopenharmony_ci}
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic u8 f300_get_line(struct cx23885_dev *dev, u32 line)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	cx23885_gpio_enable(dev, line, 0);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	return cx23885_gpio_get(dev, line);
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic void f300_send_byte(struct cx23885_dev *dev, u8 dta)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	u8 i;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
4662306a36Sopenharmony_ci		f300_set_line(dev, F300_CLK, 0);
4762306a36Sopenharmony_ci		udelay(30);
4862306a36Sopenharmony_ci		f300_set_line(dev, F300_DATA, (dta & 0x80) >> 7);/* msb first */
4962306a36Sopenharmony_ci		udelay(30);
5062306a36Sopenharmony_ci		dta <<= 1;
5162306a36Sopenharmony_ci		f300_set_line(dev, F300_CLK, 1);
5262306a36Sopenharmony_ci		udelay(30);
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic u8 f300_get_byte(struct cx23885_dev *dev)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	u8 i, dta = 0;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
6162306a36Sopenharmony_ci		f300_set_line(dev, F300_CLK, 0);
6262306a36Sopenharmony_ci		udelay(30);
6362306a36Sopenharmony_ci		dta <<= 1;
6462306a36Sopenharmony_ci		f300_set_line(dev, F300_CLK, 1);
6562306a36Sopenharmony_ci		udelay(30);
6662306a36Sopenharmony_ci		dta |= f300_get_line(dev, F300_DATA);/* msb first */
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	return dta;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic u8 f300_xfer(struct dvb_frontend *fe, u8 *buf)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct cx23885_tsport *port = fe->dvb->priv;
7662306a36Sopenharmony_ci	struct cx23885_dev *dev = port->dev;
7762306a36Sopenharmony_ci	u8 i, temp, ret = 0;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	temp = buf[0];
8062306a36Sopenharmony_ci	for (i = 0; i < buf[0]; i++)
8162306a36Sopenharmony_ci		temp += buf[i + 1];
8262306a36Sopenharmony_ci	temp = (~temp + 1);/* get check sum */
8362306a36Sopenharmony_ci	buf[1 + buf[0]] = temp;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	f300_set_line(dev, F300_RESET, 1);
8662306a36Sopenharmony_ci	f300_set_line(dev, F300_CLK, 1);
8762306a36Sopenharmony_ci	udelay(30);
8862306a36Sopenharmony_ci	f300_set_line(dev, F300_DATA, 1);
8962306a36Sopenharmony_ci	msleep(1);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	/* question: */
9262306a36Sopenharmony_ci	f300_set_line(dev, F300_RESET, 0);/* begin to send data */
9362306a36Sopenharmony_ci	msleep(1);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	f300_send_byte(dev, 0xe0);/* the slave address is 0xe0, write */
9662306a36Sopenharmony_ci	msleep(1);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	temp = buf[0];
9962306a36Sopenharmony_ci	temp += 2;
10062306a36Sopenharmony_ci	for (i = 0; i < temp; i++)
10162306a36Sopenharmony_ci		f300_send_byte(dev, buf[i]);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	f300_set_line(dev, F300_RESET, 1);/* sent data over */
10462306a36Sopenharmony_ci	f300_set_line(dev, F300_DATA, 1);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	/* answer: */
10762306a36Sopenharmony_ci	temp = 0;
10862306a36Sopenharmony_ci	for (i = 0; ((i < 8) & (temp == 0)); i++) {
10962306a36Sopenharmony_ci		msleep(1);
11062306a36Sopenharmony_ci		if (f300_get_line(dev, F300_BUSY) == 0)
11162306a36Sopenharmony_ci			temp = 1;
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	if (i > 7) {
11562306a36Sopenharmony_ci		pr_err("%s: timeout, the slave no response\n",
11662306a36Sopenharmony_ci								__func__);
11762306a36Sopenharmony_ci		ret = 1; /* timeout, the slave no response */
11862306a36Sopenharmony_ci	} else { /* the slave not busy, prepare for getting data */
11962306a36Sopenharmony_ci		f300_set_line(dev, F300_RESET, 0);/*ready...*/
12062306a36Sopenharmony_ci		msleep(1);
12162306a36Sopenharmony_ci		f300_send_byte(dev, 0xe1);/* 0xe1 is Read */
12262306a36Sopenharmony_ci		msleep(1);
12362306a36Sopenharmony_ci		temp = f300_get_byte(dev);/*get the data length */
12462306a36Sopenharmony_ci		if (temp > 14)
12562306a36Sopenharmony_ci			temp = 14;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci		for (i = 0; i < (temp + 1); i++)
12862306a36Sopenharmony_ci			f300_get_byte(dev);/* get data to empty buffer */
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci		f300_set_line(dev, F300_RESET, 1);/* received data over */
13162306a36Sopenharmony_ci		f300_set_line(dev, F300_DATA, 1);
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	return ret;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ciint f300_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage voltage)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	u8 buf[16];
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	buf[0] = 0x05;
14262306a36Sopenharmony_ci	buf[1] = 0x38;/* write port */
14362306a36Sopenharmony_ci	buf[2] = 0x01;/* A port, lnb power */
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	switch (voltage) {
14662306a36Sopenharmony_ci	case SEC_VOLTAGE_13:
14762306a36Sopenharmony_ci		buf[3] = 0x01;/* power on */
14862306a36Sopenharmony_ci		buf[4] = 0x02;/* B port, H/V */
14962306a36Sopenharmony_ci		buf[5] = 0x00;/*13V v*/
15062306a36Sopenharmony_ci		break;
15162306a36Sopenharmony_ci	case SEC_VOLTAGE_18:
15262306a36Sopenharmony_ci		buf[3] = 0x01;
15362306a36Sopenharmony_ci		buf[4] = 0x02;
15462306a36Sopenharmony_ci		buf[5] = 0x01;/* 18V h*/
15562306a36Sopenharmony_ci		break;
15662306a36Sopenharmony_ci	case SEC_VOLTAGE_OFF:
15762306a36Sopenharmony_ci		buf[3] = 0x00;/* power off */
15862306a36Sopenharmony_ci		buf[4] = 0x00;
15962306a36Sopenharmony_ci		buf[5] = 0x00;
16062306a36Sopenharmony_ci		break;
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	return f300_xfer(fe, buf);
16462306a36Sopenharmony_ci}
165