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