Skip to content

Commit 2f2f43d

Browse files
committed
TI TPS6594 PMIC support (RTC, pinctrl, regulators)
Merge series from Esteban Blanc <[email protected]>: TPS6594 is a Power Management IC which provides regulators and others features like GPIOs, RTC, watchdog, ESMs (Error Signal Monitor), and PFSM (Pre-configurable Finite State Machine). The SoC and the PMIC can communicate through the I2C or SPI interfaces. TPS6594 is the super-set device while TPS6593 and LP8764 are derivatives. This series adds support to TI TPS6594 PMIC and its derivatives.
2 parents 30e15cb + f17ccc5 commit 2f2f43d

File tree

9 files changed

+2519
-0
lines changed

9 files changed

+2519
-0
lines changed

drivers/mfd/Kconfig

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1698,6 +1698,38 @@ config MFD_TPS65912_SPI
16981698
If you say yes here you get support for the TPS65912 series of
16991699
PM chips with SPI interface.
17001700

1701+
config MFD_TPS6594
1702+
tristate
1703+
select MFD_CORE
1704+
select REGMAP
1705+
select REGMAP_IRQ
1706+
1707+
config MFD_TPS6594_I2C
1708+
tristate "TI TPS6594 Power Management chip with I2C"
1709+
select MFD_TPS6594
1710+
select REGMAP_I2C
1711+
select CRC8
1712+
depends on I2C
1713+
help
1714+
If you say yes here you get support for the TPS6594 series of
1715+
PM chips with I2C interface.
1716+
1717+
This driver can also be built as a module. If so, the module
1718+
will be called tps6594-i2c.
1719+
1720+
config MFD_TPS6594_SPI
1721+
tristate "TI TPS6594 Power Management chip with SPI"
1722+
select MFD_TPS6594
1723+
select REGMAP_SPI
1724+
select CRC8
1725+
depends on SPI_MASTER
1726+
help
1727+
If you say yes here you get support for the TPS6594 series of
1728+
PM chips with SPI interface.
1729+
1730+
This driver can also be built as a module. If so, the module
1731+
will be called tps6594-spi.
1732+
17011733
config TWL4030_CORE
17021734
bool "TI TWL4030/TWL5030/TWL6030/TPS659x0 Support"
17031735
depends on I2C=y

drivers/mfd/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ obj-$(CONFIG_MFD_TPS65910) += tps65910.o
9696
obj-$(CONFIG_MFD_TPS65912) += tps65912-core.o
9797
obj-$(CONFIG_MFD_TPS65912_I2C) += tps65912-i2c.o
9898
obj-$(CONFIG_MFD_TPS65912_SPI) += tps65912-spi.o
99+
obj-$(CONFIG_MFD_TPS6594) += tps6594-core.o
100+
obj-$(CONFIG_MFD_TPS6594_I2C) += tps6594-i2c.o
101+
obj-$(CONFIG_MFD_TPS6594_SPI) += tps6594-spi.o
99102
obj-$(CONFIG_MENELAUS) += menelaus.o
100103

101104
obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o

drivers/mfd/tps6594-core.c

Lines changed: 462 additions & 0 deletions
Large diffs are not rendered by default.

drivers/mfd/tps6594-i2c.c

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* I2C access driver for TI TPS6594/TPS6593/LP8764 PMICs
4+
*
5+
* Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/
6+
*/
7+
8+
#include <linux/crc8.h>
9+
#include <linux/i2c.h>
10+
#include <linux/module.h>
11+
#include <linux/mod_devicetable.h>
12+
#include <linux/of_device.h>
13+
#include <linux/regmap.h>
14+
15+
#include <linux/mfd/tps6594.h>
16+
17+
static bool enable_crc;
18+
module_param(enable_crc, bool, 0444);
19+
MODULE_PARM_DESC(enable_crc, "Enable CRC feature for I2C interface");
20+
21+
DECLARE_CRC8_TABLE(tps6594_i2c_crc_table);
22+
23+
static int tps6594_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
24+
{
25+
int ret = i2c_transfer(adap, msgs, num);
26+
27+
if (ret == num)
28+
return 0;
29+
else if (ret < 0)
30+
return ret;
31+
else
32+
return -EIO;
33+
}
34+
35+
static int tps6594_i2c_reg_read_with_crc(struct i2c_client *client, u8 page, u8 reg, u8 *val)
36+
{
37+
struct i2c_msg msgs[2];
38+
u8 buf_rx[] = { 0, 0 };
39+
/* I2C address = I2C base address + Page index */
40+
const u8 addr = client->addr + page;
41+
/*
42+
* CRC is calculated from every bit included in the protocol
43+
* except the ACK bits from the target. Byte stream is:
44+
* - B0: (I2C_addr_7bits << 1) | WR_bit, with WR_bit = 0
45+
* - B1: reg
46+
* - B2: (I2C_addr_7bits << 1) | RD_bit, with RD_bit = 1
47+
* - B3: val
48+
* - B4: CRC from B0-B1-B2-B3
49+
*/
50+
u8 crc_data[] = { addr << 1, reg, addr << 1 | 1, 0 };
51+
int ret;
52+
53+
/* Write register */
54+
msgs[0].addr = addr;
55+
msgs[0].flags = 0;
56+
msgs[0].len = 1;
57+
msgs[0].buf = &reg;
58+
59+
/* Read data and CRC */
60+
msgs[1].addr = msgs[0].addr;
61+
msgs[1].flags = I2C_M_RD;
62+
msgs[1].len = 2;
63+
msgs[1].buf = buf_rx;
64+
65+
ret = tps6594_i2c_transfer(client->adapter, msgs, 2);
66+
if (ret < 0)
67+
return ret;
68+
69+
crc_data[sizeof(crc_data) - 1] = *val = buf_rx[0];
70+
if (buf_rx[1] != crc8(tps6594_i2c_crc_table, crc_data, sizeof(crc_data), CRC8_INIT_VALUE))
71+
return -EIO;
72+
73+
return ret;
74+
}
75+
76+
static int tps6594_i2c_reg_write_with_crc(struct i2c_client *client, u8 page, u8 reg, u8 val)
77+
{
78+
struct i2c_msg msg;
79+
u8 buf[] = { reg, val, 0 };
80+
/* I2C address = I2C base address + Page index */
81+
const u8 addr = client->addr + page;
82+
/*
83+
* CRC is calculated from every bit included in the protocol
84+
* except the ACK bits from the target. Byte stream is:
85+
* - B0: (I2C_addr_7bits << 1) | WR_bit, with WR_bit = 0
86+
* - B1: reg
87+
* - B2: val
88+
* - B3: CRC from B0-B1-B2
89+
*/
90+
const u8 crc_data[] = { addr << 1, reg, val };
91+
92+
/* Write register, data and CRC */
93+
msg.addr = addr;
94+
msg.flags = client->flags & I2C_M_TEN;
95+
msg.len = sizeof(buf);
96+
msg.buf = buf;
97+
98+
buf[msg.len - 1] = crc8(tps6594_i2c_crc_table, crc_data, sizeof(crc_data), CRC8_INIT_VALUE);
99+
100+
return tps6594_i2c_transfer(client->adapter, &msg, 1);
101+
}
102+
103+
static int tps6594_i2c_read(void *context, const void *reg_buf, size_t reg_size,
104+
void *val_buf, size_t val_size)
105+
{
106+
struct i2c_client *client = context;
107+
struct tps6594 *tps = i2c_get_clientdata(client);
108+
struct i2c_msg msgs[2];
109+
const u8 *reg_bytes = reg_buf;
110+
u8 *val_bytes = val_buf;
111+
const u8 page = reg_bytes[1];
112+
u8 reg = reg_bytes[0];
113+
int ret = 0;
114+
int i;
115+
116+
if (tps->use_crc) {
117+
/*
118+
* Auto-increment feature does not support CRC protocol.
119+
* Converts the bulk read operation into a series of single read operations.
120+
*/
121+
for (i = 0 ; ret == 0 && i < val_size ; i++)
122+
ret = tps6594_i2c_reg_read_with_crc(client, page, reg + i, val_bytes + i);
123+
124+
return ret;
125+
}
126+
127+
/* Write register: I2C address = I2C base address + Page index */
128+
msgs[0].addr = client->addr + page;
129+
msgs[0].flags = 0;
130+
msgs[0].len = 1;
131+
msgs[0].buf = &reg;
132+
133+
/* Read data */
134+
msgs[1].addr = msgs[0].addr;
135+
msgs[1].flags = I2C_M_RD;
136+
msgs[1].len = val_size;
137+
msgs[1].buf = val_bytes;
138+
139+
return tps6594_i2c_transfer(client->adapter, msgs, 2);
140+
}
141+
142+
static int tps6594_i2c_write(void *context, const void *data, size_t count)
143+
{
144+
struct i2c_client *client = context;
145+
struct tps6594 *tps = i2c_get_clientdata(client);
146+
struct i2c_msg msg;
147+
const u8 *bytes = data;
148+
u8 *buf;
149+
const u8 page = bytes[1];
150+
const u8 reg = bytes[0];
151+
int ret = 0;
152+
int i;
153+
154+
if (tps->use_crc) {
155+
/*
156+
* Auto-increment feature does not support CRC protocol.
157+
* Converts the bulk write operation into a series of single write operations.
158+
*/
159+
for (i = 0 ; ret == 0 && i < count - 2 ; i++)
160+
ret = tps6594_i2c_reg_write_with_crc(client, page, reg + i, bytes[i + 2]);
161+
162+
return ret;
163+
}
164+
165+
/* Setup buffer: page byte is not sent */
166+
buf = kzalloc(--count, GFP_KERNEL);
167+
if (!buf)
168+
return -ENOMEM;
169+
170+
buf[0] = reg;
171+
for (i = 0 ; i < count - 1 ; i++)
172+
buf[i + 1] = bytes[i + 2];
173+
174+
/* Write register and data: I2C address = I2C base address + Page index */
175+
msg.addr = client->addr + page;
176+
msg.flags = client->flags & I2C_M_TEN;
177+
msg.len = count;
178+
msg.buf = buf;
179+
180+
ret = tps6594_i2c_transfer(client->adapter, &msg, 1);
181+
182+
kfree(buf);
183+
return ret;
184+
}
185+
186+
static const struct regmap_config tps6594_i2c_regmap_config = {
187+
.reg_bits = 16,
188+
.val_bits = 8,
189+
.max_register = TPS6594_REG_DWD_FAIL_CNT_REG,
190+
.volatile_reg = tps6594_is_volatile_reg,
191+
.read = tps6594_i2c_read,
192+
.write = tps6594_i2c_write,
193+
};
194+
195+
static const struct of_device_id tps6594_i2c_of_match_table[] = {
196+
{ .compatible = "ti,tps6594-q1", .data = (void *)TPS6594, },
197+
{ .compatible = "ti,tps6593-q1", .data = (void *)TPS6593, },
198+
{ .compatible = "ti,lp8764-q1", .data = (void *)LP8764, },
199+
{}
200+
};
201+
MODULE_DEVICE_TABLE(of, tps6594_i2c_of_match_table);
202+
203+
static int tps6594_i2c_probe(struct i2c_client *client)
204+
{
205+
struct device *dev = &client->dev;
206+
struct tps6594 *tps;
207+
const struct of_device_id *match;
208+
209+
tps = devm_kzalloc(dev, sizeof(*tps), GFP_KERNEL);
210+
if (!tps)
211+
return -ENOMEM;
212+
213+
i2c_set_clientdata(client, tps);
214+
215+
tps->dev = dev;
216+
tps->reg = client->addr;
217+
tps->irq = client->irq;
218+
219+
tps->regmap = devm_regmap_init(dev, NULL, client, &tps6594_i2c_regmap_config);
220+
if (IS_ERR(tps->regmap))
221+
return dev_err_probe(dev, PTR_ERR(tps->regmap), "Failed to init regmap\n");
222+
223+
match = of_match_device(tps6594_i2c_of_match_table, dev);
224+
if (!match)
225+
return dev_err_probe(dev, PTR_ERR(match), "Failed to find matching chip ID\n");
226+
tps->chip_id = (unsigned long)match->data;
227+
228+
crc8_populate_msb(tps6594_i2c_crc_table, TPS6594_CRC8_POLYNOMIAL);
229+
230+
return tps6594_device_init(tps, enable_crc);
231+
}
232+
233+
static struct i2c_driver tps6594_i2c_driver = {
234+
.driver = {
235+
.name = "tps6594",
236+
.of_match_table = tps6594_i2c_of_match_table,
237+
},
238+
.probe_new = tps6594_i2c_probe,
239+
};
240+
module_i2c_driver(tps6594_i2c_driver);
241+
242+
MODULE_AUTHOR("Julien Panis <[email protected]>");
243+
MODULE_DESCRIPTION("TPS6594 I2C Interface Driver");
244+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)