162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * I2C access driver for TI TPS6594/TPS6593/LP8764 PMICs
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/crc8.h>
962306a36Sopenharmony_ci#include <linux/i2c.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
1262306a36Sopenharmony_ci#include <linux/of_device.h>
1362306a36Sopenharmony_ci#include <linux/regmap.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <linux/mfd/tps6594.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic bool enable_crc;
1862306a36Sopenharmony_cimodule_param(enable_crc, bool, 0444);
1962306a36Sopenharmony_ciMODULE_PARM_DESC(enable_crc, "Enable CRC feature for I2C interface");
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ciDECLARE_CRC8_TABLE(tps6594_i2c_crc_table);
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic int tps6594_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	int ret = i2c_transfer(adap, msgs, num);
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	if (ret == num)
2862306a36Sopenharmony_ci		return 0;
2962306a36Sopenharmony_ci	else if (ret < 0)
3062306a36Sopenharmony_ci		return ret;
3162306a36Sopenharmony_ci	else
3262306a36Sopenharmony_ci		return -EIO;
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic int tps6594_i2c_reg_read_with_crc(struct i2c_client *client, u8 page, u8 reg, u8 *val)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	struct i2c_msg msgs[2];
3862306a36Sopenharmony_ci	u8 buf_rx[] = { 0, 0 };
3962306a36Sopenharmony_ci	/* I2C address = I2C base address + Page index */
4062306a36Sopenharmony_ci	const u8 addr = client->addr + page;
4162306a36Sopenharmony_ci	/*
4262306a36Sopenharmony_ci	 * CRC is calculated from every bit included in the protocol
4362306a36Sopenharmony_ci	 * except the ACK bits from the target. Byte stream is:
4462306a36Sopenharmony_ci	 * - B0: (I2C_addr_7bits << 1) | WR_bit, with WR_bit = 0
4562306a36Sopenharmony_ci	 * - B1: reg
4662306a36Sopenharmony_ci	 * - B2: (I2C_addr_7bits << 1) | RD_bit, with RD_bit = 1
4762306a36Sopenharmony_ci	 * - B3: val
4862306a36Sopenharmony_ci	 * - B4: CRC from B0-B1-B2-B3
4962306a36Sopenharmony_ci	 */
5062306a36Sopenharmony_ci	u8 crc_data[] = { addr << 1, reg, addr << 1 | 1, 0 };
5162306a36Sopenharmony_ci	int ret;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	/* Write register */
5462306a36Sopenharmony_ci	msgs[0].addr = addr;
5562306a36Sopenharmony_ci	msgs[0].flags = 0;
5662306a36Sopenharmony_ci	msgs[0].len = 1;
5762306a36Sopenharmony_ci	msgs[0].buf = &reg;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	/* Read data and CRC */
6062306a36Sopenharmony_ci	msgs[1].addr = msgs[0].addr;
6162306a36Sopenharmony_ci	msgs[1].flags = I2C_M_RD;
6262306a36Sopenharmony_ci	msgs[1].len = 2;
6362306a36Sopenharmony_ci	msgs[1].buf = buf_rx;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	ret = tps6594_i2c_transfer(client->adapter, msgs, 2);
6662306a36Sopenharmony_ci	if (ret < 0)
6762306a36Sopenharmony_ci		return ret;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	crc_data[sizeof(crc_data) - 1] = *val = buf_rx[0];
7062306a36Sopenharmony_ci	if (buf_rx[1] != crc8(tps6594_i2c_crc_table, crc_data, sizeof(crc_data), CRC8_INIT_VALUE))
7162306a36Sopenharmony_ci		return -EIO;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return ret;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic int tps6594_i2c_reg_write_with_crc(struct i2c_client *client, u8 page, u8 reg, u8 val)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct i2c_msg msg;
7962306a36Sopenharmony_ci	u8 buf[] = { reg, val, 0 };
8062306a36Sopenharmony_ci	/* I2C address = I2C base address + Page index */
8162306a36Sopenharmony_ci	const u8 addr = client->addr + page;
8262306a36Sopenharmony_ci	/*
8362306a36Sopenharmony_ci	 * CRC is calculated from every bit included in the protocol
8462306a36Sopenharmony_ci	 * except the ACK bits from the target. Byte stream is:
8562306a36Sopenharmony_ci	 * - B0: (I2C_addr_7bits << 1) | WR_bit, with WR_bit = 0
8662306a36Sopenharmony_ci	 * - B1: reg
8762306a36Sopenharmony_ci	 * - B2: val
8862306a36Sopenharmony_ci	 * - B3: CRC from B0-B1-B2
8962306a36Sopenharmony_ci	 */
9062306a36Sopenharmony_ci	const u8 crc_data[] = { addr << 1, reg, val };
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	/* Write register, data and CRC */
9362306a36Sopenharmony_ci	msg.addr = addr;
9462306a36Sopenharmony_ci	msg.flags = client->flags & I2C_M_TEN;
9562306a36Sopenharmony_ci	msg.len = sizeof(buf);
9662306a36Sopenharmony_ci	msg.buf = buf;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	buf[msg.len - 1] = crc8(tps6594_i2c_crc_table, crc_data, sizeof(crc_data), CRC8_INIT_VALUE);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	return tps6594_i2c_transfer(client->adapter, &msg, 1);
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic int tps6594_i2c_read(void *context, const void *reg_buf, size_t reg_size,
10462306a36Sopenharmony_ci			    void *val_buf, size_t val_size)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	struct i2c_client *client = context;
10762306a36Sopenharmony_ci	struct tps6594 *tps = i2c_get_clientdata(client);
10862306a36Sopenharmony_ci	struct i2c_msg msgs[2];
10962306a36Sopenharmony_ci	const u8 *reg_bytes = reg_buf;
11062306a36Sopenharmony_ci	u8 *val_bytes = val_buf;
11162306a36Sopenharmony_ci	const u8 page = reg_bytes[1];
11262306a36Sopenharmony_ci	u8 reg = reg_bytes[0];
11362306a36Sopenharmony_ci	int ret = 0;
11462306a36Sopenharmony_ci	int i;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (tps->use_crc) {
11762306a36Sopenharmony_ci		/*
11862306a36Sopenharmony_ci		 * Auto-increment feature does not support CRC protocol.
11962306a36Sopenharmony_ci		 * Converts the bulk read operation into a series of single read operations.
12062306a36Sopenharmony_ci		 */
12162306a36Sopenharmony_ci		for (i = 0 ; ret == 0 && i < val_size ; i++)
12262306a36Sopenharmony_ci			ret = tps6594_i2c_reg_read_with_crc(client, page, reg + i, val_bytes + i);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci		return ret;
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	/* Write register: I2C address = I2C base address + Page index */
12862306a36Sopenharmony_ci	msgs[0].addr = client->addr + page;
12962306a36Sopenharmony_ci	msgs[0].flags = 0;
13062306a36Sopenharmony_ci	msgs[0].len = 1;
13162306a36Sopenharmony_ci	msgs[0].buf = &reg;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	/* Read data */
13462306a36Sopenharmony_ci	msgs[1].addr = msgs[0].addr;
13562306a36Sopenharmony_ci	msgs[1].flags = I2C_M_RD;
13662306a36Sopenharmony_ci	msgs[1].len = val_size;
13762306a36Sopenharmony_ci	msgs[1].buf = val_bytes;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	return tps6594_i2c_transfer(client->adapter, msgs, 2);
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic int tps6594_i2c_write(void *context, const void *data, size_t count)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct i2c_client *client = context;
14562306a36Sopenharmony_ci	struct tps6594 *tps = i2c_get_clientdata(client);
14662306a36Sopenharmony_ci	struct i2c_msg msg;
14762306a36Sopenharmony_ci	const u8 *bytes = data;
14862306a36Sopenharmony_ci	u8 *buf;
14962306a36Sopenharmony_ci	const u8 page = bytes[1];
15062306a36Sopenharmony_ci	const u8 reg = bytes[0];
15162306a36Sopenharmony_ci	int ret = 0;
15262306a36Sopenharmony_ci	int i;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	if (tps->use_crc) {
15562306a36Sopenharmony_ci		/*
15662306a36Sopenharmony_ci		 * Auto-increment feature does not support CRC protocol.
15762306a36Sopenharmony_ci		 * Converts the bulk write operation into a series of single write operations.
15862306a36Sopenharmony_ci		 */
15962306a36Sopenharmony_ci		for (i = 0 ; ret == 0 && i < count - 2 ; i++)
16062306a36Sopenharmony_ci			ret = tps6594_i2c_reg_write_with_crc(client, page, reg + i, bytes[i + 2]);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci		return ret;
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	/* Setup buffer: page byte is not sent */
16662306a36Sopenharmony_ci	buf = kzalloc(--count, GFP_KERNEL);
16762306a36Sopenharmony_ci	if (!buf)
16862306a36Sopenharmony_ci		return -ENOMEM;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	buf[0] = reg;
17162306a36Sopenharmony_ci	for (i = 0 ; i < count - 1 ; i++)
17262306a36Sopenharmony_ci		buf[i + 1] = bytes[i + 2];
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	/* Write register and data: I2C address = I2C base address + Page index */
17562306a36Sopenharmony_ci	msg.addr = client->addr + page;
17662306a36Sopenharmony_ci	msg.flags = client->flags & I2C_M_TEN;
17762306a36Sopenharmony_ci	msg.len = count;
17862306a36Sopenharmony_ci	msg.buf = buf;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	ret = tps6594_i2c_transfer(client->adapter, &msg, 1);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	kfree(buf);
18362306a36Sopenharmony_ci	return ret;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic const struct regmap_config tps6594_i2c_regmap_config = {
18762306a36Sopenharmony_ci	.reg_bits = 16,
18862306a36Sopenharmony_ci	.val_bits = 8,
18962306a36Sopenharmony_ci	.max_register = TPS6594_REG_DWD_FAIL_CNT_REG,
19062306a36Sopenharmony_ci	.volatile_reg = tps6594_is_volatile_reg,
19162306a36Sopenharmony_ci	.read = tps6594_i2c_read,
19262306a36Sopenharmony_ci	.write = tps6594_i2c_write,
19362306a36Sopenharmony_ci};
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic const struct of_device_id tps6594_i2c_of_match_table[] = {
19662306a36Sopenharmony_ci	{ .compatible = "ti,tps6594-q1", .data = (void *)TPS6594, },
19762306a36Sopenharmony_ci	{ .compatible = "ti,tps6593-q1", .data = (void *)TPS6593, },
19862306a36Sopenharmony_ci	{ .compatible = "ti,lp8764-q1",  .data = (void *)LP8764,  },
19962306a36Sopenharmony_ci	{}
20062306a36Sopenharmony_ci};
20162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tps6594_i2c_of_match_table);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic int tps6594_i2c_probe(struct i2c_client *client)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	struct device *dev = &client->dev;
20662306a36Sopenharmony_ci	struct tps6594 *tps;
20762306a36Sopenharmony_ci	const struct of_device_id *match;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	tps = devm_kzalloc(dev, sizeof(*tps), GFP_KERNEL);
21062306a36Sopenharmony_ci	if (!tps)
21162306a36Sopenharmony_ci		return -ENOMEM;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	i2c_set_clientdata(client, tps);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	tps->dev = dev;
21662306a36Sopenharmony_ci	tps->reg = client->addr;
21762306a36Sopenharmony_ci	tps->irq = client->irq;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	tps->regmap = devm_regmap_init(dev, NULL, client, &tps6594_i2c_regmap_config);
22062306a36Sopenharmony_ci	if (IS_ERR(tps->regmap))
22162306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(tps->regmap), "Failed to init regmap\n");
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	match = of_match_device(tps6594_i2c_of_match_table, dev);
22462306a36Sopenharmony_ci	if (!match)
22562306a36Sopenharmony_ci		return dev_err_probe(dev, -EINVAL, "Failed to find matching chip ID\n");
22662306a36Sopenharmony_ci	tps->chip_id = (unsigned long)match->data;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	crc8_populate_msb(tps6594_i2c_crc_table, TPS6594_CRC8_POLYNOMIAL);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	return tps6594_device_init(tps, enable_crc);
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic struct i2c_driver tps6594_i2c_driver = {
23462306a36Sopenharmony_ci	.driver	= {
23562306a36Sopenharmony_ci		.name = "tps6594",
23662306a36Sopenharmony_ci		.of_match_table = tps6594_i2c_of_match_table,
23762306a36Sopenharmony_ci	},
23862306a36Sopenharmony_ci	.probe = tps6594_i2c_probe,
23962306a36Sopenharmony_ci};
24062306a36Sopenharmony_cimodule_i2c_driver(tps6594_i2c_driver);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ciMODULE_AUTHOR("Julien Panis <jpanis@baylibre.com>");
24362306a36Sopenharmony_ciMODULE_DESCRIPTION("TPS6594 I2C Interface Driver");
24462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
245