1 // SPDX-License-Identifier: GPL-2.0-or-later
3 #include <linux/debugfs.h>
4 #include <linux/delay.h>
7 #include <linux/init.h>
8 #include <linux/hwmon.h>
9 #include <linux/module.h>
10 #include <linux/mutex.h>
12 /* Aries current average temp ADC code CSR */
13 #define ARIES_CURRENT_AVG_TEMP_ADC_CSR 0x42c
15 /* Device Load check register */
16 #define ARIES_CODE_LOAD_REG 0x605
17 /* Value indicating FW was loaded properly, [3:1] = 3'b111 */
18 #define ARIES_LOAD_CODE 0xe
20 /* Main Micro Heartbeat register */
21 #define ARIES_MM_HEARTBEAT_ADDR 0x923
23 /* Reg offset to specify Address for MM assisted accesses */
24 #define ARIES_MM_ASSIST_REG_ADDR_OFFSET 0xd99
25 /* Reg offset to specify Command for MM assisted accesses */
26 #define ARIES_MM_ASSIST_CMD_OFFSET 0xd9d
27 /* Reg offset to MM SPARE 0 used specify Address[7:0] */
28 #define ARIES_MM_ASSIST_SPARE_0_OFFSET 0xd9f
29 /* Reg offset to MM SPARE 3 used specify Data Byte 0 */
30 #define ARIES_MM_ASSIST_SPARE_3_OFFSET 0xda2
31 /* Wide register reads */
32 #define ARIES_MM_RD_WIDE_REG_2B 0x1d
33 #define ARIES_MM_RD_WIDE_REG_3B 0x1e
34 #define ARIES_MM_RD_WIDE_REG_4B 0x1f
35 #define ARIES_MM_RD_WIDE_REG_5B 0x20
37 /* Time delay between checking MM status of EEPROM write (microseconds) */
38 #define ARIES_MM_STATUS_TIME 5000
40 /* AL Main SRAM DMEM offset (A0) */
41 #define AL_MAIN_SRAM_DMEM_OFFSET (64 * 1024)
42 /* SRAM read command */
43 #define AL_TG_RD_LOC_IND_SRAM 0x16
45 /* Offset for main micro FW info */
46 #define ARIES_MAIN_MICRO_FW_INFO (96 * 1024 - 128)
47 /* FW Info (Major) offset location in struct */
48 #define ARIES_MM_FW_VERSION_MAJOR 0
49 /* FW Info (Minor) offset location in struct */
50 #define ARIES_MM_FW_VERSION_MINOR 1
51 /* FW Info (Build no.) offset location in struct */
52 #define ARIES_MM_FW_VERSION_BUILD 2
54 #define ARIES_TEMP_CAL_CODE_DEFAULT 84
56 /* Struct defining FW version loaded on an Aries device */
57 struct pt5161l_fw_ver
{
63 /* Each client has this additional data */
65 struct i2c_client
*client
;
66 struct dentry
*debugfs
;
67 struct pt5161l_fw_ver fw_ver
;
68 struct mutex lock
; /* for atomic I2C transactions */
70 bool code_load_okay
; /* indicate if code load reg value is expected */
71 bool mm_heartbeat_okay
; /* indicate if Main Micro heartbeat is good */
72 bool mm_wide_reg_access
; /* MM assisted wide register access */
75 static struct dentry
*pt5161l_debugfs_dir
;
78 * Write multiple data bytes to Aries over I2C
80 static int pt5161l_write_block_data(struct pt5161l_data
*data
, u32 address
,
83 struct i2c_client
*client
= data
->client
;
86 u8 xfer_len
, curr_len
;
88 u8 cmd
= 0x0F; /* [7]:pec_en, [4:2]:func, [1]:start, [0]:end */
89 u8 config
= 0x40; /* [6]:cfg_type, [4:1]:burst_len, [0]:address bit16 */
91 while (remain_len
> 0) {
96 curr_len
= remain_len
;
100 buf
[0] = config
| (curr_len
- 1) << 1 | ((address
>> 16) & 0x1);
101 buf
[1] = (address
>> 8) & 0xff;
102 buf
[2] = address
& 0xff;
103 memcpy(&buf
[3], val
, curr_len
);
105 xfer_len
= 3 + curr_len
;
106 ret
= i2c_smbus_write_block_data(client
, cmd
, xfer_len
, buf
);
118 * Read multiple data bytes from Aries over I2C
120 static int pt5161l_read_block_data(struct pt5161l_data
*data
, u32 address
,
123 struct i2c_client
*client
= data
->client
;
127 u8 wbuf
[16], rbuf
[24];
128 u8 cmd
= 0x08; /* [7]:pec_en, [4:2]:func, [1]:start, [0]:end */
129 u8 config
= 0x00; /* [6]:cfg_type, [4:1]:burst_len, [0]:address bit16 */
131 while (remain_len
> 0) {
132 if (remain_len
> 16) {
136 curr_len
= remain_len
;
140 wbuf
[0] = config
| (curr_len
- 1) << 1 |
141 ((address
>> 16) & 0x1);
142 wbuf
[1] = (address
>> 8) & 0xff;
143 wbuf
[2] = address
& 0xff;
145 for (tries
= 0; tries
< 3; tries
++) {
146 ret
= i2c_smbus_write_block_data(client
, (cmd
| 0x2), 3,
151 ret
= i2c_smbus_read_block_data(client
, (cmd
| 0x1),
159 memcpy(val
, rbuf
, curr_len
);
167 static int pt5161l_read_wide_reg(struct pt5161l_data
*data
, u32 address
,
175 * Safely access wide registers using mailbox method to prevent
176 * risking conflict with Aries firmware; otherwise fallback to
177 * legacy, less secure method.
179 if (data
->mm_wide_reg_access
) {
180 buf
[0] = address
& 0xff;
181 buf
[1] = (address
>> 8) & 0xff;
182 buf
[2] = (address
>> 16) & 0x1;
183 ret
= pt5161l_write_block_data(data
,
184 ARIES_MM_ASSIST_SPARE_0_OFFSET
,
189 /* Set command based on width */
192 buf
[0] = ARIES_MM_RD_WIDE_REG_2B
;
195 buf
[0] = ARIES_MM_RD_WIDE_REG_3B
;
198 buf
[0] = ARIES_MM_RD_WIDE_REG_4B
;
201 buf
[0] = ARIES_MM_RD_WIDE_REG_5B
;
206 ret
= pt5161l_write_block_data(data
, ARIES_MM_ASSIST_CMD_OFFSET
,
212 for (tries
= 0; tries
< 100; tries
++) {
213 ret
= pt5161l_read_block_data(data
,
214 ARIES_MM_ASSIST_CMD_OFFSET
,
222 usleep_range(ARIES_MM_STATUS_TIME
,
223 ARIES_MM_STATUS_TIME
+ 1000);
228 ret
= pt5161l_read_block_data(data
,
229 ARIES_MM_ASSIST_SPARE_3_OFFSET
,
234 return pt5161l_read_block_data(data
, address
, width
, val
);
241 * Read multiple (up to eight) data bytes from micro SRAM over I2C
244 pt5161l_read_block_data_main_micro_indirect(struct pt5161l_data
*data
,
245 u32 address
, u8 len
, u8
*val
)
250 u32 uind_offs
= ARIES_MM_ASSIST_REG_ADDR_OFFSET
;
251 u32 eeprom_base
, eeprom_addr
;
253 /* No multi-byte indirect support here. Hence read a byte at a time */
254 eeprom_base
= address
- AL_MAIN_SRAM_DMEM_OFFSET
;
255 for (i
= 0; i
< len
; i
++) {
256 eeprom_addr
= eeprom_base
+ i
;
257 buf
[0] = eeprom_addr
& 0xff;
258 buf
[1] = (eeprom_addr
>> 8) & 0xff;
259 buf
[2] = (eeprom_addr
>> 16) & 0xff;
260 ret
= pt5161l_write_block_data(data
, uind_offs
, 3, buf
);
264 buf
[0] = AL_TG_RD_LOC_IND_SRAM
;
265 ret
= pt5161l_write_block_data(data
, uind_offs
+ 4, 1, buf
);
270 for (tries
= 0; tries
< 255; tries
++) {
271 ret
= pt5161l_read_block_data(data
, uind_offs
+ 4, 1,
282 ret
= pt5161l_read_block_data(data
, uind_offs
+ 3, 1, buf
);
293 * Check firmware load status
295 static int pt5161l_fw_load_check(struct pt5161l_data
*data
)
300 ret
= pt5161l_read_block_data(data
, ARIES_CODE_LOAD_REG
, 1, buf
);
304 if (buf
[0] < ARIES_LOAD_CODE
) {
305 dev_dbg(&data
->client
->dev
,
306 "Code Load reg unexpected. Not all modules are loaded %x\n",
308 data
->code_load_okay
= false;
310 data
->code_load_okay
= true;
317 * Check main micro heartbeat
319 static int pt5161l_heartbeat_check(struct pt5161l_data
*data
)
324 bool hb_changed
= false;
326 ret
= pt5161l_read_block_data(data
, ARIES_MM_HEARTBEAT_ADDR
, 1, buf
);
331 for (tries
= 0; tries
< 100; tries
++) {
332 ret
= pt5161l_read_block_data(data
, ARIES_MM_HEARTBEAT_ADDR
, 1,
337 if (buf
[0] != heartbeat
) {
342 data
->mm_heartbeat_okay
= hb_changed
;
348 * Check the status of firmware
350 static int pt5161l_fwsts_check(struct pt5161l_data
*data
)
354 u8 major
= 0, minor
= 0;
357 ret
= pt5161l_fw_load_check(data
);
361 ret
= pt5161l_heartbeat_check(data
);
365 if (data
->code_load_okay
&& data
->mm_heartbeat_okay
) {
366 ret
= pt5161l_read_block_data_main_micro_indirect(data
, ARIES_MAIN_MICRO_FW_INFO
+
367 ARIES_MM_FW_VERSION_MAJOR
,
372 ret
= pt5161l_read_block_data_main_micro_indirect(data
, ARIES_MAIN_MICRO_FW_INFO
+
373 ARIES_MM_FW_VERSION_MINOR
,
378 ret
= pt5161l_read_block_data_main_micro_indirect(data
, ARIES_MAIN_MICRO_FW_INFO
+
379 ARIES_MM_FW_VERSION_BUILD
,
383 build
= buf
[1] << 8 | buf
[0];
385 data
->fw_ver
.major
= major
;
386 data
->fw_ver
.minor
= minor
;
387 data
->fw_ver
.build
= build
;
392 static int pt5161l_fw_is_at_least(struct pt5161l_data
*data
, u8 major
, u8 minor
,
395 u32 ver
= major
<< 24 | minor
<< 16 | build
;
396 u32 curr_ver
= data
->fw_ver
.major
<< 24 | data
->fw_ver
.minor
<< 16 |
405 static int pt5161l_init_dev(struct pt5161l_data
*data
)
409 mutex_lock(&data
->lock
);
410 ret
= pt5161l_fwsts_check(data
);
411 mutex_unlock(&data
->lock
);
415 /* Firmware 2.2.0 enables safe access to wide registers */
416 if (pt5161l_fw_is_at_least(data
, 2, 2, 0))
417 data
->mm_wide_reg_access
= true;
419 data
->init_done
= true;
424 static int pt5161l_read(struct device
*dev
, enum hwmon_sensor_types type
,
425 u32 attr
, int channel
, long *val
)
427 struct pt5161l_data
*data
= dev_get_drvdata(dev
);
433 case hwmon_temp_input
:
434 if (!data
->init_done
) {
435 ret
= pt5161l_init_dev(data
);
440 mutex_lock(&data
->lock
);
441 ret
= pt5161l_read_wide_reg(data
,
442 ARIES_CURRENT_AVG_TEMP_ADC_CSR
, 4,
444 mutex_unlock(&data
->lock
);
446 dev_dbg(dev
, "Read adc_code failed %d\n", ret
);
450 adc_code
= buf
[3] << 24 | buf
[2] << 16 | buf
[1] << 8 | buf
[0];
451 if (adc_code
== 0 || adc_code
>= 0x3ff) {
452 dev_dbg(dev
, "Invalid adc_code %x\n", adc_code
);
457 ((adc_code
- (ARIES_TEMP_CAL_CODE_DEFAULT
+ 250)) *
467 static umode_t
pt5161l_is_visible(const void *data
,
468 enum hwmon_sensor_types type
, u32 attr
,
472 case hwmon_temp_input
:
481 static const struct hwmon_channel_info
*pt5161l_info
[] = {
482 HWMON_CHANNEL_INFO(temp
, HWMON_T_INPUT
),
486 static const struct hwmon_ops pt5161l_hwmon_ops
= {
487 .is_visible
= pt5161l_is_visible
,
488 .read
= pt5161l_read
,
491 static const struct hwmon_chip_info pt5161l_chip_info
= {
492 .ops
= &pt5161l_hwmon_ops
,
493 .info
= pt5161l_info
,
496 static ssize_t
pt5161l_debugfs_read_fw_ver(struct file
*file
, char __user
*buf
,
497 size_t count
, loff_t
*ppos
)
499 struct pt5161l_data
*data
= file
->private_data
;
503 mutex_lock(&data
->lock
);
504 ret
= pt5161l_fwsts_check(data
);
505 mutex_unlock(&data
->lock
);
509 ret
= snprintf(ver
, sizeof(ver
), "%u.%u.%u\n", data
->fw_ver
.major
,
510 data
->fw_ver
.minor
, data
->fw_ver
.build
);
512 return simple_read_from_buffer(buf
, count
, ppos
, ver
, ret
);
515 static const struct file_operations pt5161l_debugfs_ops_fw_ver
= {
516 .read
= pt5161l_debugfs_read_fw_ver
,
520 static ssize_t
pt5161l_debugfs_read_fw_load_sts(struct file
*file
,
521 char __user
*buf
, size_t count
,
524 struct pt5161l_data
*data
= file
->private_data
;
529 mutex_lock(&data
->lock
);
530 ret
= pt5161l_fw_load_check(data
);
531 mutex_unlock(&data
->lock
);
533 status
= data
->code_load_okay
;
535 ret
= snprintf(health
, sizeof(health
), "%s\n",
536 status
? "normal" : "abnormal");
538 return simple_read_from_buffer(buf
, count
, ppos
, health
, ret
);
541 static const struct file_operations pt5161l_debugfs_ops_fw_load_sts
= {
542 .read
= pt5161l_debugfs_read_fw_load_sts
,
546 static ssize_t
pt5161l_debugfs_read_hb_sts(struct file
*file
, char __user
*buf
,
547 size_t count
, loff_t
*ppos
)
549 struct pt5161l_data
*data
= file
->private_data
;
554 mutex_lock(&data
->lock
);
555 ret
= pt5161l_heartbeat_check(data
);
556 mutex_unlock(&data
->lock
);
558 status
= data
->mm_heartbeat_okay
;
560 ret
= snprintf(health
, sizeof(health
), "%s\n",
561 status
? "normal" : "abnormal");
563 return simple_read_from_buffer(buf
, count
, ppos
, health
, ret
);
566 static const struct file_operations pt5161l_debugfs_ops_hb_sts
= {
567 .read
= pt5161l_debugfs_read_hb_sts
,
571 static int pt5161l_init_debugfs(struct pt5161l_data
*data
)
573 data
->debugfs
= debugfs_create_dir(dev_name(&data
->client
->dev
),
574 pt5161l_debugfs_dir
);
576 debugfs_create_file("fw_ver", 0444, data
->debugfs
, data
,
577 &pt5161l_debugfs_ops_fw_ver
);
579 debugfs_create_file("fw_load_status", 0444, data
->debugfs
, data
,
580 &pt5161l_debugfs_ops_fw_load_sts
);
582 debugfs_create_file("heartbeat_status", 0444, data
->debugfs
, data
,
583 &pt5161l_debugfs_ops_hb_sts
);
588 static int pt5161l_probe(struct i2c_client
*client
)
590 struct device
*dev
= &client
->dev
;
591 struct device
*hwmon_dev
;
592 struct pt5161l_data
*data
;
594 data
= devm_kzalloc(dev
, sizeof(*data
), GFP_KERNEL
);
598 data
->client
= client
;
599 mutex_init(&data
->lock
);
600 pt5161l_init_dev(data
);
601 dev_set_drvdata(dev
, data
);
603 hwmon_dev
= devm_hwmon_device_register_with_info(dev
, client
->name
,
608 pt5161l_init_debugfs(data
);
610 return PTR_ERR_OR_ZERO(hwmon_dev
);
613 static void pt5161l_remove(struct i2c_client
*client
)
615 struct pt5161l_data
*data
= i2c_get_clientdata(client
);
617 debugfs_remove_recursive(data
->debugfs
);
620 static const struct of_device_id __maybe_unused pt5161l_of_match
[] = {
621 { .compatible
= "asteralabs,pt5161l" },
624 MODULE_DEVICE_TABLE(of
, pt5161l_of_match
);
626 static const struct acpi_device_id __maybe_unused pt5161l_acpi_match
[] = {
630 MODULE_DEVICE_TABLE(acpi
, pt5161l_acpi_match
);
632 static const struct i2c_device_id pt5161l_id
[] = {
636 MODULE_DEVICE_TABLE(i2c
, pt5161l_id
);
638 static struct i2c_driver pt5161l_driver
= {
639 .class = I2C_CLASS_HWMON
,
642 .of_match_table
= of_match_ptr(pt5161l_of_match
),
643 .acpi_match_table
= ACPI_PTR(pt5161l_acpi_match
),
645 .probe
= pt5161l_probe
,
646 .remove
= pt5161l_remove
,
647 .id_table
= pt5161l_id
,
650 static int __init
pt5161l_init(void)
652 pt5161l_debugfs_dir
= debugfs_create_dir("pt5161l", NULL
);
653 return i2c_add_driver(&pt5161l_driver
);
656 static void __exit
pt5161l_exit(void)
658 i2c_del_driver(&pt5161l_driver
);
659 debugfs_remove_recursive(pt5161l_debugfs_dir
);
662 module_init(pt5161l_init
);
663 module_exit(pt5161l_exit
);
665 MODULE_AUTHOR("Cosmo Chou <cosmo.chou@quantatw.com>");
666 MODULE_DESCRIPTION("Hwmon driver for Astera Labs Aries PCIe retimer");
667 MODULE_LICENSE("GPL");