From nobody Wed Dec 17 11:34:55 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4BC97C25B67 for ; Thu, 26 Oct 2023 13:35:21 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1345075AbjJZNfV (ORCPT ); Thu, 26 Oct 2023 09:35:21 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37926 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231200AbjJZNfT (ORCPT ); Thu, 26 Oct 2023 09:35:19 -0400 Received: from esa1.ltts.com (unknown [118.185.121.105]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0D35618A for ; Thu, 26 Oct 2023 06:35:15 -0700 (PDT) IronPort-SDR: aAA2oINQM4u8R8s6ZyG2BwVPki5ksLO7bxmQ3RcVmd3DJ+98Ipu76o9zLEH8glG+NA5Q9OoI1T pseVau6h83iA== Received: from unknown (HELO BLTSP01651.lnties.com) ([10.20.120.98]) by esa1.ltts.com with ESMTP; 26 Oct 2023 19:04:03 +0530 From: Gairuboina Sirisha To: linux-kernel@vger.kernel.org Cc: lee@kernel.org, arnd@arndb.de, gregkh@linuxfoundation.org, Gairuboina Sirisha Subject: [PATCH v1 2/3] drivers: mfd: Add support for TPS65224 i2c driver Date: Thu, 26 Oct 2023 19:02:25 +0530 Message-Id: <20231026133226.290040-3-sirisha.gairuboina@Ltts.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231026133226.290040-1-sirisha.gairuboina@Ltts.com> References: <20231026133226.290040-1-sirisha.gairuboina@Ltts.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" From: Gairuboina Sirisha Added MFD driver that enables I2C communication with and without CRC Signed-off-by: Gairuboina Sirisha --- drivers/mfd/Kconfig | 14 +++ drivers/mfd/Makefile | 1 + drivers/mfd/tps65224-i2c.c | 245 +++++++++++++++++++++++++++++++++++++ 3 files changed, 260 insertions(+) create mode 100644 drivers/mfd/tps65224-i2c.c diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 2e4906484eed..943d85d49fc5 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1767,12 +1767,26 @@ config MFD_TPS6594_SPI =20 This driver can also be built as a module. If so, the module will be called tps6594-spi. + config MFD_TPS65224 tristate select MFD_CORE select REGMAP select REGMAP_IRQ =20 +config MFD_TPS65224_I2C + tristate "TI TPS65224 Power Management chip with I2C" + select MFD_TPS65224 + select REGMAP_I2C + select CRC8 + depends on I2C + help + If you say yes here you get support for the TPS65224 series of + PM chips with I2C interface. + + This driver can also be built as a module. If so, the module + will be called tps65224-i2c. + config TWL4030_CORE bool "TI TWL4030/TWL5030/TWL6030/TPS659x0 Support" depends on I2C=3Dy diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index ff4e158fa4db..4963fecd3e93 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -103,6 +103,7 @@ obj-$(CONFIG_MFD_TPS6594) +=3D tps6594-core.o obj-$(CONFIG_MFD_TPS6594_I2C) +=3D tps6594-i2c.o obj-$(CONFIG_MFD_TPS6594_SPI) +=3D tps6594-spi.o obj-$(CONFIG_MFD_TPS65224) +=3D tps65224-core.o +obj-$(CONFIG_MFD_TPS65224) +=3D tps65224-i2c.o obj-$(CONFIG_MENELAUS) +=3D menelaus.o =20 obj-$(CONFIG_TWL4030_CORE) +=3D twl-core.o twl4030-irq.o twl6030-irq.o diff --git a/drivers/mfd/tps65224-i2c.c b/drivers/mfd/tps65224-i2c.c new file mode 100644 index 000000000000..c6300138ce4c --- /dev/null +++ b/drivers/mfd/tps65224-i2c.c @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * I2C access driver for TI TPS65224 PMIC + * + * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/ + * + * Based on the TPS6594 I2C Interface Driver by + * Julien Panis + */ + +#include +#include +#include +#include +#include +#include + +#include + +static bool enable_crc; +module_param(enable_crc, bool, 0444); +MODULE_PARM_DESC(enable_crc, "Enable CRC feature for I2C interface"); + +DECLARE_CRC8_TABLE(tps65224_i2c_crc_table); + +static int tps65224_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg = *msgs, int num) +{ + int ret =3D i2c_transfer(adap, msgs, num); + + if (ret =3D=3D num) + return 0; + else if (ret < 0) + return ret; + else + return -EIO; +} + +static int tps65224_i2c_reg_read_with_crc(struct i2c_client *client, u8 pa= ge, u8 reg, u8 *val) +{ + struct i2c_msg msgs[2]; + u8 buf_rx[] =3D { 0, 0 }; + /* I2C address =3D I2C base address + Page index */ + const u8 addr =3D client->addr + page; + /* + * CRC is calculated from every bit included in the protocol + * except the ACK bits from the target. Byte stream is: + * - B0: (I2C_addr_7bits << 1) | WR_bit, with WR_bit =3D 0 + * - B1: reg + * - B2: (I2C_addr_7bits << 1) | RD_bit, with RD_bit =3D 1 + * - B3: val + * - B4: CRC from B0-B1-B2-B3 + */ + u8 crc_data[] =3D { addr << 1, reg, addr << 1 | 1, 0 }; + int ret; + + /* Write register */ + msgs[0].addr =3D addr; + msgs[0].flags =3D 0; + msgs[0].len =3D 1; + msgs[0].buf =3D ® + + /* Read data and CRC */ + msgs[1].addr =3D msgs[0].addr; + msgs[1].flags =3D I2C_M_RD; + msgs[1].len =3D 2; + msgs[1].buf =3D buf_rx; + + ret =3D tps65224_i2c_transfer(client->adapter, msgs, 2); + if (ret < 0) + return ret; + + crc_data[sizeof(crc_data) - 1] =3D *val =3D buf_rx[0]; + if (buf_rx[1] !=3D crc8(tps65224_i2c_crc_table, crc_data, sizeof(crc_data= ), CRC8_INIT_VALUE)) + return -EIO; + + return ret; +} + +static int tps65224_i2c_reg_write_with_crc(struct i2c_client *client, u8 p= age, u8 reg, u8 val) +{ + struct i2c_msg msg; + u8 buf[] =3D { reg, val, 0 }; + /* I2C address =3D I2C base address + Page index */ + const u8 addr =3D client->addr + page; + /* + * CRC is calculated from every bit included in the protocol + * except the ACK bits from the target. Byte stream is: + * - B0: (I2C_addr_7bits << 1) | WR_bit, with WR_bit =3D 0 + * - B1: reg + * - B2: val + * - B3: CRC from B0-B1-B2 + */ + const u8 crc_data[] =3D { addr << 1, reg, val }; + + /* Write register, data and CRC */ + msg.addr =3D addr; + msg.flags =3D client->flags & I2C_M_TEN; + msg.len =3D sizeof(buf); + msg.buf =3D buf; + + buf[msg.len - 1] =3D crc8(tps65224_i2c_crc_table, crc_data, + sizeof(crc_data), CRC8_INIT_VALUE); + + return tps65224_i2c_transfer(client->adapter, &msg, 1); +} + +static int tps65224_i2c_read(void *context, const void *reg_buf, size_t re= g_size, + void *val_buf, size_t val_size) +{ + struct i2c_client *client =3D context; + struct tps65224 *tps =3D i2c_get_clientdata(client); + struct i2c_msg msgs[2]; + const u8 *reg_bytes =3D reg_buf; + u8 *val_bytes =3D val_buf; + const u8 page =3D reg_bytes[1]; + u8 reg =3D reg_bytes[0]; + int ret =3D 0; + int i; + + if (tps->use_crc) { + /* + * Auto-increment feature does not support CRC protocol. + * Converts the bulk read operation into a series of single read operati= ons. + */ + for (i =3D 0 ; ret =3D=3D 0 && i < val_size ; i++) + ret =3D tps65224_i2c_reg_read_with_crc(client, page, reg + i, val_bytes= + i); + + return ret; + } + + /* Write register: I2C address =3D I2C base address + Page index */ + msgs[0].addr =3D client->addr + page; + msgs[0].flags =3D 0; + msgs[0].len =3D 1; + msgs[0].buf =3D ® + + /* Read data */ + msgs[1].addr =3D msgs[0].addr; + msgs[1].flags =3D I2C_M_RD; + msgs[1].len =3D val_size; + msgs[1].buf =3D val_bytes; + + return tps65224_i2c_transfer(client->adapter, msgs, 2); +} + +static int tps65224_i2c_write(void *context, const void *data, size_t coun= t) +{ + struct i2c_client *client =3D context; + struct tps65224 *tps =3D i2c_get_clientdata(client); + struct i2c_msg msg; + const u8 *bytes =3D data; + u8 *buf; + const u8 page =3D bytes[1]; + const u8 reg =3D bytes[0]; + int ret =3D 0; + int i; + + if (tps->use_crc) { + /* + * Auto-increment feature does not support CRC protocol. + * Converts the bulk write operation into a series of single write opera= tions. + */ + for (i =3D 0 ; ret =3D=3D 0 && i < count - 2 ; i++) + ret =3D tps65224_i2c_reg_write_with_crc(client, page, reg + i, bytes[i = + 2]); + + return ret; + } + + /* Setup buffer: page byte is not sent */ + buf =3D kzalloc(--count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + buf[0] =3D reg; + for (i =3D 0 ; i < count - 1 ; i++) + buf[i + 1] =3D bytes[i + 2]; + + /* Write register and data: I2C address =3D I2C base address + Page index= */ + msg.addr =3D client->addr + page; + msg.flags =3D client->flags & I2C_M_TEN; + msg.len =3D count; + msg.buf =3D buf; + + ret =3D tps65224_i2c_transfer(client->adapter, &msg, 1); + + kfree(buf); + return ret; +} + +static const struct regmap_config tps65224_i2c_regmap_config =3D { + .reg_bits =3D 16, + .val_bits =3D 8, + .max_register =3D TPS65224_REG_WD_FAIL_CNT_REG, + .volatile_reg =3D tps65224_is_volatile_reg, + .read =3D tps65224_i2c_read, + .write =3D tps65224_i2c_write, +}; + +static const struct of_device_id tps65224_i2c_of_match_table[] =3D { + { .compatible =3D "ti,tps65224-q1", }, + {} +}; +MODULE_DEVICE_TABLE(of, tps65224_i2c_of_match_table); + +static int tps65224_i2c_probe(struct i2c_client *client) +{ + struct device *dev =3D &client->dev; + struct tps65224 *tps; + const struct of_device_id *match; + + tps =3D devm_kzalloc(dev, sizeof(*tps), GFP_KERNEL); + if (!tps) + return -ENOMEM; + + i2c_set_clientdata(client, tps); + + tps->dev =3D dev; + tps->reg =3D client->addr; + tps->irq =3D client->irq; + + tps->regmap =3D devm_regmap_init(dev, NULL, client, &tps65224_i2c_regmap_= config); + if (IS_ERR(tps->regmap)) + return dev_err_probe(dev, PTR_ERR(tps->regmap), "Failed to init regmap\n= "); + + match =3D of_match_device(tps65224_i2c_of_match_table, dev); + if (!match) + return dev_err_probe(dev, -EINVAL, "Failed to find matching chip ID\n"); + + crc8_populate_msb(tps65224_i2c_crc_table, TPS65224_CRC8_POLYNOMIAL); + + return tps65224_device_init(tps, enable_crc); +} + +static struct i2c_driver tps65224_i2c_driver =3D { + .driver =3D { + .name =3D "tps65224", + .of_match_table =3D tps65224_i2c_of_match_table, + }, + .probe =3D tps65224_i2c_probe, +}; +module_i2c_driver(tps65224_i2c_driver); + +MODULE_AUTHOR("Gairuboina Sirisha