xref: /openbmc/linux/drivers/media/pci/pt3/pt3_i2c.c (revision 3eb66e91a25497065c5322b1268cbc3953642227)
1*a296f7bfSAkihiro Tsukada // SPDX-License-Identifier: GPL-2.0
2f5a98f37SAkihiro Tsukada /*
3f5a98f37SAkihiro Tsukada  * Earthsoft PT3 driver
4f5a98f37SAkihiro Tsukada  *
5f5a98f37SAkihiro Tsukada  * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
6f5a98f37SAkihiro Tsukada  */
7f5a98f37SAkihiro Tsukada #include <linux/delay.h>
8f5a98f37SAkihiro Tsukada #include <linux/device.h>
9f5a98f37SAkihiro Tsukada #include <linux/i2c.h>
10f5a98f37SAkihiro Tsukada #include <linux/io.h>
11f5a98f37SAkihiro Tsukada #include <linux/pci.h>
12f5a98f37SAkihiro Tsukada 
13f5a98f37SAkihiro Tsukada #include "pt3.h"
14f5a98f37SAkihiro Tsukada 
15f5a98f37SAkihiro Tsukada #define PT3_I2C_BASE  2048
16f5a98f37SAkihiro Tsukada #define PT3_CMD_ADDR_NORMAL 0
17f5a98f37SAkihiro Tsukada #define PT3_CMD_ADDR_INIT_DEMOD  4096
18f5a98f37SAkihiro Tsukada #define PT3_CMD_ADDR_INIT_TUNER  (4096 + 2042)
19f5a98f37SAkihiro Tsukada 
20f5a98f37SAkihiro Tsukada /* masks for I2C status register */
21f5a98f37SAkihiro Tsukada #define STAT_SEQ_RUNNING 0x1
22f5a98f37SAkihiro Tsukada #define STAT_SEQ_ERROR   0x6
23f5a98f37SAkihiro Tsukada #define STAT_NO_SEQ      0x8
24f5a98f37SAkihiro Tsukada 
25f5a98f37SAkihiro Tsukada #define PT3_I2C_RUN   (1 << 16)
26f5a98f37SAkihiro Tsukada #define PT3_I2C_RESET (1 << 17)
27f5a98f37SAkihiro Tsukada 
28f5a98f37SAkihiro Tsukada enum ctl_cmd {
29f5a98f37SAkihiro Tsukada 	I_END,
30f5a98f37SAkihiro Tsukada 	I_ADDRESS,
31f5a98f37SAkihiro Tsukada 	I_CLOCK_L,
32f5a98f37SAkihiro Tsukada 	I_CLOCK_H,
33f5a98f37SAkihiro Tsukada 	I_DATA_L,
34f5a98f37SAkihiro Tsukada 	I_DATA_H,
35f5a98f37SAkihiro Tsukada 	I_RESET,
36f5a98f37SAkihiro Tsukada 	I_SLEEP,
37f5a98f37SAkihiro Tsukada 	I_DATA_L_NOP  = 0x08,
38f5a98f37SAkihiro Tsukada 	I_DATA_H_NOP  = 0x0c,
39f5a98f37SAkihiro Tsukada 	I_DATA_H_READ = 0x0d,
40f5a98f37SAkihiro Tsukada 	I_DATA_H_ACK0 = 0x0e,
41f5a98f37SAkihiro Tsukada 	I_DATA_H_ACK1 = 0x0f,
42f5a98f37SAkihiro Tsukada };
43f5a98f37SAkihiro Tsukada 
44f5a98f37SAkihiro Tsukada 
cmdbuf_add(struct pt3_i2cbuf * cbuf,enum ctl_cmd cmd)45f5a98f37SAkihiro Tsukada static void cmdbuf_add(struct pt3_i2cbuf *cbuf, enum ctl_cmd cmd)
46f5a98f37SAkihiro Tsukada {
47f5a98f37SAkihiro Tsukada 	int buf_idx;
48f5a98f37SAkihiro Tsukada 
49f5a98f37SAkihiro Tsukada 	if ((cbuf->num_cmds % 2) == 0)
50f5a98f37SAkihiro Tsukada 		cbuf->tmp = cmd;
51f5a98f37SAkihiro Tsukada 	else {
52f5a98f37SAkihiro Tsukada 		cbuf->tmp |= cmd << 4;
53f5a98f37SAkihiro Tsukada 		buf_idx = cbuf->num_cmds / 2;
54f5a98f37SAkihiro Tsukada 		if (buf_idx < ARRAY_SIZE(cbuf->data))
55f5a98f37SAkihiro Tsukada 			cbuf->data[buf_idx] = cbuf->tmp;
56f5a98f37SAkihiro Tsukada 	}
57f5a98f37SAkihiro Tsukada 	cbuf->num_cmds++;
58f5a98f37SAkihiro Tsukada }
59f5a98f37SAkihiro Tsukada 
put_end(struct pt3_i2cbuf * cbuf)60f5a98f37SAkihiro Tsukada static void put_end(struct pt3_i2cbuf *cbuf)
61f5a98f37SAkihiro Tsukada {
62f5a98f37SAkihiro Tsukada 	cmdbuf_add(cbuf, I_END);
63f5a98f37SAkihiro Tsukada 	if (cbuf->num_cmds % 2)
64f5a98f37SAkihiro Tsukada 		cmdbuf_add(cbuf, I_END);
65f5a98f37SAkihiro Tsukada }
66f5a98f37SAkihiro Tsukada 
put_start(struct pt3_i2cbuf * cbuf)67f5a98f37SAkihiro Tsukada static void put_start(struct pt3_i2cbuf *cbuf)
68f5a98f37SAkihiro Tsukada {
69f5a98f37SAkihiro Tsukada 	cmdbuf_add(cbuf, I_DATA_H);
70f5a98f37SAkihiro Tsukada 	cmdbuf_add(cbuf, I_CLOCK_H);
71f5a98f37SAkihiro Tsukada 	cmdbuf_add(cbuf, I_DATA_L);
72f5a98f37SAkihiro Tsukada 	cmdbuf_add(cbuf, I_CLOCK_L);
73f5a98f37SAkihiro Tsukada }
74f5a98f37SAkihiro Tsukada 
put_byte_write(struct pt3_i2cbuf * cbuf,u8 val)75f5a98f37SAkihiro Tsukada static void put_byte_write(struct pt3_i2cbuf *cbuf, u8 val)
76f5a98f37SAkihiro Tsukada {
77f5a98f37SAkihiro Tsukada 	u8 mask;
78f5a98f37SAkihiro Tsukada 
79f5a98f37SAkihiro Tsukada 	for (mask = 0x80; mask > 0; mask >>= 1)
80f5a98f37SAkihiro Tsukada 		cmdbuf_add(cbuf, (val & mask) ? I_DATA_H_NOP : I_DATA_L_NOP);
81f5a98f37SAkihiro Tsukada 	cmdbuf_add(cbuf, I_DATA_H_ACK0);
82f5a98f37SAkihiro Tsukada }
83f5a98f37SAkihiro Tsukada 
put_byte_read(struct pt3_i2cbuf * cbuf,u32 size)84f5a98f37SAkihiro Tsukada static void put_byte_read(struct pt3_i2cbuf *cbuf, u32 size)
85f5a98f37SAkihiro Tsukada {
86f5a98f37SAkihiro Tsukada 	int i, j;
87f5a98f37SAkihiro Tsukada 
88f5a98f37SAkihiro Tsukada 	for (i = 0; i < size; i++) {
89f5a98f37SAkihiro Tsukada 		for (j = 0; j < 8; j++)
90f5a98f37SAkihiro Tsukada 			cmdbuf_add(cbuf, I_DATA_H_READ);
91f5a98f37SAkihiro Tsukada 		cmdbuf_add(cbuf, (i == size - 1) ? I_DATA_H_NOP : I_DATA_L_NOP);
92f5a98f37SAkihiro Tsukada 	}
93f5a98f37SAkihiro Tsukada }
94f5a98f37SAkihiro Tsukada 
put_stop(struct pt3_i2cbuf * cbuf)95f5a98f37SAkihiro Tsukada static void put_stop(struct pt3_i2cbuf *cbuf)
96f5a98f37SAkihiro Tsukada {
97f5a98f37SAkihiro Tsukada 	cmdbuf_add(cbuf, I_DATA_L);
98f5a98f37SAkihiro Tsukada 	cmdbuf_add(cbuf, I_CLOCK_H);
99f5a98f37SAkihiro Tsukada 	cmdbuf_add(cbuf, I_DATA_H);
100f5a98f37SAkihiro Tsukada }
101f5a98f37SAkihiro Tsukada 
102f5a98f37SAkihiro Tsukada 
103f5a98f37SAkihiro Tsukada /* translates msgs to internal commands for bit-banging */
translate(struct pt3_i2cbuf * cbuf,struct i2c_msg * msgs,int num)104f5a98f37SAkihiro Tsukada static void translate(struct pt3_i2cbuf *cbuf, struct i2c_msg *msgs, int num)
105f5a98f37SAkihiro Tsukada {
106f5a98f37SAkihiro Tsukada 	int i, j;
107f5a98f37SAkihiro Tsukada 	bool rd;
108f5a98f37SAkihiro Tsukada 
109f5a98f37SAkihiro Tsukada 	cbuf->num_cmds = 0;
110f5a98f37SAkihiro Tsukada 	for (i = 0; i < num; i++) {
111f5a98f37SAkihiro Tsukada 		rd = !!(msgs[i].flags & I2C_M_RD);
112f5a98f37SAkihiro Tsukada 		put_start(cbuf);
113f5a98f37SAkihiro Tsukada 		put_byte_write(cbuf, msgs[i].addr << 1 | rd);
114f5a98f37SAkihiro Tsukada 		if (rd)
115f5a98f37SAkihiro Tsukada 			put_byte_read(cbuf, msgs[i].len);
116f5a98f37SAkihiro Tsukada 		else
117f5a98f37SAkihiro Tsukada 			for (j = 0; j < msgs[i].len; j++)
118f5a98f37SAkihiro Tsukada 				put_byte_write(cbuf, msgs[i].buf[j]);
119f5a98f37SAkihiro Tsukada 	}
120f5a98f37SAkihiro Tsukada 	if (num > 0) {
121f5a98f37SAkihiro Tsukada 		put_stop(cbuf);
122f5a98f37SAkihiro Tsukada 		put_end(cbuf);
123f5a98f37SAkihiro Tsukada 	}
124f5a98f37SAkihiro Tsukada }
125f5a98f37SAkihiro Tsukada 
wait_i2c_result(struct pt3_board * pt3,u32 * result,int max_wait)126f5a98f37SAkihiro Tsukada static int wait_i2c_result(struct pt3_board *pt3, u32 *result, int max_wait)
127f5a98f37SAkihiro Tsukada {
128f5a98f37SAkihiro Tsukada 	int i;
129f5a98f37SAkihiro Tsukada 	u32 v;
130f5a98f37SAkihiro Tsukada 
131f5a98f37SAkihiro Tsukada 	for (i = 0; i < max_wait; i++) {
132f5a98f37SAkihiro Tsukada 		v = ioread32(pt3->regs[0] + REG_I2C_R);
133f5a98f37SAkihiro Tsukada 		if (!(v & STAT_SEQ_RUNNING))
134f5a98f37SAkihiro Tsukada 			break;
135f5a98f37SAkihiro Tsukada 		usleep_range(500, 750);
136f5a98f37SAkihiro Tsukada 	}
137f5a98f37SAkihiro Tsukada 	if (i >= max_wait)
138f5a98f37SAkihiro Tsukada 		return -EIO;
139f5a98f37SAkihiro Tsukada 	if (result)
140f5a98f37SAkihiro Tsukada 		*result = v;
141f5a98f37SAkihiro Tsukada 	return 0;
142f5a98f37SAkihiro Tsukada }
143f5a98f37SAkihiro Tsukada 
144f5a98f37SAkihiro Tsukada /* send [pre-]translated i2c msgs stored at addr */
send_i2c_cmd(struct pt3_board * pt3,u32 addr)145f5a98f37SAkihiro Tsukada static int send_i2c_cmd(struct pt3_board *pt3, u32 addr)
146f5a98f37SAkihiro Tsukada {
147f5a98f37SAkihiro Tsukada 	u32 ret;
148f5a98f37SAkihiro Tsukada 
149f5a98f37SAkihiro Tsukada 	/* make sure that previous transactions had finished */
150f5a98f37SAkihiro Tsukada 	if (wait_i2c_result(pt3, NULL, 50)) {
151f5a98f37SAkihiro Tsukada 		dev_warn(&pt3->pdev->dev, "(%s) prev. transaction stalled\n",
152f5a98f37SAkihiro Tsukada 				__func__);
153f5a98f37SAkihiro Tsukada 		return -EIO;
154f5a98f37SAkihiro Tsukada 	}
155f5a98f37SAkihiro Tsukada 
156f5a98f37SAkihiro Tsukada 	iowrite32(PT3_I2C_RUN | addr, pt3->regs[0] + REG_I2C_W);
157f5a98f37SAkihiro Tsukada 	usleep_range(200, 300);
158f5a98f37SAkihiro Tsukada 	/* wait for the current transaction to finish */
159f5a98f37SAkihiro Tsukada 	if (wait_i2c_result(pt3, &ret, 500) || (ret & STAT_SEQ_ERROR)) {
160f5a98f37SAkihiro Tsukada 		dev_warn(&pt3->pdev->dev, "(%s) failed.\n", __func__);
161f5a98f37SAkihiro Tsukada 		return -EIO;
162f5a98f37SAkihiro Tsukada 	}
163f5a98f37SAkihiro Tsukada 	return 0;
164f5a98f37SAkihiro Tsukada }
165f5a98f37SAkihiro Tsukada 
166f5a98f37SAkihiro Tsukada 
167f5a98f37SAkihiro Tsukada /* init commands for each demod are combined into one transaction
168f5a98f37SAkihiro Tsukada  *  and hidden in ROM with the address PT3_CMD_ADDR_INIT_DEMOD.
169f5a98f37SAkihiro Tsukada  */
pt3_init_all_demods(struct pt3_board * pt3)170f5a98f37SAkihiro Tsukada int  pt3_init_all_demods(struct pt3_board *pt3)
171f5a98f37SAkihiro Tsukada {
172f5a98f37SAkihiro Tsukada 	ioread32(pt3->regs[0] + REG_I2C_R);
173f5a98f37SAkihiro Tsukada 	return send_i2c_cmd(pt3, PT3_CMD_ADDR_INIT_DEMOD);
174f5a98f37SAkihiro Tsukada }
175f5a98f37SAkihiro Tsukada 
176f5a98f37SAkihiro Tsukada /* init commands for two ISDB-T tuners are hidden in ROM. */
pt3_init_all_mxl301rf(struct pt3_board * pt3)177f5a98f37SAkihiro Tsukada int  pt3_init_all_mxl301rf(struct pt3_board *pt3)
178f5a98f37SAkihiro Tsukada {
179f5a98f37SAkihiro Tsukada 	usleep_range(1000, 2000);
180f5a98f37SAkihiro Tsukada 	return send_i2c_cmd(pt3, PT3_CMD_ADDR_INIT_TUNER);
181f5a98f37SAkihiro Tsukada }
182f5a98f37SAkihiro Tsukada 
pt3_i2c_reset(struct pt3_board * pt3)183f5a98f37SAkihiro Tsukada void pt3_i2c_reset(struct pt3_board *pt3)
184f5a98f37SAkihiro Tsukada {
185f5a98f37SAkihiro Tsukada 	iowrite32(PT3_I2C_RESET, pt3->regs[0] + REG_I2C_W);
186f5a98f37SAkihiro Tsukada }
187f5a98f37SAkihiro Tsukada 
188f5a98f37SAkihiro Tsukada /*
189f5a98f37SAkihiro Tsukada  * I2C algorithm
190f5a98f37SAkihiro Tsukada  */
191f5a98f37SAkihiro Tsukada int
pt3_i2c_master_xfer(struct i2c_adapter * adap,struct i2c_msg * msgs,int num)192f5a98f37SAkihiro Tsukada pt3_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
193f5a98f37SAkihiro Tsukada {
194f5a98f37SAkihiro Tsukada 	struct pt3_board *pt3;
195f5a98f37SAkihiro Tsukada 	struct pt3_i2cbuf *cbuf;
196f5a98f37SAkihiro Tsukada 	int i;
197f5a98f37SAkihiro Tsukada 	void __iomem *p;
198f5a98f37SAkihiro Tsukada 
199f5a98f37SAkihiro Tsukada 	pt3 = i2c_get_adapdata(adap);
200f5a98f37SAkihiro Tsukada 	cbuf = pt3->i2c_buf;
201f5a98f37SAkihiro Tsukada 
202f5a98f37SAkihiro Tsukada 	for (i = 0; i < num; i++)
203f5a98f37SAkihiro Tsukada 		if (msgs[i].flags & I2C_M_RECV_LEN) {
204f5a98f37SAkihiro Tsukada 			dev_warn(&pt3->pdev->dev,
205f5a98f37SAkihiro Tsukada 				"(%s) I2C_M_RECV_LEN not supported.\n",
206f5a98f37SAkihiro Tsukada 				__func__);
207f5a98f37SAkihiro Tsukada 			return -EINVAL;
208f5a98f37SAkihiro Tsukada 		}
209f5a98f37SAkihiro Tsukada 
210f5a98f37SAkihiro Tsukada 	translate(cbuf, msgs, num);
211f5a98f37SAkihiro Tsukada 	memcpy_toio(pt3->regs[1] + PT3_I2C_BASE + PT3_CMD_ADDR_NORMAL / 2,
212f5a98f37SAkihiro Tsukada 			cbuf->data, cbuf->num_cmds);
213f5a98f37SAkihiro Tsukada 
214f5a98f37SAkihiro Tsukada 	if (send_i2c_cmd(pt3, PT3_CMD_ADDR_NORMAL) < 0)
215f5a98f37SAkihiro Tsukada 		return -EIO;
216f5a98f37SAkihiro Tsukada 
217f5a98f37SAkihiro Tsukada 	p = pt3->regs[1] + PT3_I2C_BASE;
218f5a98f37SAkihiro Tsukada 	for (i = 0; i < num; i++)
219f5a98f37SAkihiro Tsukada 		if ((msgs[i].flags & I2C_M_RD) && msgs[i].len > 0) {
220f5a98f37SAkihiro Tsukada 			memcpy_fromio(msgs[i].buf, p, msgs[i].len);
221f5a98f37SAkihiro Tsukada 			p += msgs[i].len;
222f5a98f37SAkihiro Tsukada 		}
223f5a98f37SAkihiro Tsukada 
224f5a98f37SAkihiro Tsukada 	return num;
225f5a98f37SAkihiro Tsukada }
226f5a98f37SAkihiro Tsukada 
pt3_i2c_functionality(struct i2c_adapter * adap)227f5a98f37SAkihiro Tsukada u32 pt3_i2c_functionality(struct i2c_adapter *adap)
228f5a98f37SAkihiro Tsukada {
229f5a98f37SAkihiro Tsukada 	return I2C_FUNC_I2C;
230f5a98f37SAkihiro Tsukada }
231