1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * AMD Versal SysMon I2C driver 4 * 5 * Copyright (C) 2023 - 2026, Advanced Micro Devices, Inc. 6 */ 7 8 #include <linux/bits.h> 9 #include <linux/err.h> 10 #include <linux/i2c.h> 11 #include <linux/mod_devicetable.h> 12 #include <linux/module.h> 13 #include <linux/regmap.h> 14 #include <linux/types.h> 15 #include <linux/unaligned.h> 16 17 #include "versal-sysmon.h" 18 19 #define SYSMON_I2C_INSTR_READ BIT(2) 20 #define SYSMON_I2C_INSTR_WRITE BIT(3) 21 22 /* 23 * I2C command frame layout (8 bytes): 24 * [0..3] data payload (little-endian u32) 25 * [4..5] register offset >> 2 (little-endian u16) 26 * [6] instruction (read/write) 27 * [7] reserved 28 */ 29 #define SYSMON_I2C_DATA_OFS 0 30 #define SYSMON_I2C_REG_OFS 4 31 #define SYSMON_I2C_INSTR_OFS 6 32 33 static int sysmon_i2c_reg_read(void *context, unsigned int reg, 34 unsigned int *val) 35 { 36 struct i2c_client *client = context; 37 u8 write_buf[8] = { }; 38 u8 read_buf[4]; 39 int ret; 40 41 put_unaligned_le16(reg >> 2, &write_buf[SYSMON_I2C_REG_OFS]); 42 write_buf[SYSMON_I2C_INSTR_OFS] = SYSMON_I2C_INSTR_READ; 43 44 ret = i2c_master_send(client, write_buf, sizeof(write_buf)); 45 if (ret < 0) 46 return ret; 47 if (ret != sizeof(write_buf)) 48 return -EIO; 49 50 ret = i2c_master_recv(client, read_buf, sizeof(read_buf)); 51 if (ret < 0) 52 return ret; 53 if (ret != sizeof(read_buf)) 54 return -EIO; 55 56 *val = get_unaligned_le32(read_buf); 57 58 return 0; 59 } 60 61 static int sysmon_i2c_reg_write(void *context, unsigned int reg, 62 unsigned int val) 63 { 64 struct i2c_client *client = context; 65 u8 write_buf[8] = { }; 66 int ret; 67 68 put_unaligned_le32(val, &write_buf[SYSMON_I2C_DATA_OFS]); 69 put_unaligned_le16(reg >> 2, &write_buf[SYSMON_I2C_REG_OFS]); 70 write_buf[SYSMON_I2C_INSTR_OFS] = SYSMON_I2C_INSTR_WRITE; 71 72 ret = i2c_master_send(client, write_buf, sizeof(write_buf)); 73 if (ret < 0) 74 return ret; 75 if (ret != sizeof(write_buf)) 76 return -EIO; 77 78 return 0; 79 } 80 81 /* 82 * Almost all registers are volatile (live ADC readings, interrupt 83 * status). The rest are not accessed often enough to benefit from 84 * caching. 85 */ 86 static const struct regmap_config sysmon_i2c_regmap_config = { 87 .reg_bits = 32, 88 .val_bits = 32, 89 .reg_stride = SYSMON_REG_STRIDE, 90 .max_register = SYSMON_MAX_REG, 91 .reg_read = sysmon_i2c_reg_read, 92 .reg_write = sysmon_i2c_reg_write, 93 }; 94 95 static int sysmon_i2c_probe(struct i2c_client *client) 96 { 97 struct device *dev = &client->dev; 98 struct regmap *regmap; 99 100 regmap = devm_regmap_init(dev, NULL, client, &sysmon_i2c_regmap_config); 101 if (IS_ERR(regmap)) 102 return PTR_ERR(regmap); 103 104 /* I2C has no IRQ connection; events are not supported */ 105 return devm_versal_sysmon_core_probe(dev, regmap); 106 } 107 108 static const struct of_device_id sysmon_i2c_of_match_table[] = { 109 { .compatible = "xlnx,versal-sysmon" }, 110 { } 111 }; 112 MODULE_DEVICE_TABLE(of, sysmon_i2c_of_match_table); 113 114 static const struct i2c_device_id sysmon_i2c_id_table[] = { 115 { .name = "versal-sysmon" }, 116 { } 117 }; 118 MODULE_DEVICE_TABLE(i2c, sysmon_i2c_id_table); 119 120 static struct i2c_driver sysmon_i2c_driver = { 121 .probe = sysmon_i2c_probe, 122 .driver = { 123 .name = "versal-sysmon-i2c", 124 .of_match_table = sysmon_i2c_of_match_table, 125 }, 126 .id_table = sysmon_i2c_id_table, 127 }; 128 module_i2c_driver(sysmon_i2c_driver); 129 130 MODULE_LICENSE("GPL"); 131 MODULE_DESCRIPTION("AMD Versal SysMon I2C Driver"); 132 MODULE_IMPORT_NS("VERSAL_SYSMON"); 133 MODULE_AUTHOR("Conall O'Griofa <conall.ogriofa@amd.com>"); 134 MODULE_AUTHOR("Salih Erim <salih.erim@amd.com>"); 135