xref: /openbmc/linux/drivers/mfd/tps6594-i2c.c (revision a6b6790c)
1325bec71SJulien Panis // SPDX-License-Identifier: GPL-2.0
2325bec71SJulien Panis /*
3325bec71SJulien Panis  * I2C access driver for TI TPS6594/TPS6593/LP8764 PMICs
4325bec71SJulien Panis  *
5325bec71SJulien Panis  * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/
6325bec71SJulien Panis  */
7325bec71SJulien Panis 
8325bec71SJulien Panis #include <linux/crc8.h>
9325bec71SJulien Panis #include <linux/i2c.h>
10325bec71SJulien Panis #include <linux/module.h>
11325bec71SJulien Panis #include <linux/mod_devicetable.h>
12325bec71SJulien Panis #include <linux/of_device.h>
13325bec71SJulien Panis #include <linux/regmap.h>
14325bec71SJulien Panis 
15325bec71SJulien Panis #include <linux/mfd/tps6594.h>
16325bec71SJulien Panis 
17325bec71SJulien Panis static bool enable_crc;
18325bec71SJulien Panis module_param(enable_crc, bool, 0444);
19325bec71SJulien Panis MODULE_PARM_DESC(enable_crc, "Enable CRC feature for I2C interface");
20325bec71SJulien Panis 
21325bec71SJulien Panis DECLARE_CRC8_TABLE(tps6594_i2c_crc_table);
22325bec71SJulien Panis 
tps6594_i2c_transfer(struct i2c_adapter * adap,struct i2c_msg * msgs,int num)23325bec71SJulien Panis static int tps6594_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
24325bec71SJulien Panis {
25325bec71SJulien Panis 	int ret = i2c_transfer(adap, msgs, num);
26325bec71SJulien Panis 
27325bec71SJulien Panis 	if (ret == num)
28325bec71SJulien Panis 		return 0;
29325bec71SJulien Panis 	else if (ret < 0)
30325bec71SJulien Panis 		return ret;
31325bec71SJulien Panis 	else
32325bec71SJulien Panis 		return -EIO;
33325bec71SJulien Panis }
34325bec71SJulien Panis 
tps6594_i2c_reg_read_with_crc(struct i2c_client * client,u8 page,u8 reg,u8 * val)35325bec71SJulien Panis static int tps6594_i2c_reg_read_with_crc(struct i2c_client *client, u8 page, u8 reg, u8 *val)
36325bec71SJulien Panis {
37325bec71SJulien Panis 	struct i2c_msg msgs[2];
38325bec71SJulien Panis 	u8 buf_rx[] = { 0, 0 };
39325bec71SJulien Panis 	/* I2C address = I2C base address + Page index */
40325bec71SJulien Panis 	const u8 addr = client->addr + page;
41325bec71SJulien Panis 	/*
42325bec71SJulien Panis 	 * CRC is calculated from every bit included in the protocol
43325bec71SJulien Panis 	 * except the ACK bits from the target. Byte stream is:
44325bec71SJulien Panis 	 * - B0: (I2C_addr_7bits << 1) | WR_bit, with WR_bit = 0
45325bec71SJulien Panis 	 * - B1: reg
46325bec71SJulien Panis 	 * - B2: (I2C_addr_7bits << 1) | RD_bit, with RD_bit = 1
47325bec71SJulien Panis 	 * - B3: val
48325bec71SJulien Panis 	 * - B4: CRC from B0-B1-B2-B3
49325bec71SJulien Panis 	 */
50325bec71SJulien Panis 	u8 crc_data[] = { addr << 1, reg, addr << 1 | 1, 0 };
51325bec71SJulien Panis 	int ret;
52325bec71SJulien Panis 
53325bec71SJulien Panis 	/* Write register */
54325bec71SJulien Panis 	msgs[0].addr = addr;
55325bec71SJulien Panis 	msgs[0].flags = 0;
56325bec71SJulien Panis 	msgs[0].len = 1;
57325bec71SJulien Panis 	msgs[0].buf = &reg;
58325bec71SJulien Panis 
59325bec71SJulien Panis 	/* Read data and CRC */
60325bec71SJulien Panis 	msgs[1].addr = msgs[0].addr;
61325bec71SJulien Panis 	msgs[1].flags = I2C_M_RD;
62325bec71SJulien Panis 	msgs[1].len = 2;
63325bec71SJulien Panis 	msgs[1].buf = buf_rx;
64325bec71SJulien Panis 
65325bec71SJulien Panis 	ret = tps6594_i2c_transfer(client->adapter, msgs, 2);
66325bec71SJulien Panis 	if (ret < 0)
67325bec71SJulien Panis 		return ret;
68325bec71SJulien Panis 
69325bec71SJulien Panis 	crc_data[sizeof(crc_data) - 1] = *val = buf_rx[0];
70325bec71SJulien Panis 	if (buf_rx[1] != crc8(tps6594_i2c_crc_table, crc_data, sizeof(crc_data), CRC8_INIT_VALUE))
71325bec71SJulien Panis 		return -EIO;
72325bec71SJulien Panis 
73325bec71SJulien Panis 	return ret;
74325bec71SJulien Panis }
75325bec71SJulien Panis 
tps6594_i2c_reg_write_with_crc(struct i2c_client * client,u8 page,u8 reg,u8 val)76325bec71SJulien Panis static int tps6594_i2c_reg_write_with_crc(struct i2c_client *client, u8 page, u8 reg, u8 val)
77325bec71SJulien Panis {
78325bec71SJulien Panis 	struct i2c_msg msg;
79325bec71SJulien Panis 	u8 buf[] = { reg, val, 0 };
80325bec71SJulien Panis 	/* I2C address = I2C base address + Page index */
81325bec71SJulien Panis 	const u8 addr = client->addr + page;
82325bec71SJulien Panis 	/*
83325bec71SJulien Panis 	 * CRC is calculated from every bit included in the protocol
84325bec71SJulien Panis 	 * except the ACK bits from the target. Byte stream is:
85325bec71SJulien Panis 	 * - B0: (I2C_addr_7bits << 1) | WR_bit, with WR_bit = 0
86325bec71SJulien Panis 	 * - B1: reg
87325bec71SJulien Panis 	 * - B2: val
88325bec71SJulien Panis 	 * - B3: CRC from B0-B1-B2
89325bec71SJulien Panis 	 */
90325bec71SJulien Panis 	const u8 crc_data[] = { addr << 1, reg, val };
91325bec71SJulien Panis 
92325bec71SJulien Panis 	/* Write register, data and CRC */
93325bec71SJulien Panis 	msg.addr = addr;
94325bec71SJulien Panis 	msg.flags = client->flags & I2C_M_TEN;
95325bec71SJulien Panis 	msg.len = sizeof(buf);
96325bec71SJulien Panis 	msg.buf = buf;
97325bec71SJulien Panis 
98325bec71SJulien Panis 	buf[msg.len - 1] = crc8(tps6594_i2c_crc_table, crc_data, sizeof(crc_data), CRC8_INIT_VALUE);
99325bec71SJulien Panis 
100325bec71SJulien Panis 	return tps6594_i2c_transfer(client->adapter, &msg, 1);
101325bec71SJulien Panis }
102325bec71SJulien Panis 
tps6594_i2c_read(void * context,const void * reg_buf,size_t reg_size,void * val_buf,size_t val_size)103325bec71SJulien Panis static int tps6594_i2c_read(void *context, const void *reg_buf, size_t reg_size,
104325bec71SJulien Panis 			    void *val_buf, size_t val_size)
105325bec71SJulien Panis {
106325bec71SJulien Panis 	struct i2c_client *client = context;
107325bec71SJulien Panis 	struct tps6594 *tps = i2c_get_clientdata(client);
108325bec71SJulien Panis 	struct i2c_msg msgs[2];
109325bec71SJulien Panis 	const u8 *reg_bytes = reg_buf;
110325bec71SJulien Panis 	u8 *val_bytes = val_buf;
111325bec71SJulien Panis 	const u8 page = reg_bytes[1];
112325bec71SJulien Panis 	u8 reg = reg_bytes[0];
113325bec71SJulien Panis 	int ret = 0;
114325bec71SJulien Panis 	int i;
115325bec71SJulien Panis 
116325bec71SJulien Panis 	if (tps->use_crc) {
117325bec71SJulien Panis 		/*
118325bec71SJulien Panis 		 * Auto-increment feature does not support CRC protocol.
119325bec71SJulien Panis 		 * Converts the bulk read operation into a series of single read operations.
120325bec71SJulien Panis 		 */
121325bec71SJulien Panis 		for (i = 0 ; ret == 0 && i < val_size ; i++)
122325bec71SJulien Panis 			ret = tps6594_i2c_reg_read_with_crc(client, page, reg + i, val_bytes + i);
123325bec71SJulien Panis 
124325bec71SJulien Panis 		return ret;
125325bec71SJulien Panis 	}
126325bec71SJulien Panis 
127325bec71SJulien Panis 	/* Write register: I2C address = I2C base address + Page index */
128325bec71SJulien Panis 	msgs[0].addr = client->addr + page;
129325bec71SJulien Panis 	msgs[0].flags = 0;
130325bec71SJulien Panis 	msgs[0].len = 1;
131325bec71SJulien Panis 	msgs[0].buf = &reg;
132325bec71SJulien Panis 
133325bec71SJulien Panis 	/* Read data */
134325bec71SJulien Panis 	msgs[1].addr = msgs[0].addr;
135325bec71SJulien Panis 	msgs[1].flags = I2C_M_RD;
136325bec71SJulien Panis 	msgs[1].len = val_size;
137325bec71SJulien Panis 	msgs[1].buf = val_bytes;
138325bec71SJulien Panis 
139325bec71SJulien Panis 	return tps6594_i2c_transfer(client->adapter, msgs, 2);
140325bec71SJulien Panis }
141325bec71SJulien Panis 
tps6594_i2c_write(void * context,const void * data,size_t count)142325bec71SJulien Panis static int tps6594_i2c_write(void *context, const void *data, size_t count)
143325bec71SJulien Panis {
144325bec71SJulien Panis 	struct i2c_client *client = context;
145325bec71SJulien Panis 	struct tps6594 *tps = i2c_get_clientdata(client);
146325bec71SJulien Panis 	struct i2c_msg msg;
147325bec71SJulien Panis 	const u8 *bytes = data;
148325bec71SJulien Panis 	u8 *buf;
149325bec71SJulien Panis 	const u8 page = bytes[1];
150325bec71SJulien Panis 	const u8 reg = bytes[0];
151325bec71SJulien Panis 	int ret = 0;
152325bec71SJulien Panis 	int i;
153325bec71SJulien Panis 
154325bec71SJulien Panis 	if (tps->use_crc) {
155325bec71SJulien Panis 		/*
156325bec71SJulien Panis 		 * Auto-increment feature does not support CRC protocol.
157325bec71SJulien Panis 		 * Converts the bulk write operation into a series of single write operations.
158325bec71SJulien Panis 		 */
159325bec71SJulien Panis 		for (i = 0 ; ret == 0 && i < count - 2 ; i++)
160325bec71SJulien Panis 			ret = tps6594_i2c_reg_write_with_crc(client, page, reg + i, bytes[i + 2]);
161325bec71SJulien Panis 
162325bec71SJulien Panis 		return ret;
163325bec71SJulien Panis 	}
164325bec71SJulien Panis 
165325bec71SJulien Panis 	/* Setup buffer: page byte is not sent */
166325bec71SJulien Panis 	buf = kzalloc(--count, GFP_KERNEL);
167325bec71SJulien Panis 	if (!buf)
168325bec71SJulien Panis 		return -ENOMEM;
169325bec71SJulien Panis 
170325bec71SJulien Panis 	buf[0] = reg;
171325bec71SJulien Panis 	for (i = 0 ; i < count - 1 ; i++)
172325bec71SJulien Panis 		buf[i + 1] = bytes[i + 2];
173325bec71SJulien Panis 
174325bec71SJulien Panis 	/* Write register and data: I2C address = I2C base address + Page index */
175325bec71SJulien Panis 	msg.addr = client->addr + page;
176325bec71SJulien Panis 	msg.flags = client->flags & I2C_M_TEN;
177325bec71SJulien Panis 	msg.len = count;
178325bec71SJulien Panis 	msg.buf = buf;
179325bec71SJulien Panis 
180325bec71SJulien Panis 	ret = tps6594_i2c_transfer(client->adapter, &msg, 1);
181325bec71SJulien Panis 
182325bec71SJulien Panis 	kfree(buf);
183325bec71SJulien Panis 	return ret;
184325bec71SJulien Panis }
185325bec71SJulien Panis 
186325bec71SJulien Panis static const struct regmap_config tps6594_i2c_regmap_config = {
187325bec71SJulien Panis 	.reg_bits = 16,
188325bec71SJulien Panis 	.val_bits = 8,
189325bec71SJulien Panis 	.max_register = TPS6594_REG_DWD_FAIL_CNT_REG,
190325bec71SJulien Panis 	.volatile_reg = tps6594_is_volatile_reg,
191325bec71SJulien Panis 	.read = tps6594_i2c_read,
192325bec71SJulien Panis 	.write = tps6594_i2c_write,
193325bec71SJulien Panis };
194325bec71SJulien Panis 
195325bec71SJulien Panis static const struct of_device_id tps6594_i2c_of_match_table[] = {
196325bec71SJulien Panis 	{ .compatible = "ti,tps6594-q1", .data = (void *)TPS6594, },
197325bec71SJulien Panis 	{ .compatible = "ti,tps6593-q1", .data = (void *)TPS6593, },
198325bec71SJulien Panis 	{ .compatible = "ti,lp8764-q1",  .data = (void *)LP8764,  },
199325bec71SJulien Panis 	{}
200325bec71SJulien Panis };
201325bec71SJulien Panis MODULE_DEVICE_TABLE(of, tps6594_i2c_of_match_table);
202325bec71SJulien Panis 
tps6594_i2c_probe(struct i2c_client * client)203325bec71SJulien Panis static int tps6594_i2c_probe(struct i2c_client *client)
204325bec71SJulien Panis {
205325bec71SJulien Panis 	struct device *dev = &client->dev;
206325bec71SJulien Panis 	struct tps6594 *tps;
207325bec71SJulien Panis 	const struct of_device_id *match;
208325bec71SJulien Panis 
209325bec71SJulien Panis 	tps = devm_kzalloc(dev, sizeof(*tps), GFP_KERNEL);
210325bec71SJulien Panis 	if (!tps)
211325bec71SJulien Panis 		return -ENOMEM;
212325bec71SJulien Panis 
213325bec71SJulien Panis 	i2c_set_clientdata(client, tps);
214325bec71SJulien Panis 
215325bec71SJulien Panis 	tps->dev = dev;
216325bec71SJulien Panis 	tps->reg = client->addr;
217325bec71SJulien Panis 	tps->irq = client->irq;
218325bec71SJulien Panis 
219325bec71SJulien Panis 	tps->regmap = devm_regmap_init(dev, NULL, client, &tps6594_i2c_regmap_config);
220325bec71SJulien Panis 	if (IS_ERR(tps->regmap))
221325bec71SJulien Panis 		return dev_err_probe(dev, PTR_ERR(tps->regmap), "Failed to init regmap\n");
222325bec71SJulien Panis 
223325bec71SJulien Panis 	match = of_match_device(tps6594_i2c_of_match_table, dev);
224325bec71SJulien Panis 	if (!match)
225cc5f2eb7SDan Carpenter 		return dev_err_probe(dev, -EINVAL, "Failed to find matching chip ID\n");
226325bec71SJulien Panis 	tps->chip_id = (unsigned long)match->data;
227325bec71SJulien Panis 
228325bec71SJulien Panis 	crc8_populate_msb(tps6594_i2c_crc_table, TPS6594_CRC8_POLYNOMIAL);
229325bec71SJulien Panis 
230325bec71SJulien Panis 	return tps6594_device_init(tps, enable_crc);
231325bec71SJulien Panis }
232325bec71SJulien Panis 
233325bec71SJulien Panis static struct i2c_driver tps6594_i2c_driver = {
234325bec71SJulien Panis 	.driver	= {
235325bec71SJulien Panis 		.name = "tps6594",
236325bec71SJulien Panis 		.of_match_table = tps6594_i2c_of_match_table,
237325bec71SJulien Panis 	},
238*a6b6790cSUwe Kleine-König 	.probe = tps6594_i2c_probe,
239325bec71SJulien Panis };
240325bec71SJulien Panis module_i2c_driver(tps6594_i2c_driver);
241325bec71SJulien Panis 
242325bec71SJulien Panis MODULE_AUTHOR("Julien Panis <jpanis@baylibre.com>");
243325bec71SJulien Panis MODULE_DESCRIPTION("TPS6594 I2C Interface Driver");
244325bec71SJulien Panis MODULE_LICENSE("GPL");
245