1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Hardware monitoring driver for Texas Instruments TPS53679 |
4 | * |
5 | * Copyright (c) 2017 Mellanox Technologies. All rights reserved. |
6 | * Copyright (c) 2017 Vadim Pasternak <vadimp@mellanox.com> |
7 | */ |
8 | |
9 | #include <linux/bits.h> |
10 | #include <linux/err.h> |
11 | #include <linux/i2c.h> |
12 | #include <linux/init.h> |
13 | #include <linux/kernel.h> |
14 | #include <linux/module.h> |
15 | #include <linux/of.h> |
16 | #include "pmbus.h" |
17 | |
18 | enum chips { |
19 | tps53647, tps53667, tps53676, tps53679, tps53681, tps53688 |
20 | }; |
21 | |
22 | #define TPS53647_PAGE_NUM 1 |
23 | |
24 | #define TPS53676_USER_DATA_03 0xb3 |
25 | #define TPS53676_MAX_PHASES 7 |
26 | |
27 | #define TPS53679_PROT_VR12_5MV 0x01 /* VR12.0 mode, 5-mV DAC */ |
28 | #define TPS53679_PROT_VR12_5_10MV 0x02 /* VR12.5 mode, 10-mV DAC */ |
29 | #define TPS53679_PROT_VR13_10MV 0x04 /* VR13.0 mode, 10-mV DAC */ |
30 | #define TPS53679_PROT_IMVP8_5MV 0x05 /* IMVP8 mode, 5-mV DAC */ |
31 | #define TPS53679_PROT_VR13_5MV 0x07 /* VR13.0 mode, 5-mV DAC */ |
32 | #define TPS53679_PAGE_NUM 2 |
33 | |
34 | #define TPS53681_DEVICE_ID 0x81 |
35 | |
36 | #define TPS53681_PMBUS_REVISION 0x33 |
37 | |
38 | #define TPS53681_MFR_SPECIFIC_20 0xe4 /* Number of phases, per page */ |
39 | |
40 | static const struct i2c_device_id tps53679_id[]; |
41 | |
42 | static int tps53679_identify_mode(struct i2c_client *client, |
43 | struct pmbus_driver_info *info) |
44 | { |
45 | u8 vout_params; |
46 | int i, ret; |
47 | |
48 | for (i = 0; i < info->pages; i++) { |
49 | /* Read the register with VOUT scaling value.*/ |
50 | ret = pmbus_read_byte_data(client, page: i, reg: PMBUS_VOUT_MODE); |
51 | if (ret < 0) |
52 | return ret; |
53 | |
54 | vout_params = ret & GENMASK(4, 0); |
55 | |
56 | switch (vout_params) { |
57 | case TPS53679_PROT_VR13_10MV: |
58 | case TPS53679_PROT_VR12_5_10MV: |
59 | info->vrm_version[i] = vr13; |
60 | break; |
61 | case TPS53679_PROT_VR13_5MV: |
62 | case TPS53679_PROT_VR12_5MV: |
63 | case TPS53679_PROT_IMVP8_5MV: |
64 | info->vrm_version[i] = vr12; |
65 | break; |
66 | default: |
67 | return -EINVAL; |
68 | } |
69 | } |
70 | |
71 | return 0; |
72 | } |
73 | |
74 | static int tps53679_identify_phases(struct i2c_client *client, |
75 | struct pmbus_driver_info *info) |
76 | { |
77 | int ret; |
78 | |
79 | /* On TPS53681, only channel A provides per-phase output current */ |
80 | ret = pmbus_read_byte_data(client, page: 0, TPS53681_MFR_SPECIFIC_20); |
81 | if (ret < 0) |
82 | return ret; |
83 | info->phases[0] = (ret & 0x07) + 1; |
84 | |
85 | return 0; |
86 | } |
87 | |
88 | static int tps53679_identify_chip(struct i2c_client *client, |
89 | u8 revision, u16 id) |
90 | { |
91 | u8 buf[I2C_SMBUS_BLOCK_MAX]; |
92 | int ret; |
93 | |
94 | ret = pmbus_read_byte_data(client, page: 0, reg: PMBUS_REVISION); |
95 | if (ret < 0) |
96 | return ret; |
97 | if (ret != revision) { |
98 | dev_err(&client->dev, "Unexpected PMBus revision 0x%x\n" , ret); |
99 | return -ENODEV; |
100 | } |
101 | |
102 | ret = i2c_smbus_read_block_data(client, command: PMBUS_IC_DEVICE_ID, values: buf); |
103 | if (ret < 0) |
104 | return ret; |
105 | if (ret != 1 || buf[0] != id) { |
106 | dev_err(&client->dev, "Unexpected device ID 0x%x\n" , buf[0]); |
107 | return -ENODEV; |
108 | } |
109 | return 0; |
110 | } |
111 | |
112 | /* |
113 | * Common identification function for chips with multi-phase support. |
114 | * Since those chips have special configuration registers, we want to have |
115 | * some level of reassurance that we are really talking with the chip |
116 | * being probed. Check PMBus revision and chip ID. |
117 | */ |
118 | static int tps53679_identify_multiphase(struct i2c_client *client, |
119 | struct pmbus_driver_info *info, |
120 | int pmbus_rev, int device_id) |
121 | { |
122 | int ret; |
123 | |
124 | ret = tps53679_identify_chip(client, revision: pmbus_rev, id: device_id); |
125 | if (ret < 0) |
126 | return ret; |
127 | |
128 | ret = tps53679_identify_mode(client, info); |
129 | if (ret < 0) |
130 | return ret; |
131 | |
132 | return tps53679_identify_phases(client, info); |
133 | } |
134 | |
135 | static int tps53679_identify(struct i2c_client *client, |
136 | struct pmbus_driver_info *info) |
137 | { |
138 | return tps53679_identify_mode(client, info); |
139 | } |
140 | |
141 | static int tps53681_identify(struct i2c_client *client, |
142 | struct pmbus_driver_info *info) |
143 | { |
144 | return tps53679_identify_multiphase(client, info, |
145 | TPS53681_PMBUS_REVISION, |
146 | TPS53681_DEVICE_ID); |
147 | } |
148 | |
149 | static int tps53676_identify(struct i2c_client *client, |
150 | struct pmbus_driver_info *info) |
151 | { |
152 | u8 buf[I2C_SMBUS_BLOCK_MAX]; |
153 | int phases_a = 0, phases_b = 0; |
154 | int i, ret; |
155 | |
156 | ret = i2c_smbus_read_block_data(client, command: PMBUS_IC_DEVICE_ID, values: buf); |
157 | if (ret < 0) |
158 | return ret; |
159 | if (strncmp("TI\x53\x67\x60" , buf, 5)) { |
160 | dev_err(&client->dev, "Unexpected device ID: %s\n" , buf); |
161 | return -ENODEV; |
162 | } |
163 | |
164 | ret = i2c_smbus_read_block_data(client, TPS53676_USER_DATA_03, values: buf); |
165 | if (ret < 0) |
166 | return ret; |
167 | if (ret != 24) |
168 | return -EIO; |
169 | for (i = 0; i < 2 * TPS53676_MAX_PHASES; i += 2) { |
170 | if (buf[i + 1] & 0x80) { |
171 | if (buf[i] & 0x08) |
172 | phases_b++; |
173 | else |
174 | phases_a++; |
175 | } |
176 | } |
177 | |
178 | info->format[PSC_VOLTAGE_OUT] = linear; |
179 | info->pages = 1; |
180 | info->phases[0] = phases_a; |
181 | if (phases_b > 0) { |
182 | info->pages = 2; |
183 | info->phases[1] = phases_b; |
184 | } |
185 | return 0; |
186 | } |
187 | |
188 | static int tps53681_read_word_data(struct i2c_client *client, int page, |
189 | int phase, int reg) |
190 | { |
191 | /* |
192 | * For reading the total output current (READ_IOUT) for all phases, |
193 | * the chip datasheet is a bit vague. It says "PHASE must be set to |
194 | * FFh to access all phases simultaneously. PHASE may also be set to |
195 | * 80h readack (!) the total phase current". |
196 | * Experiments show that the command does _not_ report the total |
197 | * current for all phases if the phase is set to 0xff. Instead, it |
198 | * appears to report the current of one of the phases. Override phase |
199 | * parameter with 0x80 when reading the total output current on page 0. |
200 | */ |
201 | if (reg == PMBUS_READ_IOUT && page == 0 && phase == 0xff) |
202 | return pmbus_read_word_data(client, page, phase: 0x80, reg); |
203 | return -ENODATA; |
204 | } |
205 | |
206 | static struct pmbus_driver_info tps53679_info = { |
207 | .format[PSC_VOLTAGE_IN] = linear, |
208 | .format[PSC_VOLTAGE_OUT] = vid, |
209 | .format[PSC_TEMPERATURE] = linear, |
210 | .format[PSC_CURRENT_OUT] = linear, |
211 | .format[PSC_POWER] = linear, |
212 | .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | |
213 | PMBUS_HAVE_STATUS_INPUT | |
214 | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | |
215 | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | |
216 | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | |
217 | PMBUS_HAVE_POUT, |
218 | .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | |
219 | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | |
220 | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | |
221 | PMBUS_HAVE_POUT, |
222 | .pfunc[0] = PMBUS_HAVE_IOUT, |
223 | .pfunc[1] = PMBUS_HAVE_IOUT, |
224 | .pfunc[2] = PMBUS_HAVE_IOUT, |
225 | .pfunc[3] = PMBUS_HAVE_IOUT, |
226 | .pfunc[4] = PMBUS_HAVE_IOUT, |
227 | .pfunc[5] = PMBUS_HAVE_IOUT, |
228 | .pfunc[6] = PMBUS_HAVE_IOUT, |
229 | }; |
230 | |
231 | static int tps53679_probe(struct i2c_client *client) |
232 | { |
233 | struct device *dev = &client->dev; |
234 | struct pmbus_driver_info *info; |
235 | enum chips chip_id; |
236 | |
237 | if (dev->of_node) |
238 | chip_id = (uintptr_t)of_device_get_match_data(dev); |
239 | else |
240 | chip_id = i2c_match_id(id: tps53679_id, client)->driver_data; |
241 | |
242 | info = devm_kmemdup(dev, src: &tps53679_info, len: sizeof(*info), GFP_KERNEL); |
243 | if (!info) |
244 | return -ENOMEM; |
245 | |
246 | switch (chip_id) { |
247 | case tps53647: |
248 | case tps53667: |
249 | info->pages = TPS53647_PAGE_NUM; |
250 | info->identify = tps53679_identify; |
251 | break; |
252 | case tps53676: |
253 | info->identify = tps53676_identify; |
254 | break; |
255 | case tps53679: |
256 | case tps53688: |
257 | info->pages = TPS53679_PAGE_NUM; |
258 | info->identify = tps53679_identify; |
259 | break; |
260 | case tps53681: |
261 | info->pages = TPS53679_PAGE_NUM; |
262 | info->phases[0] = 6; |
263 | info->identify = tps53681_identify; |
264 | info->read_word_data = tps53681_read_word_data; |
265 | break; |
266 | default: |
267 | return -ENODEV; |
268 | } |
269 | |
270 | return pmbus_do_probe(client, info); |
271 | } |
272 | |
273 | static const struct i2c_device_id tps53679_id[] = { |
274 | {"bmr474" , tps53676}, |
275 | {"tps53647" , tps53647}, |
276 | {"tps53667" , tps53667}, |
277 | {"tps53676" , tps53676}, |
278 | {"tps53679" , tps53679}, |
279 | {"tps53681" , tps53681}, |
280 | {"tps53688" , tps53688}, |
281 | {} |
282 | }; |
283 | |
284 | MODULE_DEVICE_TABLE(i2c, tps53679_id); |
285 | |
286 | static const struct of_device_id __maybe_unused tps53679_of_match[] = { |
287 | {.compatible = "ti,tps53647" , .data = (void *)tps53647}, |
288 | {.compatible = "ti,tps53667" , .data = (void *)tps53667}, |
289 | {.compatible = "ti,tps53676" , .data = (void *)tps53676}, |
290 | {.compatible = "ti,tps53679" , .data = (void *)tps53679}, |
291 | {.compatible = "ti,tps53681" , .data = (void *)tps53681}, |
292 | {.compatible = "ti,tps53688" , .data = (void *)tps53688}, |
293 | {} |
294 | }; |
295 | MODULE_DEVICE_TABLE(of, tps53679_of_match); |
296 | |
297 | static struct i2c_driver tps53679_driver = { |
298 | .driver = { |
299 | .name = "tps53679" , |
300 | .of_match_table = of_match_ptr(tps53679_of_match), |
301 | }, |
302 | .probe = tps53679_probe, |
303 | .id_table = tps53679_id, |
304 | }; |
305 | |
306 | module_i2c_driver(tps53679_driver); |
307 | |
308 | MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>" ); |
309 | MODULE_DESCRIPTION("PMBus driver for Texas Instruments TPS53679" ); |
310 | MODULE_LICENSE("GPL" ); |
311 | MODULE_IMPORT_NS(PMBUS); |
312 | |