1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * rmi-i2c.c - Side band RMI over I2C support for AMD out 4 * of band management 5 * 6 * Copyright (C) 2024 Advanced Micro Devices, Inc. 7 */ 8 9 #include <linux/delay.h> 10 #include <linux/err.h> 11 #include <linux/i2c.h> 12 #include <linux/init.h> 13 #include <linux/module.h> 14 #include <linux/mutex.h> 15 #include <linux/of.h> 16 #include <linux/regmap.h> 17 #include "rmi-core.h" 18 19 static int sbrmi_enable_alert(struct sbrmi_data *data) 20 { 21 int ctrl, ret; 22 23 /* 24 * Enable the SB-RMI Software alert status 25 * by writing 0 to bit 4 of Control register(0x1) 26 */ 27 ret = regmap_read(data->regmap, SBRMI_CTRL, &ctrl); 28 if (ret < 0) 29 return ret; 30 31 if (ctrl & 0x10) { 32 ctrl &= ~0x10; 33 return regmap_write(data->regmap, SBRMI_CTRL, ctrl); 34 } 35 36 return 0; 37 } 38 39 static int sbrmi_get_max_pwr_limit(struct sbrmi_data *data) 40 { 41 struct apml_mbox_msg msg = { 0 }; 42 int ret; 43 44 msg.cmd = SBRMI_READ_PKG_MAX_PWR_LIMIT; 45 ret = rmi_mailbox_xfer(data, &msg); 46 if (ret < 0) 47 return ret; 48 data->pwr_limit_max = msg.mb_in_out; 49 50 return ret; 51 } 52 53 static int sbrmi_i2c_probe(struct i2c_client *client) 54 { 55 struct device *dev = &client->dev; 56 struct sbrmi_data *data; 57 struct regmap_config sbrmi_i2c_regmap_config = { 58 .reg_bits = 8, 59 .val_bits = 8, 60 }; 61 int ret; 62 63 data = devm_kzalloc(dev, sizeof(struct sbrmi_data), GFP_KERNEL); 64 if (!data) 65 return -ENOMEM; 66 67 mutex_init(&data->lock); 68 69 data->regmap = devm_regmap_init_i2c(client, &sbrmi_i2c_regmap_config); 70 if (IS_ERR(data->regmap)) 71 return PTR_ERR(data->regmap); 72 73 /* Enable alert for SB-RMI sequence */ 74 ret = sbrmi_enable_alert(data); 75 if (ret < 0) 76 return ret; 77 78 /* Cache maximum power limit */ 79 ret = sbrmi_get_max_pwr_limit(data); 80 if (ret < 0) 81 return ret; 82 83 data->dev_static_addr = client->addr; 84 dev_set_drvdata(dev, data); 85 86 ret = create_hwmon_sensor_device(dev, data); 87 if (ret < 0) 88 return ret; 89 return create_misc_rmi_device(data, dev); 90 } 91 92 static void sbrmi_i2c_remove(struct i2c_client *client) 93 { 94 struct sbrmi_data *data = dev_get_drvdata(&client->dev); 95 96 misc_deregister(&data->sbrmi_misc_dev); 97 /* Assign fops and parent of misc dev to NULL */ 98 data->sbrmi_misc_dev.fops = NULL; 99 data->sbrmi_misc_dev.parent = NULL; 100 dev_info(&client->dev, "Removed sbrmi-i2c driver\n"); 101 return; 102 } 103 104 static const struct i2c_device_id sbrmi_id[] = { 105 {"sbrmi-i2c"}, 106 {} 107 }; 108 MODULE_DEVICE_TABLE(i2c, sbrmi_id); 109 110 static const struct of_device_id __maybe_unused sbrmi_of_match[] = { 111 { 112 .compatible = "amd,sbrmi", 113 }, 114 { }, 115 }; 116 MODULE_DEVICE_TABLE(of, sbrmi_of_match); 117 118 static struct i2c_driver sbrmi_driver = { 119 .driver = { 120 .name = "sbrmi-i2c", 121 .of_match_table = of_match_ptr(sbrmi_of_match), 122 }, 123 .probe = sbrmi_i2c_probe, 124 .remove = sbrmi_i2c_remove, 125 .id_table = sbrmi_id, 126 }; 127 128 module_i2c_driver(sbrmi_driver); 129 130 MODULE_AUTHOR("Akshay Gupta <akshay.gupta@amd.com>"); 131 MODULE_AUTHOR("Naveen Krishna Chatradhi <naveenkrishna.chatradhi@amd.com>"); 132 MODULE_DESCRIPTION("Hwmon driver for AMD SB-RMI emulated sensor"); 133 MODULE_LICENSE("GPL"); 134