18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Silicon Labs C8051F300 microcontroller. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * It is used for LNB power control in TeVii S470, 68c2ecf20Sopenharmony_ci * TBS 6920 PCIe DVB-S2 cards. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Microcontroller connected to cx23885 GPIO pins: 98c2ecf20Sopenharmony_ci * GPIO0 - data - P0.3 F300 108c2ecf20Sopenharmony_ci * GPIO1 - reset - P0.2 F300 118c2ecf20Sopenharmony_ci * GPIO2 - clk - P0.1 F300 128c2ecf20Sopenharmony_ci * GPIO3 - busy - P0.0 F300 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Copyright (C) 2009 Igor M. Liplianin <liplianin@me.by> 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "cx23885.h" 188c2ecf20Sopenharmony_ci#include "cx23885-f300.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define F300_DATA GPIO_0 218c2ecf20Sopenharmony_ci#define F300_RESET GPIO_1 228c2ecf20Sopenharmony_ci#define F300_CLK GPIO_2 238c2ecf20Sopenharmony_ci#define F300_BUSY GPIO_3 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic void f300_set_line(struct cx23885_dev *dev, u32 line, u8 lvl) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci cx23885_gpio_enable(dev, line, 1); 288c2ecf20Sopenharmony_ci if (lvl == 1) 298c2ecf20Sopenharmony_ci cx23885_gpio_set(dev, line); 308c2ecf20Sopenharmony_ci else 318c2ecf20Sopenharmony_ci cx23885_gpio_clear(dev, line); 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic u8 f300_get_line(struct cx23885_dev *dev, u32 line) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci cx23885_gpio_enable(dev, line, 0); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci return cx23885_gpio_get(dev, line); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic void f300_send_byte(struct cx23885_dev *dev, u8 dta) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci u8 i; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 468c2ecf20Sopenharmony_ci f300_set_line(dev, F300_CLK, 0); 478c2ecf20Sopenharmony_ci udelay(30); 488c2ecf20Sopenharmony_ci f300_set_line(dev, F300_DATA, (dta & 0x80) >> 7);/* msb first */ 498c2ecf20Sopenharmony_ci udelay(30); 508c2ecf20Sopenharmony_ci dta <<= 1; 518c2ecf20Sopenharmony_ci f300_set_line(dev, F300_CLK, 1); 528c2ecf20Sopenharmony_ci udelay(30); 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic u8 f300_get_byte(struct cx23885_dev *dev) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci u8 i, dta = 0; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 618c2ecf20Sopenharmony_ci f300_set_line(dev, F300_CLK, 0); 628c2ecf20Sopenharmony_ci udelay(30); 638c2ecf20Sopenharmony_ci dta <<= 1; 648c2ecf20Sopenharmony_ci f300_set_line(dev, F300_CLK, 1); 658c2ecf20Sopenharmony_ci udelay(30); 668c2ecf20Sopenharmony_ci dta |= f300_get_line(dev, F300_DATA);/* msb first */ 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci return dta; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic u8 f300_xfer(struct dvb_frontend *fe, u8 *buf) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct cx23885_tsport *port = fe->dvb->priv; 768c2ecf20Sopenharmony_ci struct cx23885_dev *dev = port->dev; 778c2ecf20Sopenharmony_ci u8 i, temp, ret = 0; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci temp = buf[0]; 808c2ecf20Sopenharmony_ci for (i = 0; i < buf[0]; i++) 818c2ecf20Sopenharmony_ci temp += buf[i + 1]; 828c2ecf20Sopenharmony_ci temp = (~temp + 1);/* get check sum */ 838c2ecf20Sopenharmony_ci buf[1 + buf[0]] = temp; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci f300_set_line(dev, F300_RESET, 1); 868c2ecf20Sopenharmony_ci f300_set_line(dev, F300_CLK, 1); 878c2ecf20Sopenharmony_ci udelay(30); 888c2ecf20Sopenharmony_ci f300_set_line(dev, F300_DATA, 1); 898c2ecf20Sopenharmony_ci msleep(1); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci /* question: */ 928c2ecf20Sopenharmony_ci f300_set_line(dev, F300_RESET, 0);/* begin to send data */ 938c2ecf20Sopenharmony_ci msleep(1); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci f300_send_byte(dev, 0xe0);/* the slave address is 0xe0, write */ 968c2ecf20Sopenharmony_ci msleep(1); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci temp = buf[0]; 998c2ecf20Sopenharmony_ci temp += 2; 1008c2ecf20Sopenharmony_ci for (i = 0; i < temp; i++) 1018c2ecf20Sopenharmony_ci f300_send_byte(dev, buf[i]); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci f300_set_line(dev, F300_RESET, 1);/* sent data over */ 1048c2ecf20Sopenharmony_ci f300_set_line(dev, F300_DATA, 1); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci /* answer: */ 1078c2ecf20Sopenharmony_ci temp = 0; 1088c2ecf20Sopenharmony_ci for (i = 0; ((i < 8) & (temp == 0)); i++) { 1098c2ecf20Sopenharmony_ci msleep(1); 1108c2ecf20Sopenharmony_ci if (f300_get_line(dev, F300_BUSY) == 0) 1118c2ecf20Sopenharmony_ci temp = 1; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (i > 7) { 1158c2ecf20Sopenharmony_ci pr_err("%s: timeout, the slave no response\n", 1168c2ecf20Sopenharmony_ci __func__); 1178c2ecf20Sopenharmony_ci ret = 1; /* timeout, the slave no response */ 1188c2ecf20Sopenharmony_ci } else { /* the slave not busy, prepare for getting data */ 1198c2ecf20Sopenharmony_ci f300_set_line(dev, F300_RESET, 0);/*ready...*/ 1208c2ecf20Sopenharmony_ci msleep(1); 1218c2ecf20Sopenharmony_ci f300_send_byte(dev, 0xe1);/* 0xe1 is Read */ 1228c2ecf20Sopenharmony_ci msleep(1); 1238c2ecf20Sopenharmony_ci temp = f300_get_byte(dev);/*get the data length */ 1248c2ecf20Sopenharmony_ci if (temp > 14) 1258c2ecf20Sopenharmony_ci temp = 14; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci for (i = 0; i < (temp + 1); i++) 1288c2ecf20Sopenharmony_ci f300_get_byte(dev);/* get data to empty buffer */ 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci f300_set_line(dev, F300_RESET, 1);/* received data over */ 1318c2ecf20Sopenharmony_ci f300_set_line(dev, F300_DATA, 1); 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci return ret; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ciint f300_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage voltage) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci u8 buf[16]; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci buf[0] = 0x05; 1428c2ecf20Sopenharmony_ci buf[1] = 0x38;/* write port */ 1438c2ecf20Sopenharmony_ci buf[2] = 0x01;/* A port, lnb power */ 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci switch (voltage) { 1468c2ecf20Sopenharmony_ci case SEC_VOLTAGE_13: 1478c2ecf20Sopenharmony_ci buf[3] = 0x01;/* power on */ 1488c2ecf20Sopenharmony_ci buf[4] = 0x02;/* B port, H/V */ 1498c2ecf20Sopenharmony_ci buf[5] = 0x00;/*13V v*/ 1508c2ecf20Sopenharmony_ci break; 1518c2ecf20Sopenharmony_ci case SEC_VOLTAGE_18: 1528c2ecf20Sopenharmony_ci buf[3] = 0x01; 1538c2ecf20Sopenharmony_ci buf[4] = 0x02; 1548c2ecf20Sopenharmony_ci buf[5] = 0x01;/* 18V h*/ 1558c2ecf20Sopenharmony_ci break; 1568c2ecf20Sopenharmony_ci case SEC_VOLTAGE_OFF: 1578c2ecf20Sopenharmony_ci buf[3] = 0x00;/* power off */ 1588c2ecf20Sopenharmony_ci buf[4] = 0x00; 1598c2ecf20Sopenharmony_ci buf[5] = 0x00; 1608c2ecf20Sopenharmony_ci break; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci return f300_xfer(fe, buf); 1648c2ecf20Sopenharmony_ci} 165