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 = ® |
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(adap: client->adapter, msgs, num: 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(table: tps6594_i2c_crc_table, pdata: crc_data, nbytes: 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(table: tps6594_i2c_crc_table, pdata: crc_data, nbytes: sizeof(crc_data), CRC8_INIT_VALUE); |
99 | |
100 | return tps6594_i2c_transfer(adap: client->adapter, msgs: &msg, num: 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: reg + i, val: 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 = ® |
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(adap: client->adapter, msgs, num: 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: reg + i, val: bytes[i + 2]); |
161 | |
162 | return ret; |
163 | } |
164 | |
165 | /* Setup buffer: page byte is not sent */ |
166 | buf = kzalloc(size: --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(adap: client->adapter, msgs: &msg, num: 1); |
181 | |
182 | kfree(objp: 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, size: sizeof(*tps), GFP_KERNEL); |
210 | if (!tps) |
211 | return -ENOMEM; |
212 | |
213 | i2c_set_clientdata(client, data: 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(ptr: tps->regmap)) |
221 | return dev_err_probe(dev, err: PTR_ERR(ptr: tps->regmap), fmt: "Failed to init regmap\n" ); |
222 | |
223 | match = of_match_device(matches: tps6594_i2c_of_match_table, dev); |
224 | if (!match) |
225 | return dev_err_probe(dev, err: -EINVAL, fmt: "Failed to find matching chip ID\n" ); |
226 | tps->chip_id = (unsigned long)match->data; |
227 | |
228 | crc8_populate_msb(table: 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 = tps6594_i2c_probe, |
239 | }; |
240 | module_i2c_driver(tps6594_i2c_driver); |
241 | |
242 | MODULE_AUTHOR("Julien Panis <jpanis@baylibre.com>" ); |
243 | MODULE_DESCRIPTION("TPS6594 I2C Interface Driver" ); |
244 | MODULE_LICENSE("GPL" ); |
245 | |