162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Earthsoft PT3 driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/delay.h> 862306a36Sopenharmony_ci#include <linux/device.h> 962306a36Sopenharmony_ci#include <linux/i2c.h> 1062306a36Sopenharmony_ci#include <linux/io.h> 1162306a36Sopenharmony_ci#include <linux/pci.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "pt3.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define PT3_I2C_BASE 2048 1662306a36Sopenharmony_ci#define PT3_CMD_ADDR_NORMAL 0 1762306a36Sopenharmony_ci#define PT3_CMD_ADDR_INIT_DEMOD 4096 1862306a36Sopenharmony_ci#define PT3_CMD_ADDR_INIT_TUNER (4096 + 2042) 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* masks for I2C status register */ 2162306a36Sopenharmony_ci#define STAT_SEQ_RUNNING 0x1 2262306a36Sopenharmony_ci#define STAT_SEQ_ERROR 0x6 2362306a36Sopenharmony_ci#define STAT_NO_SEQ 0x8 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define PT3_I2C_RUN (1 << 16) 2662306a36Sopenharmony_ci#define PT3_I2C_RESET (1 << 17) 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cienum ctl_cmd { 2962306a36Sopenharmony_ci I_END, 3062306a36Sopenharmony_ci I_ADDRESS, 3162306a36Sopenharmony_ci I_CLOCK_L, 3262306a36Sopenharmony_ci I_CLOCK_H, 3362306a36Sopenharmony_ci I_DATA_L, 3462306a36Sopenharmony_ci I_DATA_H, 3562306a36Sopenharmony_ci I_RESET, 3662306a36Sopenharmony_ci I_SLEEP, 3762306a36Sopenharmony_ci I_DATA_L_NOP = 0x08, 3862306a36Sopenharmony_ci I_DATA_H_NOP = 0x0c, 3962306a36Sopenharmony_ci I_DATA_H_READ = 0x0d, 4062306a36Sopenharmony_ci I_DATA_H_ACK0 = 0x0e, 4162306a36Sopenharmony_ci I_DATA_H_ACK1 = 0x0f, 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic void cmdbuf_add(struct pt3_i2cbuf *cbuf, enum ctl_cmd cmd) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci int buf_idx; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if ((cbuf->num_cmds % 2) == 0) 5062306a36Sopenharmony_ci cbuf->tmp = cmd; 5162306a36Sopenharmony_ci else { 5262306a36Sopenharmony_ci cbuf->tmp |= cmd << 4; 5362306a36Sopenharmony_ci buf_idx = cbuf->num_cmds / 2; 5462306a36Sopenharmony_ci if (buf_idx < ARRAY_SIZE(cbuf->data)) 5562306a36Sopenharmony_ci cbuf->data[buf_idx] = cbuf->tmp; 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci cbuf->num_cmds++; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic void put_end(struct pt3_i2cbuf *cbuf) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci cmdbuf_add(cbuf, I_END); 6362306a36Sopenharmony_ci if (cbuf->num_cmds % 2) 6462306a36Sopenharmony_ci cmdbuf_add(cbuf, I_END); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic void put_start(struct pt3_i2cbuf *cbuf) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci cmdbuf_add(cbuf, I_DATA_H); 7062306a36Sopenharmony_ci cmdbuf_add(cbuf, I_CLOCK_H); 7162306a36Sopenharmony_ci cmdbuf_add(cbuf, I_DATA_L); 7262306a36Sopenharmony_ci cmdbuf_add(cbuf, I_CLOCK_L); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic void put_byte_write(struct pt3_i2cbuf *cbuf, u8 val) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci u8 mask; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci for (mask = 0x80; mask > 0; mask >>= 1) 8062306a36Sopenharmony_ci cmdbuf_add(cbuf, (val & mask) ? I_DATA_H_NOP : I_DATA_L_NOP); 8162306a36Sopenharmony_ci cmdbuf_add(cbuf, I_DATA_H_ACK0); 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic void put_byte_read(struct pt3_i2cbuf *cbuf, u32 size) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci int i, j; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci for (i = 0; i < size; i++) { 8962306a36Sopenharmony_ci for (j = 0; j < 8; j++) 9062306a36Sopenharmony_ci cmdbuf_add(cbuf, I_DATA_H_READ); 9162306a36Sopenharmony_ci cmdbuf_add(cbuf, (i == size - 1) ? I_DATA_H_NOP : I_DATA_L_NOP); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic void put_stop(struct pt3_i2cbuf *cbuf) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci cmdbuf_add(cbuf, I_DATA_L); 9862306a36Sopenharmony_ci cmdbuf_add(cbuf, I_CLOCK_H); 9962306a36Sopenharmony_ci cmdbuf_add(cbuf, I_DATA_H); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/* translates msgs to internal commands for bit-banging */ 10462306a36Sopenharmony_cistatic void translate(struct pt3_i2cbuf *cbuf, struct i2c_msg *msgs, int num) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci int i, j; 10762306a36Sopenharmony_ci bool rd; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci cbuf->num_cmds = 0; 11062306a36Sopenharmony_ci for (i = 0; i < num; i++) { 11162306a36Sopenharmony_ci rd = !!(msgs[i].flags & I2C_M_RD); 11262306a36Sopenharmony_ci put_start(cbuf); 11362306a36Sopenharmony_ci put_byte_write(cbuf, msgs[i].addr << 1 | rd); 11462306a36Sopenharmony_ci if (rd) 11562306a36Sopenharmony_ci put_byte_read(cbuf, msgs[i].len); 11662306a36Sopenharmony_ci else 11762306a36Sopenharmony_ci for (j = 0; j < msgs[i].len; j++) 11862306a36Sopenharmony_ci put_byte_write(cbuf, msgs[i].buf[j]); 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci if (num > 0) { 12162306a36Sopenharmony_ci put_stop(cbuf); 12262306a36Sopenharmony_ci put_end(cbuf); 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic int wait_i2c_result(struct pt3_board *pt3, u32 *result, int max_wait) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci int i; 12962306a36Sopenharmony_ci u32 v; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci for (i = 0; i < max_wait; i++) { 13262306a36Sopenharmony_ci v = ioread32(pt3->regs[0] + REG_I2C_R); 13362306a36Sopenharmony_ci if (!(v & STAT_SEQ_RUNNING)) 13462306a36Sopenharmony_ci break; 13562306a36Sopenharmony_ci usleep_range(500, 750); 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci if (i >= max_wait) 13862306a36Sopenharmony_ci return -EIO; 13962306a36Sopenharmony_ci if (result) 14062306a36Sopenharmony_ci *result = v; 14162306a36Sopenharmony_ci return 0; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/* send [pre-]translated i2c msgs stored at addr */ 14562306a36Sopenharmony_cistatic int send_i2c_cmd(struct pt3_board *pt3, u32 addr) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci u32 ret; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* make sure that previous transactions had finished */ 15062306a36Sopenharmony_ci if (wait_i2c_result(pt3, NULL, 50)) { 15162306a36Sopenharmony_ci dev_warn(&pt3->pdev->dev, "(%s) prev. transaction stalled\n", 15262306a36Sopenharmony_ci __func__); 15362306a36Sopenharmony_ci return -EIO; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci iowrite32(PT3_I2C_RUN | addr, pt3->regs[0] + REG_I2C_W); 15762306a36Sopenharmony_ci usleep_range(200, 300); 15862306a36Sopenharmony_ci /* wait for the current transaction to finish */ 15962306a36Sopenharmony_ci if (wait_i2c_result(pt3, &ret, 500) || (ret & STAT_SEQ_ERROR)) { 16062306a36Sopenharmony_ci dev_warn(&pt3->pdev->dev, "(%s) failed.\n", __func__); 16162306a36Sopenharmony_ci return -EIO; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci/* init commands for each demod are combined into one transaction 16862306a36Sopenharmony_ci * and hidden in ROM with the address PT3_CMD_ADDR_INIT_DEMOD. 16962306a36Sopenharmony_ci */ 17062306a36Sopenharmony_ciint pt3_init_all_demods(struct pt3_board *pt3) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci ioread32(pt3->regs[0] + REG_I2C_R); 17362306a36Sopenharmony_ci return send_i2c_cmd(pt3, PT3_CMD_ADDR_INIT_DEMOD); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci/* init commands for two ISDB-T tuners are hidden in ROM. */ 17762306a36Sopenharmony_ciint pt3_init_all_mxl301rf(struct pt3_board *pt3) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci usleep_range(1000, 2000); 18062306a36Sopenharmony_ci return send_i2c_cmd(pt3, PT3_CMD_ADDR_INIT_TUNER); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_civoid pt3_i2c_reset(struct pt3_board *pt3) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci iowrite32(PT3_I2C_RESET, pt3->regs[0] + REG_I2C_W); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci/* 18962306a36Sopenharmony_ci * I2C algorithm 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_ciint 19262306a36Sopenharmony_cipt3_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct pt3_board *pt3; 19562306a36Sopenharmony_ci struct pt3_i2cbuf *cbuf; 19662306a36Sopenharmony_ci int i; 19762306a36Sopenharmony_ci void __iomem *p; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci pt3 = i2c_get_adapdata(adap); 20062306a36Sopenharmony_ci cbuf = pt3->i2c_buf; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci for (i = 0; i < num; i++) 20362306a36Sopenharmony_ci if (msgs[i].flags & I2C_M_RECV_LEN) { 20462306a36Sopenharmony_ci dev_warn(&pt3->pdev->dev, 20562306a36Sopenharmony_ci "(%s) I2C_M_RECV_LEN not supported.\n", 20662306a36Sopenharmony_ci __func__); 20762306a36Sopenharmony_ci return -EINVAL; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci translate(cbuf, msgs, num); 21162306a36Sopenharmony_ci memcpy_toio(pt3->regs[1] + PT3_I2C_BASE + PT3_CMD_ADDR_NORMAL / 2, 21262306a36Sopenharmony_ci cbuf->data, cbuf->num_cmds); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (send_i2c_cmd(pt3, PT3_CMD_ADDR_NORMAL) < 0) 21562306a36Sopenharmony_ci return -EIO; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci p = pt3->regs[1] + PT3_I2C_BASE; 21862306a36Sopenharmony_ci for (i = 0; i < num; i++) 21962306a36Sopenharmony_ci if ((msgs[i].flags & I2C_M_RD) && msgs[i].len > 0) { 22062306a36Sopenharmony_ci memcpy_fromio(msgs[i].buf, p, msgs[i].len); 22162306a36Sopenharmony_ci p += msgs[i].len; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci return num; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ciu32 pt3_i2c_functionality(struct i2c_adapter *adap) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci return I2C_FUNC_I2C; 23062306a36Sopenharmony_ci} 231