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 = ® 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 = ® 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