18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Earthsoft PT3 driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/delay.h> 88c2ecf20Sopenharmony_ci#include <linux/device.h> 98c2ecf20Sopenharmony_ci#include <linux/i2c.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/pci.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "pt3.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define PT3_I2C_BASE 2048 168c2ecf20Sopenharmony_ci#define PT3_CMD_ADDR_NORMAL 0 178c2ecf20Sopenharmony_ci#define PT3_CMD_ADDR_INIT_DEMOD 4096 188c2ecf20Sopenharmony_ci#define PT3_CMD_ADDR_INIT_TUNER (4096 + 2042) 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* masks for I2C status register */ 218c2ecf20Sopenharmony_ci#define STAT_SEQ_RUNNING 0x1 228c2ecf20Sopenharmony_ci#define STAT_SEQ_ERROR 0x6 238c2ecf20Sopenharmony_ci#define STAT_NO_SEQ 0x8 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define PT3_I2C_RUN (1 << 16) 268c2ecf20Sopenharmony_ci#define PT3_I2C_RESET (1 << 17) 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cienum ctl_cmd { 298c2ecf20Sopenharmony_ci I_END, 308c2ecf20Sopenharmony_ci I_ADDRESS, 318c2ecf20Sopenharmony_ci I_CLOCK_L, 328c2ecf20Sopenharmony_ci I_CLOCK_H, 338c2ecf20Sopenharmony_ci I_DATA_L, 348c2ecf20Sopenharmony_ci I_DATA_H, 358c2ecf20Sopenharmony_ci I_RESET, 368c2ecf20Sopenharmony_ci I_SLEEP, 378c2ecf20Sopenharmony_ci I_DATA_L_NOP = 0x08, 388c2ecf20Sopenharmony_ci I_DATA_H_NOP = 0x0c, 398c2ecf20Sopenharmony_ci I_DATA_H_READ = 0x0d, 408c2ecf20Sopenharmony_ci I_DATA_H_ACK0 = 0x0e, 418c2ecf20Sopenharmony_ci I_DATA_H_ACK1 = 0x0f, 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic void cmdbuf_add(struct pt3_i2cbuf *cbuf, enum ctl_cmd cmd) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci int buf_idx; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci if ((cbuf->num_cmds % 2) == 0) 508c2ecf20Sopenharmony_ci cbuf->tmp = cmd; 518c2ecf20Sopenharmony_ci else { 528c2ecf20Sopenharmony_ci cbuf->tmp |= cmd << 4; 538c2ecf20Sopenharmony_ci buf_idx = cbuf->num_cmds / 2; 548c2ecf20Sopenharmony_ci if (buf_idx < ARRAY_SIZE(cbuf->data)) 558c2ecf20Sopenharmony_ci cbuf->data[buf_idx] = cbuf->tmp; 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci cbuf->num_cmds++; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic void put_end(struct pt3_i2cbuf *cbuf) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci cmdbuf_add(cbuf, I_END); 638c2ecf20Sopenharmony_ci if (cbuf->num_cmds % 2) 648c2ecf20Sopenharmony_ci cmdbuf_add(cbuf, I_END); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic void put_start(struct pt3_i2cbuf *cbuf) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci cmdbuf_add(cbuf, I_DATA_H); 708c2ecf20Sopenharmony_ci cmdbuf_add(cbuf, I_CLOCK_H); 718c2ecf20Sopenharmony_ci cmdbuf_add(cbuf, I_DATA_L); 728c2ecf20Sopenharmony_ci cmdbuf_add(cbuf, I_CLOCK_L); 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic void put_byte_write(struct pt3_i2cbuf *cbuf, u8 val) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci u8 mask; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci for (mask = 0x80; mask > 0; mask >>= 1) 808c2ecf20Sopenharmony_ci cmdbuf_add(cbuf, (val & mask) ? I_DATA_H_NOP : I_DATA_L_NOP); 818c2ecf20Sopenharmony_ci cmdbuf_add(cbuf, I_DATA_H_ACK0); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic void put_byte_read(struct pt3_i2cbuf *cbuf, u32 size) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci int i, j; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci for (i = 0; i < size; i++) { 898c2ecf20Sopenharmony_ci for (j = 0; j < 8; j++) 908c2ecf20Sopenharmony_ci cmdbuf_add(cbuf, I_DATA_H_READ); 918c2ecf20Sopenharmony_ci cmdbuf_add(cbuf, (i == size - 1) ? I_DATA_H_NOP : I_DATA_L_NOP); 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic void put_stop(struct pt3_i2cbuf *cbuf) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci cmdbuf_add(cbuf, I_DATA_L); 988c2ecf20Sopenharmony_ci cmdbuf_add(cbuf, I_CLOCK_H); 998c2ecf20Sopenharmony_ci cmdbuf_add(cbuf, I_DATA_H); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* translates msgs to internal commands for bit-banging */ 1048c2ecf20Sopenharmony_cistatic void translate(struct pt3_i2cbuf *cbuf, struct i2c_msg *msgs, int num) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci int i, j; 1078c2ecf20Sopenharmony_ci bool rd; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci cbuf->num_cmds = 0; 1108c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 1118c2ecf20Sopenharmony_ci rd = !!(msgs[i].flags & I2C_M_RD); 1128c2ecf20Sopenharmony_ci put_start(cbuf); 1138c2ecf20Sopenharmony_ci put_byte_write(cbuf, msgs[i].addr << 1 | rd); 1148c2ecf20Sopenharmony_ci if (rd) 1158c2ecf20Sopenharmony_ci put_byte_read(cbuf, msgs[i].len); 1168c2ecf20Sopenharmony_ci else 1178c2ecf20Sopenharmony_ci for (j = 0; j < msgs[i].len; j++) 1188c2ecf20Sopenharmony_ci put_byte_write(cbuf, msgs[i].buf[j]); 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci if (num > 0) { 1218c2ecf20Sopenharmony_ci put_stop(cbuf); 1228c2ecf20Sopenharmony_ci put_end(cbuf); 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic int wait_i2c_result(struct pt3_board *pt3, u32 *result, int max_wait) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci int i; 1298c2ecf20Sopenharmony_ci u32 v; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci for (i = 0; i < max_wait; i++) { 1328c2ecf20Sopenharmony_ci v = ioread32(pt3->regs[0] + REG_I2C_R); 1338c2ecf20Sopenharmony_ci if (!(v & STAT_SEQ_RUNNING)) 1348c2ecf20Sopenharmony_ci break; 1358c2ecf20Sopenharmony_ci usleep_range(500, 750); 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci if (i >= max_wait) 1388c2ecf20Sopenharmony_ci return -EIO; 1398c2ecf20Sopenharmony_ci if (result) 1408c2ecf20Sopenharmony_ci *result = v; 1418c2ecf20Sopenharmony_ci return 0; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/* send [pre-]translated i2c msgs stored at addr */ 1458c2ecf20Sopenharmony_cistatic int send_i2c_cmd(struct pt3_board *pt3, u32 addr) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci u32 ret; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* make sure that previous transactions had finished */ 1508c2ecf20Sopenharmony_ci if (wait_i2c_result(pt3, NULL, 50)) { 1518c2ecf20Sopenharmony_ci dev_warn(&pt3->pdev->dev, "(%s) prev. transaction stalled\n", 1528c2ecf20Sopenharmony_ci __func__); 1538c2ecf20Sopenharmony_ci return -EIO; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci iowrite32(PT3_I2C_RUN | addr, pt3->regs[0] + REG_I2C_W); 1578c2ecf20Sopenharmony_ci usleep_range(200, 300); 1588c2ecf20Sopenharmony_ci /* wait for the current transaction to finish */ 1598c2ecf20Sopenharmony_ci if (wait_i2c_result(pt3, &ret, 500) || (ret & STAT_SEQ_ERROR)) { 1608c2ecf20Sopenharmony_ci dev_warn(&pt3->pdev->dev, "(%s) failed.\n", __func__); 1618c2ecf20Sopenharmony_ci return -EIO; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci/* init commands for each demod are combined into one transaction 1688c2ecf20Sopenharmony_ci * and hidden in ROM with the address PT3_CMD_ADDR_INIT_DEMOD. 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_ciint pt3_init_all_demods(struct pt3_board *pt3) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci ioread32(pt3->regs[0] + REG_I2C_R); 1738c2ecf20Sopenharmony_ci return send_i2c_cmd(pt3, PT3_CMD_ADDR_INIT_DEMOD); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci/* init commands for two ISDB-T tuners are hidden in ROM. */ 1778c2ecf20Sopenharmony_ciint pt3_init_all_mxl301rf(struct pt3_board *pt3) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 1808c2ecf20Sopenharmony_ci return send_i2c_cmd(pt3, PT3_CMD_ADDR_INIT_TUNER); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_civoid pt3_i2c_reset(struct pt3_board *pt3) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci iowrite32(PT3_I2C_RESET, pt3->regs[0] + REG_I2C_W); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci/* 1898c2ecf20Sopenharmony_ci * I2C algorithm 1908c2ecf20Sopenharmony_ci */ 1918c2ecf20Sopenharmony_ciint 1928c2ecf20Sopenharmony_cipt3_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci struct pt3_board *pt3; 1958c2ecf20Sopenharmony_ci struct pt3_i2cbuf *cbuf; 1968c2ecf20Sopenharmony_ci int i; 1978c2ecf20Sopenharmony_ci void __iomem *p; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci pt3 = i2c_get_adapdata(adap); 2008c2ecf20Sopenharmony_ci cbuf = pt3->i2c_buf; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) 2038c2ecf20Sopenharmony_ci if (msgs[i].flags & I2C_M_RECV_LEN) { 2048c2ecf20Sopenharmony_ci dev_warn(&pt3->pdev->dev, 2058c2ecf20Sopenharmony_ci "(%s) I2C_M_RECV_LEN not supported.\n", 2068c2ecf20Sopenharmony_ci __func__); 2078c2ecf20Sopenharmony_ci return -EINVAL; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci translate(cbuf, msgs, num); 2118c2ecf20Sopenharmony_ci memcpy_toio(pt3->regs[1] + PT3_I2C_BASE + PT3_CMD_ADDR_NORMAL / 2, 2128c2ecf20Sopenharmony_ci cbuf->data, cbuf->num_cmds); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (send_i2c_cmd(pt3, PT3_CMD_ADDR_NORMAL) < 0) 2158c2ecf20Sopenharmony_ci return -EIO; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci p = pt3->regs[1] + PT3_I2C_BASE; 2188c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) 2198c2ecf20Sopenharmony_ci if ((msgs[i].flags & I2C_M_RD) && msgs[i].len > 0) { 2208c2ecf20Sopenharmony_ci memcpy_fromio(msgs[i].buf, p, msgs[i].len); 2218c2ecf20Sopenharmony_ci p += msgs[i].len; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci return num; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ciu32 pt3_i2c_functionality(struct i2c_adapter *adap) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci return I2C_FUNC_I2C; 2308c2ecf20Sopenharmony_ci} 231