xref: /linux/drivers/media/usb/go7007/go7007-i2c.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
1*1802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2866b8695SGreg Kroah-Hartman /*
3866b8695SGreg Kroah-Hartman  * Copyright (C) 2005-2006 Micronas USA Inc.
4866b8695SGreg Kroah-Hartman  */
5866b8695SGreg Kroah-Hartman 
6866b8695SGreg Kroah-Hartman #include <linux/module.h>
7866b8695SGreg Kroah-Hartman #include <linux/delay.h>
8866b8695SGreg Kroah-Hartman #include <linux/sched.h>
9866b8695SGreg Kroah-Hartman #include <linux/list.h>
10866b8695SGreg Kroah-Hartman #include <linux/unistd.h>
11866b8695SGreg Kroah-Hartman #include <linux/time.h>
12866b8695SGreg Kroah-Hartman #include <linux/device.h>
13866b8695SGreg Kroah-Hartman #include <linux/i2c.h>
14fd9a40daSMauro Carvalho Chehab #include <linux/mutex.h>
15866b8695SGreg Kroah-Hartman #include <linux/uaccess.h>
16866b8695SGreg Kroah-Hartman 
17866b8695SGreg Kroah-Hartman #include "go7007-priv.h"
18866b8695SGreg Kroah-Hartman 
19866b8695SGreg Kroah-Hartman /********************* Driver for on-board I2C adapter *********************/
20866b8695SGreg Kroah-Hartman 
21866b8695SGreg Kroah-Hartman /* #define GO7007_I2C_DEBUG */
22866b8695SGreg Kroah-Hartman 
23866b8695SGreg Kroah-Hartman #define SPI_I2C_ADDR_BASE		0x1400
24866b8695SGreg Kroah-Hartman #define STATUS_REG_ADDR			(SPI_I2C_ADDR_BASE + 0x2)
25866b8695SGreg Kroah-Hartman #define I2C_CTRL_REG_ADDR		(SPI_I2C_ADDR_BASE + 0x6)
26866b8695SGreg Kroah-Hartman #define I2C_DEV_UP_ADDR_REG_ADDR	(SPI_I2C_ADDR_BASE + 0x7)
27866b8695SGreg Kroah-Hartman #define I2C_LO_ADDR_REG_ADDR		(SPI_I2C_ADDR_BASE + 0x8)
28866b8695SGreg Kroah-Hartman #define I2C_DATA_REG_ADDR		(SPI_I2C_ADDR_BASE + 0x9)
29866b8695SGreg Kroah-Hartman #define I2C_CLKFREQ_REG_ADDR		(SPI_I2C_ADDR_BASE + 0xa)
30866b8695SGreg Kroah-Hartman 
31866b8695SGreg Kroah-Hartman #define I2C_STATE_MASK			0x0007
32866b8695SGreg Kroah-Hartman #define I2C_READ_READY_MASK		0x0008
33866b8695SGreg Kroah-Hartman 
34866b8695SGreg Kroah-Hartman /* There is only one I2C port on the TW2804 that feeds all four GO7007 VIPs
35866b8695SGreg Kroah-Hartman  * on the Adlink PCI-MPG24, so access is shared between all of them. */
36fd9a40daSMauro Carvalho Chehab static DEFINE_MUTEX(adlink_mpg24_i2c_lock);
37866b8695SGreg Kroah-Hartman 
38866b8695SGreg Kroah-Hartman static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read,
39866b8695SGreg Kroah-Hartman 		u16 command, int flags, u8 *data)
40866b8695SGreg Kroah-Hartman {
41ffb97493SHans Verkuil 	int i, ret = -EIO;
42866b8695SGreg Kroah-Hartman 	u16 val;
43866b8695SGreg Kroah-Hartman 
44866b8695SGreg Kroah-Hartman 	if (go->status == STATUS_SHUTDOWN)
45ffb97493SHans Verkuil 		return -ENODEV;
46866b8695SGreg Kroah-Hartman 
47866b8695SGreg Kroah-Hartman #ifdef GO7007_I2C_DEBUG
48866b8695SGreg Kroah-Hartman 	if (read)
49afc2e8a0SYAMANE Toshiaki 		dev_dbg(go->dev, "go7007-i2c: reading 0x%02x on 0x%02x\n",
50866b8695SGreg Kroah-Hartman 			command, addr);
51866b8695SGreg Kroah-Hartman 	else
52afc2e8a0SYAMANE Toshiaki 		dev_dbg(go->dev,
53866b8695SGreg Kroah-Hartman 			"go7007-i2c: writing 0x%02x to 0x%02x on 0x%02x\n",
54866b8695SGreg Kroah-Hartman 			*data, command, addr);
55866b8695SGreg Kroah-Hartman #endif
56866b8695SGreg Kroah-Hartman 
57fd9a40daSMauro Carvalho Chehab 	mutex_lock(&go->hw_lock);
58866b8695SGreg Kroah-Hartman 
59866b8695SGreg Kroah-Hartman 	if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) {
60866b8695SGreg Kroah-Hartman 		/* Bridge the I2C port on this GO7007 to the shared bus */
61fd9a40daSMauro Carvalho Chehab 		mutex_lock(&adlink_mpg24_i2c_lock);
62866b8695SGreg Kroah-Hartman 		go7007_write_addr(go, 0x3c82, 0x0020);
63866b8695SGreg Kroah-Hartman 	}
64866b8695SGreg Kroah-Hartman 
65866b8695SGreg Kroah-Hartman 	/* Wait for I2C adapter to be ready */
66866b8695SGreg Kroah-Hartman 	for (i = 0; i < 10; ++i) {
67866b8695SGreg Kroah-Hartman 		if (go7007_read_addr(go, STATUS_REG_ADDR, &val) < 0)
68866b8695SGreg Kroah-Hartman 			goto i2c_done;
69866b8695SGreg Kroah-Hartman 		if (!(val & I2C_STATE_MASK))
70866b8695SGreg Kroah-Hartman 			break;
71866b8695SGreg Kroah-Hartman 		msleep(100);
72866b8695SGreg Kroah-Hartman 	}
73866b8695SGreg Kroah-Hartman 	if (i == 10) {
74afc2e8a0SYAMANE Toshiaki 		dev_err(go->dev, "go7007-i2c: I2C adapter is hung\n");
75866b8695SGreg Kroah-Hartman 		goto i2c_done;
76866b8695SGreg Kroah-Hartman 	}
77866b8695SGreg Kroah-Hartman 
78866b8695SGreg Kroah-Hartman 	/* Set target register (command) */
79866b8695SGreg Kroah-Hartman 	go7007_write_addr(go, I2C_CTRL_REG_ADDR, flags);
80866b8695SGreg Kroah-Hartman 	go7007_write_addr(go, I2C_LO_ADDR_REG_ADDR, command);
81866b8695SGreg Kroah-Hartman 
82866b8695SGreg Kroah-Hartman 	/* If we're writing, send the data and target address and we're done */
83866b8695SGreg Kroah-Hartman 	if (!read) {
84866b8695SGreg Kroah-Hartman 		go7007_write_addr(go, I2C_DATA_REG_ADDR, *data);
85866b8695SGreg Kroah-Hartman 		go7007_write_addr(go, I2C_DEV_UP_ADDR_REG_ADDR,
86866b8695SGreg Kroah-Hartman 					(addr << 9) | (command >> 8));
87866b8695SGreg Kroah-Hartman 		ret = 0;
88866b8695SGreg Kroah-Hartman 		goto i2c_done;
89866b8695SGreg Kroah-Hartman 	}
90866b8695SGreg Kroah-Hartman 
91866b8695SGreg Kroah-Hartman 	/* Otherwise, we're reading.  First clear i2c_rx_data_rdy. */
92866b8695SGreg Kroah-Hartman 	if (go7007_read_addr(go, I2C_DATA_REG_ADDR, &val) < 0)
93866b8695SGreg Kroah-Hartman 		goto i2c_done;
94866b8695SGreg Kroah-Hartman 
95866b8695SGreg Kroah-Hartman 	/* Send the target address plus read flag */
96866b8695SGreg Kroah-Hartman 	go7007_write_addr(go, I2C_DEV_UP_ADDR_REG_ADDR,
97866b8695SGreg Kroah-Hartman 			(addr << 9) | 0x0100 | (command >> 8));
98866b8695SGreg Kroah-Hartman 
99866b8695SGreg Kroah-Hartman 	/* Wait for i2c_rx_data_rdy */
100866b8695SGreg Kroah-Hartman 	for (i = 0; i < 10; ++i) {
101866b8695SGreg Kroah-Hartman 		if (go7007_read_addr(go, STATUS_REG_ADDR, &val) < 0)
102866b8695SGreg Kroah-Hartman 			goto i2c_done;
103866b8695SGreg Kroah-Hartman 		if (val & I2C_READ_READY_MASK)
104866b8695SGreg Kroah-Hartman 			break;
105866b8695SGreg Kroah-Hartman 		msleep(100);
106866b8695SGreg Kroah-Hartman 	}
107866b8695SGreg Kroah-Hartman 	if (i == 10) {
108afc2e8a0SYAMANE Toshiaki 		dev_err(go->dev, "go7007-i2c: I2C adapter is hung\n");
109866b8695SGreg Kroah-Hartman 		goto i2c_done;
110866b8695SGreg Kroah-Hartman 	}
111866b8695SGreg Kroah-Hartman 
112866b8695SGreg Kroah-Hartman 	/* Retrieve the read byte */
113866b8695SGreg Kroah-Hartman 	if (go7007_read_addr(go, I2C_DATA_REG_ADDR, &val) < 0)
114866b8695SGreg Kroah-Hartman 		goto i2c_done;
115866b8695SGreg Kroah-Hartman 	*data = val;
116866b8695SGreg Kroah-Hartman 	ret = 0;
117866b8695SGreg Kroah-Hartman 
118866b8695SGreg Kroah-Hartman i2c_done:
119866b8695SGreg Kroah-Hartman 	if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) {
120866b8695SGreg Kroah-Hartman 		/* Isolate the I2C port on this GO7007 from the shared bus */
121866b8695SGreg Kroah-Hartman 		go7007_write_addr(go, 0x3c82, 0x0000);
122fd9a40daSMauro Carvalho Chehab 		mutex_unlock(&adlink_mpg24_i2c_lock);
123866b8695SGreg Kroah-Hartman 	}
124fd9a40daSMauro Carvalho Chehab 	mutex_unlock(&go->hw_lock);
125866b8695SGreg Kroah-Hartman 	return ret;
126866b8695SGreg Kroah-Hartman }
127866b8695SGreg Kroah-Hartman 
128866b8695SGreg Kroah-Hartman static int go7007_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
129866b8695SGreg Kroah-Hartman 		unsigned short flags, char read_write,
130866b8695SGreg Kroah-Hartman 		u8 command, int size, union i2c_smbus_data *data)
131866b8695SGreg Kroah-Hartman {
132866b8695SGreg Kroah-Hartman 	struct go7007 *go = i2c_get_adapdata(adapter);
133866b8695SGreg Kroah-Hartman 
134866b8695SGreg Kroah-Hartman 	if (size != I2C_SMBUS_BYTE_DATA)
135ffb97493SHans Verkuil 		return -EIO;
136866b8695SGreg Kroah-Hartman 	return go7007_i2c_xfer(go, addr, read_write == I2C_SMBUS_READ, command,
137866b8695SGreg Kroah-Hartman 			flags & I2C_CLIENT_SCCB ? 0x10 : 0x00, &data->byte);
138866b8695SGreg Kroah-Hartman }
139866b8695SGreg Kroah-Hartman 
140866b8695SGreg Kroah-Hartman /* VERY LIMITED I2C master xfer function -- only needed because the
141866b8695SGreg Kroah-Hartman  * SMBus functions only support 8-bit commands and the SAA7135 uses
142866b8695SGreg Kroah-Hartman  * 16-bit commands.  The I2C interface on the GO7007, as limited as
143866b8695SGreg Kroah-Hartman  * it is, does support this mode. */
144866b8695SGreg Kroah-Hartman 
145866b8695SGreg Kroah-Hartman static int go7007_i2c_master_xfer(struct i2c_adapter *adapter,
146866b8695SGreg Kroah-Hartman 					struct i2c_msg msgs[], int num)
147866b8695SGreg Kroah-Hartman {
148866b8695SGreg Kroah-Hartman 	struct go7007 *go = i2c_get_adapdata(adapter);
149866b8695SGreg Kroah-Hartman 	int i;
150866b8695SGreg Kroah-Hartman 
151866b8695SGreg Kroah-Hartman 	for (i = 0; i < num; ++i) {
152866b8695SGreg Kroah-Hartman 		/* We can only do two things here -- write three bytes, or
153866b8695SGreg Kroah-Hartman 		 * write two bytes and read one byte. */
154866b8695SGreg Kroah-Hartman 		if (msgs[i].len == 2) {
155866b8695SGreg Kroah-Hartman 			if (i + 1 == num || msgs[i].addr != msgs[i + 1].addr ||
156866b8695SGreg Kroah-Hartman 					(msgs[i].flags & I2C_M_RD) ||
157866b8695SGreg Kroah-Hartman 					!(msgs[i + 1].flags & I2C_M_RD) ||
158866b8695SGreg Kroah-Hartman 					msgs[i + 1].len != 1)
159ffb97493SHans Verkuil 				return -EIO;
160866b8695SGreg Kroah-Hartman 			if (go7007_i2c_xfer(go, msgs[i].addr, 1,
161866b8695SGreg Kroah-Hartman 					(msgs[i].buf[0] << 8) | msgs[i].buf[1],
162866b8695SGreg Kroah-Hartman 					0x01, &msgs[i + 1].buf[0]) < 0)
163ffb97493SHans Verkuil 				return -EIO;
164866b8695SGreg Kroah-Hartman 			++i;
165866b8695SGreg Kroah-Hartman 		} else if (msgs[i].len == 3) {
166866b8695SGreg Kroah-Hartman 			if (msgs[i].flags & I2C_M_RD)
167ffb97493SHans Verkuil 				return -EIO;
168866b8695SGreg Kroah-Hartman 			if (msgs[i].len != 3)
169ffb97493SHans Verkuil 				return -EIO;
170866b8695SGreg Kroah-Hartman 			if (go7007_i2c_xfer(go, msgs[i].addr, 0,
171866b8695SGreg Kroah-Hartman 					(msgs[i].buf[0] << 8) | msgs[i].buf[1],
172866b8695SGreg Kroah-Hartman 					0x01, &msgs[i].buf[2]) < 0)
173ffb97493SHans Verkuil 				return -EIO;
174866b8695SGreg Kroah-Hartman 		} else
175ffb97493SHans Verkuil 			return -EIO;
176866b8695SGreg Kroah-Hartman 	}
177866b8695SGreg Kroah-Hartman 
178ffb97493SHans Verkuil 	return num;
179866b8695SGreg Kroah-Hartman }
180866b8695SGreg Kroah-Hartman 
181866b8695SGreg Kroah-Hartman static u32 go7007_functionality(struct i2c_adapter *adapter)
182866b8695SGreg Kroah-Hartman {
183866b8695SGreg Kroah-Hartman 	return I2C_FUNC_SMBUS_BYTE_DATA;
184866b8695SGreg Kroah-Hartman }
185866b8695SGreg Kroah-Hartman 
18678f2c50bSJulia Lawall static const struct i2c_algorithm go7007_algo = {
187866b8695SGreg Kroah-Hartman 	.smbus_xfer	= go7007_smbus_xfer,
188866b8695SGreg Kroah-Hartman 	.master_xfer	= go7007_i2c_master_xfer,
189866b8695SGreg Kroah-Hartman 	.functionality	= go7007_functionality,
190866b8695SGreg Kroah-Hartman };
191866b8695SGreg Kroah-Hartman 
192866b8695SGreg Kroah-Hartman static struct i2c_adapter go7007_adap_templ = {
193866b8695SGreg Kroah-Hartman 	.owner			= THIS_MODULE,
194866b8695SGreg Kroah-Hartman 	.name			= "WIS GO7007SB",
195866b8695SGreg Kroah-Hartman 	.algo			= &go7007_algo,
196866b8695SGreg Kroah-Hartman };
197866b8695SGreg Kroah-Hartman 
198866b8695SGreg Kroah-Hartman int go7007_i2c_init(struct go7007 *go)
199866b8695SGreg Kroah-Hartman {
200866b8695SGreg Kroah-Hartman 	memcpy(&go->i2c_adapter, &go7007_adap_templ,
201866b8695SGreg Kroah-Hartman 			sizeof(go7007_adap_templ));
202866b8695SGreg Kroah-Hartman 	go->i2c_adapter.dev.parent = go->dev;
203866b8695SGreg Kroah-Hartman 	i2c_set_adapdata(&go->i2c_adapter, go);
204866b8695SGreg Kroah-Hartman 	if (i2c_add_adapter(&go->i2c_adapter) < 0) {
205afc2e8a0SYAMANE Toshiaki 		dev_err(go->dev,
206866b8695SGreg Kroah-Hartman 			"go7007-i2c: error: i2c_add_adapter failed\n");
207866b8695SGreg Kroah-Hartman 		return -1;
208866b8695SGreg Kroah-Hartman 	}
209866b8695SGreg Kroah-Hartman 	return 0;
210866b8695SGreg Kroah-Hartman }
211