1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Driver for Delta modules, Q54SJ108A2 series 1/4 Brick DC/DC |
4 | * Regulated Power Module |
5 | * |
6 | * Copyright 2020 Delta LLC. |
7 | */ |
8 | |
9 | #include <linux/debugfs.h> |
10 | #include <linux/i2c.h> |
11 | #include <linux/kstrtox.h> |
12 | #include <linux/module.h> |
13 | #include <linux/of.h> |
14 | #include "pmbus.h" |
15 | |
16 | #define STORE_DEFAULT_ALL 0x11 |
17 | #define ERASE_BLACKBOX_DATA 0xD1 |
18 | #define READ_HISTORY_EVENT_NUMBER 0xD2 |
19 | #define READ_HISTORY_EVENTS 0xE0 |
20 | #define SET_HISTORY_EVENT_OFFSET 0xE1 |
21 | #define PMBUS_FLASH_KEY_WRITE 0xEC |
22 | |
23 | enum chips { |
24 | q54sj108a2 |
25 | }; |
26 | |
27 | enum { |
28 | Q54SJ108A2_DEBUGFS_OPERATION = 0, |
29 | Q54SJ108A2_DEBUGFS_CLEARFAULT, |
30 | Q54SJ108A2_DEBUGFS_WRITEPROTECT, |
31 | Q54SJ108A2_DEBUGFS_STOREDEFAULT, |
32 | Q54SJ108A2_DEBUGFS_VOOV_RESPONSE, |
33 | Q54SJ108A2_DEBUGFS_IOOC_RESPONSE, |
34 | Q54SJ108A2_DEBUGFS_PMBUS_VERSION, |
35 | Q54SJ108A2_DEBUGFS_MFR_ID, |
36 | Q54SJ108A2_DEBUGFS_MFR_MODEL, |
37 | Q54SJ108A2_DEBUGFS_MFR_REVISION, |
38 | Q54SJ108A2_DEBUGFS_MFR_LOCATION, |
39 | Q54SJ108A2_DEBUGFS_BLACKBOX_ERASE, |
40 | Q54SJ108A2_DEBUGFS_BLACKBOX_READ_OFFSET, |
41 | Q54SJ108A2_DEBUGFS_BLACKBOX_SET_OFFSET, |
42 | Q54SJ108A2_DEBUGFS_BLACKBOX_READ, |
43 | Q54SJ108A2_DEBUGFS_FLASH_KEY, |
44 | Q54SJ108A2_DEBUGFS_NUM_ENTRIES |
45 | }; |
46 | |
47 | struct q54sj108a2_data { |
48 | enum chips chip; |
49 | struct i2c_client *client; |
50 | |
51 | int debugfs_entries[Q54SJ108A2_DEBUGFS_NUM_ENTRIES]; |
52 | }; |
53 | |
54 | #define to_psu(x, y) container_of((x), struct q54sj108a2_data, debugfs_entries[(y)]) |
55 | |
56 | static struct pmbus_driver_info q54sj108a2_info[] = { |
57 | [q54sj108a2] = { |
58 | .pages = 1, |
59 | |
60 | /* Source : Delta Q54SJ108A2 */ |
61 | .format[PSC_TEMPERATURE] = linear, |
62 | .format[PSC_VOLTAGE_IN] = linear, |
63 | .format[PSC_CURRENT_OUT] = linear, |
64 | |
65 | .func[0] = PMBUS_HAVE_VIN | |
66 | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | |
67 | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | |
68 | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | |
69 | PMBUS_HAVE_STATUS_INPUT, |
70 | }, |
71 | }; |
72 | |
73 | static ssize_t q54sj108a2_debugfs_read(struct file *file, char __user *buf, |
74 | size_t count, loff_t *ppos) |
75 | { |
76 | int rc; |
77 | int *idxp = file->private_data; |
78 | int idx = *idxp; |
79 | struct q54sj108a2_data *psu = to_psu(idxp, idx); |
80 | char data[I2C_SMBUS_BLOCK_MAX + 2] = { 0 }; |
81 | char data_char[I2C_SMBUS_BLOCK_MAX + 2] = { 0 }; |
82 | char *res; |
83 | |
84 | switch (idx) { |
85 | case Q54SJ108A2_DEBUGFS_OPERATION: |
86 | rc = i2c_smbus_read_byte_data(client: psu->client, command: PMBUS_OPERATION); |
87 | if (rc < 0) |
88 | return rc; |
89 | |
90 | rc = snprintf(buf: data, size: 3, fmt: "%02x" , rc); |
91 | break; |
92 | case Q54SJ108A2_DEBUGFS_WRITEPROTECT: |
93 | rc = i2c_smbus_read_byte_data(client: psu->client, command: PMBUS_WRITE_PROTECT); |
94 | if (rc < 0) |
95 | return rc; |
96 | |
97 | rc = snprintf(buf: data, size: 3, fmt: "%02x" , rc); |
98 | break; |
99 | case Q54SJ108A2_DEBUGFS_VOOV_RESPONSE: |
100 | rc = i2c_smbus_read_byte_data(client: psu->client, command: PMBUS_VOUT_OV_FAULT_RESPONSE); |
101 | if (rc < 0) |
102 | return rc; |
103 | |
104 | rc = snprintf(buf: data, size: 3, fmt: "%02x" , rc); |
105 | break; |
106 | case Q54SJ108A2_DEBUGFS_IOOC_RESPONSE: |
107 | rc = i2c_smbus_read_byte_data(client: psu->client, command: PMBUS_IOUT_OC_FAULT_RESPONSE); |
108 | if (rc < 0) |
109 | return rc; |
110 | |
111 | rc = snprintf(buf: data, size: 3, fmt: "%02x" , rc); |
112 | break; |
113 | case Q54SJ108A2_DEBUGFS_PMBUS_VERSION: |
114 | rc = i2c_smbus_read_byte_data(client: psu->client, command: PMBUS_REVISION); |
115 | if (rc < 0) |
116 | return rc; |
117 | |
118 | rc = snprintf(buf: data, size: 3, fmt: "%02x" , rc); |
119 | break; |
120 | case Q54SJ108A2_DEBUGFS_MFR_ID: |
121 | rc = i2c_smbus_read_block_data(client: psu->client, command: PMBUS_MFR_ID, values: data); |
122 | if (rc < 0) |
123 | return rc; |
124 | break; |
125 | case Q54SJ108A2_DEBUGFS_MFR_MODEL: |
126 | rc = i2c_smbus_read_block_data(client: psu->client, command: PMBUS_MFR_MODEL, values: data); |
127 | if (rc < 0) |
128 | return rc; |
129 | break; |
130 | case Q54SJ108A2_DEBUGFS_MFR_REVISION: |
131 | rc = i2c_smbus_read_block_data(client: psu->client, command: PMBUS_MFR_REVISION, values: data); |
132 | if (rc < 0) |
133 | return rc; |
134 | break; |
135 | case Q54SJ108A2_DEBUGFS_MFR_LOCATION: |
136 | rc = i2c_smbus_read_block_data(client: psu->client, command: PMBUS_MFR_LOCATION, values: data); |
137 | if (rc < 0) |
138 | return rc; |
139 | break; |
140 | case Q54SJ108A2_DEBUGFS_BLACKBOX_READ_OFFSET: |
141 | rc = i2c_smbus_read_byte_data(client: psu->client, READ_HISTORY_EVENT_NUMBER); |
142 | if (rc < 0) |
143 | return rc; |
144 | |
145 | rc = snprintf(buf: data, size: 3, fmt: "%02x" , rc); |
146 | break; |
147 | case Q54SJ108A2_DEBUGFS_BLACKBOX_READ: |
148 | rc = i2c_smbus_read_block_data(client: psu->client, READ_HISTORY_EVENTS, values: data); |
149 | if (rc < 0) |
150 | return rc; |
151 | |
152 | res = bin2hex(dst: data, src: data_char, count: 32); |
153 | rc = res - data; |
154 | |
155 | break; |
156 | case Q54SJ108A2_DEBUGFS_FLASH_KEY: |
157 | rc = i2c_smbus_read_block_data(client: psu->client, PMBUS_FLASH_KEY_WRITE, values: data); |
158 | if (rc < 0) |
159 | return rc; |
160 | |
161 | res = bin2hex(dst: data, src: data_char, count: 4); |
162 | rc = res - data; |
163 | |
164 | break; |
165 | default: |
166 | return -EINVAL; |
167 | } |
168 | |
169 | data[rc] = '\n'; |
170 | rc += 2; |
171 | |
172 | return simple_read_from_buffer(to: buf, count, ppos, from: data, available: rc); |
173 | } |
174 | |
175 | static ssize_t q54sj108a2_debugfs_write(struct file *file, const char __user *buf, |
176 | size_t count, loff_t *ppos) |
177 | { |
178 | u8 flash_key[4]; |
179 | u8 dst_data; |
180 | ssize_t rc; |
181 | int *idxp = file->private_data; |
182 | int idx = *idxp; |
183 | struct q54sj108a2_data *psu = to_psu(idxp, idx); |
184 | |
185 | rc = i2c_smbus_write_byte_data(client: psu->client, command: PMBUS_WRITE_PROTECT, value: 0); |
186 | if (rc) |
187 | return rc; |
188 | |
189 | switch (idx) { |
190 | case Q54SJ108A2_DEBUGFS_OPERATION: |
191 | rc = kstrtou8_from_user(s: buf, count, base: 0, res: &dst_data); |
192 | if (rc < 0) |
193 | return rc; |
194 | |
195 | rc = i2c_smbus_write_byte_data(client: psu->client, command: PMBUS_OPERATION, value: dst_data); |
196 | if (rc < 0) |
197 | return rc; |
198 | |
199 | break; |
200 | case Q54SJ108A2_DEBUGFS_CLEARFAULT: |
201 | rc = i2c_smbus_write_byte(client: psu->client, value: PMBUS_CLEAR_FAULTS); |
202 | if (rc < 0) |
203 | return rc; |
204 | |
205 | break; |
206 | case Q54SJ108A2_DEBUGFS_STOREDEFAULT: |
207 | flash_key[0] = 0x7E; |
208 | flash_key[1] = 0x15; |
209 | flash_key[2] = 0xDC; |
210 | flash_key[3] = 0x42; |
211 | rc = i2c_smbus_write_block_data(client: psu->client, PMBUS_FLASH_KEY_WRITE, length: 4, values: flash_key); |
212 | if (rc < 0) |
213 | return rc; |
214 | |
215 | rc = i2c_smbus_write_byte(client: psu->client, STORE_DEFAULT_ALL); |
216 | if (rc < 0) |
217 | return rc; |
218 | |
219 | break; |
220 | case Q54SJ108A2_DEBUGFS_VOOV_RESPONSE: |
221 | rc = kstrtou8_from_user(s: buf, count, base: 0, res: &dst_data); |
222 | if (rc < 0) |
223 | return rc; |
224 | |
225 | rc = i2c_smbus_write_byte_data(client: psu->client, command: PMBUS_VOUT_OV_FAULT_RESPONSE, value: dst_data); |
226 | if (rc < 0) |
227 | return rc; |
228 | |
229 | break; |
230 | case Q54SJ108A2_DEBUGFS_IOOC_RESPONSE: |
231 | rc = kstrtou8_from_user(s: buf, count, base: 0, res: &dst_data); |
232 | if (rc < 0) |
233 | return rc; |
234 | |
235 | rc = i2c_smbus_write_byte_data(client: psu->client, command: PMBUS_IOUT_OC_FAULT_RESPONSE, value: dst_data); |
236 | if (rc < 0) |
237 | return rc; |
238 | |
239 | break; |
240 | case Q54SJ108A2_DEBUGFS_BLACKBOX_ERASE: |
241 | rc = i2c_smbus_write_byte(client: psu->client, ERASE_BLACKBOX_DATA); |
242 | if (rc < 0) |
243 | return rc; |
244 | |
245 | break; |
246 | case Q54SJ108A2_DEBUGFS_BLACKBOX_SET_OFFSET: |
247 | rc = kstrtou8_from_user(s: buf, count, base: 0, res: &dst_data); |
248 | if (rc < 0) |
249 | return rc; |
250 | |
251 | rc = i2c_smbus_write_byte_data(client: psu->client, SET_HISTORY_EVENT_OFFSET, value: dst_data); |
252 | if (rc < 0) |
253 | return rc; |
254 | |
255 | break; |
256 | default: |
257 | return -EINVAL; |
258 | } |
259 | |
260 | return count; |
261 | } |
262 | |
263 | static const struct file_operations q54sj108a2_fops = { |
264 | .llseek = noop_llseek, |
265 | .read = q54sj108a2_debugfs_read, |
266 | .write = q54sj108a2_debugfs_write, |
267 | .open = simple_open, |
268 | }; |
269 | |
270 | static const struct i2c_device_id q54sj108a2_id[] = { |
271 | { "q54sj108a2" , q54sj108a2 }, |
272 | { }, |
273 | }; |
274 | |
275 | MODULE_DEVICE_TABLE(i2c, q54sj108a2_id); |
276 | |
277 | static int q54sj108a2_probe(struct i2c_client *client) |
278 | { |
279 | struct device *dev = &client->dev; |
280 | u8 buf[I2C_SMBUS_BLOCK_MAX + 1]; |
281 | enum chips chip_id; |
282 | int ret, i; |
283 | struct dentry *debugfs; |
284 | struct dentry *q54sj108a2_dir; |
285 | struct q54sj108a2_data *psu; |
286 | |
287 | if (!i2c_check_functionality(adap: client->adapter, |
288 | I2C_FUNC_SMBUS_BYTE_DATA | |
289 | I2C_FUNC_SMBUS_WORD_DATA | |
290 | I2C_FUNC_SMBUS_BLOCK_DATA)) |
291 | return -ENODEV; |
292 | |
293 | if (client->dev.of_node) |
294 | chip_id = (enum chips)(unsigned long)of_device_get_match_data(dev); |
295 | else |
296 | chip_id = i2c_match_id(id: q54sj108a2_id, client)->driver_data; |
297 | |
298 | ret = i2c_smbus_read_block_data(client, command: PMBUS_MFR_ID, values: buf); |
299 | if (ret < 0) { |
300 | dev_err(&client->dev, "Failed to read Manufacturer ID\n" ); |
301 | return ret; |
302 | } |
303 | if (ret != 6 || strncmp(buf, "DELTA" , 5)) { |
304 | buf[ret] = '\0'; |
305 | dev_err(dev, "Unsupported Manufacturer ID '%s'\n" , buf); |
306 | return -ENODEV; |
307 | } |
308 | |
309 | /* |
310 | * The chips support reading PMBUS_MFR_MODEL. |
311 | */ |
312 | ret = i2c_smbus_read_block_data(client, command: PMBUS_MFR_MODEL, values: buf); |
313 | if (ret < 0) { |
314 | dev_err(dev, "Failed to read Manufacturer Model\n" ); |
315 | return ret; |
316 | } |
317 | if (ret != 14 || strncmp(buf, "Q54SJ108A2" , 10)) { |
318 | buf[ret] = '\0'; |
319 | dev_err(dev, "Unsupported Manufacturer Model '%s'\n" , buf); |
320 | return -ENODEV; |
321 | } |
322 | |
323 | ret = i2c_smbus_read_block_data(client, command: PMBUS_MFR_REVISION, values: buf); |
324 | if (ret < 0) { |
325 | dev_err(dev, "Failed to read Manufacturer Revision\n" ); |
326 | return ret; |
327 | } |
328 | if (ret != 4 || buf[0] != 'S') { |
329 | buf[ret] = '\0'; |
330 | dev_err(dev, "Unsupported Manufacturer Revision '%s'\n" , buf); |
331 | return -ENODEV; |
332 | } |
333 | |
334 | ret = pmbus_do_probe(client, info: &q54sj108a2_info[chip_id]); |
335 | if (ret) |
336 | return ret; |
337 | |
338 | psu = devm_kzalloc(dev: &client->dev, size: sizeof(*psu), GFP_KERNEL); |
339 | if (!psu) |
340 | return 0; |
341 | |
342 | psu->client = client; |
343 | |
344 | debugfs = pmbus_get_debugfs_dir(client); |
345 | |
346 | q54sj108a2_dir = debugfs_create_dir(name: client->name, parent: debugfs); |
347 | |
348 | for (i = 0; i < Q54SJ108A2_DEBUGFS_NUM_ENTRIES; ++i) |
349 | psu->debugfs_entries[i] = i; |
350 | |
351 | debugfs_create_file(name: "operation" , mode: 0644, parent: q54sj108a2_dir, |
352 | data: &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_OPERATION], |
353 | fops: &q54sj108a2_fops); |
354 | debugfs_create_file(name: "clear_fault" , mode: 0200, parent: q54sj108a2_dir, |
355 | data: &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_CLEARFAULT], |
356 | fops: &q54sj108a2_fops); |
357 | debugfs_create_file(name: "write_protect" , mode: 0444, parent: q54sj108a2_dir, |
358 | data: &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_WRITEPROTECT], |
359 | fops: &q54sj108a2_fops); |
360 | debugfs_create_file(name: "store_default" , mode: 0200, parent: q54sj108a2_dir, |
361 | data: &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_STOREDEFAULT], |
362 | fops: &q54sj108a2_fops); |
363 | debugfs_create_file(name: "vo_ov_response" , mode: 0644, parent: q54sj108a2_dir, |
364 | data: &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_VOOV_RESPONSE], |
365 | fops: &q54sj108a2_fops); |
366 | debugfs_create_file(name: "io_oc_response" , mode: 0644, parent: q54sj108a2_dir, |
367 | data: &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_IOOC_RESPONSE], |
368 | fops: &q54sj108a2_fops); |
369 | debugfs_create_file(name: "pmbus_revision" , mode: 0444, parent: q54sj108a2_dir, |
370 | data: &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_PMBUS_VERSION], |
371 | fops: &q54sj108a2_fops); |
372 | debugfs_create_file(name: "mfr_id" , mode: 0444, parent: q54sj108a2_dir, |
373 | data: &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_MFR_ID], |
374 | fops: &q54sj108a2_fops); |
375 | debugfs_create_file(name: "mfr_model" , mode: 0444, parent: q54sj108a2_dir, |
376 | data: &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_MFR_MODEL], |
377 | fops: &q54sj108a2_fops); |
378 | debugfs_create_file(name: "mfr_revision" , mode: 0444, parent: q54sj108a2_dir, |
379 | data: &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_MFR_REVISION], |
380 | fops: &q54sj108a2_fops); |
381 | debugfs_create_file(name: "mfr_location" , mode: 0444, parent: q54sj108a2_dir, |
382 | data: &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_MFR_LOCATION], |
383 | fops: &q54sj108a2_fops); |
384 | debugfs_create_file(name: "blackbox_erase" , mode: 0200, parent: q54sj108a2_dir, |
385 | data: &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_BLACKBOX_ERASE], |
386 | fops: &q54sj108a2_fops); |
387 | debugfs_create_file(name: "blackbox_read_offset" , mode: 0444, parent: q54sj108a2_dir, |
388 | data: &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_BLACKBOX_READ_OFFSET], |
389 | fops: &q54sj108a2_fops); |
390 | debugfs_create_file(name: "blackbox_set_offset" , mode: 0200, parent: q54sj108a2_dir, |
391 | data: &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_BLACKBOX_SET_OFFSET], |
392 | fops: &q54sj108a2_fops); |
393 | debugfs_create_file(name: "blackbox_read" , mode: 0444, parent: q54sj108a2_dir, |
394 | data: &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_BLACKBOX_READ], |
395 | fops: &q54sj108a2_fops); |
396 | debugfs_create_file(name: "flash_key" , mode: 0444, parent: q54sj108a2_dir, |
397 | data: &psu->debugfs_entries[Q54SJ108A2_DEBUGFS_FLASH_KEY], |
398 | fops: &q54sj108a2_fops); |
399 | |
400 | return 0; |
401 | } |
402 | |
403 | static const struct of_device_id q54sj108a2_of_match[] = { |
404 | { .compatible = "delta,q54sj108a2" , .data = (void *)q54sj108a2 }, |
405 | { }, |
406 | }; |
407 | |
408 | MODULE_DEVICE_TABLE(of, q54sj108a2_of_match); |
409 | |
410 | static struct i2c_driver q54sj108a2_driver = { |
411 | .driver = { |
412 | .name = "q54sj108a2" , |
413 | .of_match_table = q54sj108a2_of_match, |
414 | }, |
415 | .probe = q54sj108a2_probe, |
416 | .id_table = q54sj108a2_id, |
417 | }; |
418 | |
419 | module_i2c_driver(q54sj108a2_driver); |
420 | |
421 | MODULE_AUTHOR("Xiao.Ma <xiao.mx.ma@deltaww.com>" ); |
422 | MODULE_DESCRIPTION("PMBus driver for Delta Q54SJ108A2 series modules" ); |
423 | MODULE_LICENSE("GPL" ); |
424 | MODULE_IMPORT_NS(PMBUS); |
425 | |