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