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/i3c/device.h>
13 #include <linux/i3c/master.h>
14 #include <linux/init.h>
15 #include <linux/module.h>
16 #include <linux/mutex.h>
17 #include <linux/of.h>
18 #include <linux/regmap.h>
19 #include "rmi-core.h"
20
21 #define REV_TWO_BYTE_ADDR 0x21
22
sbrmi_enable_alert(struct sbrmi_data * data)23 static int sbrmi_enable_alert(struct sbrmi_data *data)
24 {
25 int ctrl, ret;
26
27 /*
28 * Enable the SB-RMI Software alert status
29 * by writing 0 to bit 4 of Control register(0x1)
30 */
31 ret = regmap_read(data->regmap, SBRMI_CTRL, &ctrl);
32 if (ret < 0)
33 return ret;
34
35 if (ctrl & 0x10) {
36 ctrl &= ~0x10;
37 return regmap_write(data->regmap, SBRMI_CTRL, ctrl);
38 }
39
40 return 0;
41 }
42
sbrmi_get_max_pwr_limit(struct sbrmi_data * data)43 static int sbrmi_get_max_pwr_limit(struct sbrmi_data *data)
44 {
45 struct apml_mbox_msg msg = { 0 };
46 int ret;
47
48 msg.cmd = SBRMI_READ_PKG_MAX_PWR_LIMIT;
49 ret = rmi_mailbox_xfer(data, &msg);
50 if (ret < 0)
51 return ret;
52 data->pwr_limit_max = msg.mb_in_out;
53
54 return ret;
55 }
56
sbrmi_common_probe(struct device * dev,struct regmap * regmap,uint8_t address)57 static int sbrmi_common_probe(struct device *dev, struct regmap *regmap, uint8_t address)
58 {
59 struct sbrmi_data *data;
60 int ret;
61
62 data = devm_kzalloc(dev, sizeof(struct sbrmi_data), GFP_KERNEL);
63 if (!data)
64 return -ENOMEM;
65
66 data->regmap = regmap;
67 mutex_init(&data->lock);
68
69 /* Enable alert for SB-RMI sequence */
70 ret = sbrmi_enable_alert(data);
71 if (ret < 0)
72 return ret;
73
74 /* Cache maximum power limit */
75 ret = sbrmi_get_max_pwr_limit(data);
76 if (ret < 0)
77 return ret;
78
79 data->dev_static_addr = address;
80
81 dev_set_drvdata(dev, data);
82
83 ret = create_hwmon_sensor_device(dev, data);
84 if (ret < 0)
85 return ret;
86 return create_misc_rmi_device(data, dev);
87 }
88
89 static struct regmap_config sbrmi_regmap_config = {
90 .reg_bits = 8,
91 .val_bits = 8,
92 };
93
94 static struct regmap_config sbrmi_regmap_config_ext = {
95 .reg_bits = 16,
96 .val_bits = 8,
97 .reg_format_endian = REGMAP_ENDIAN_LITTLE,
98 };
99
sbrmi_i2c_probe(struct i2c_client * client)100 static int sbrmi_i2c_probe(struct i2c_client *client)
101 {
102 struct device *dev = &client->dev;
103 struct regmap *regmap;
104 int rev, ret;
105
106 regmap = devm_regmap_init_i2c(client, &sbrmi_regmap_config);
107 if (IS_ERR(regmap))
108 return PTR_ERR(regmap);
109
110 ret = regmap_read(regmap, SBRMI_REV, &rev);
111 if (ret)
112 return ret;
113
114 /*
115 * For Turin and newer platforms, revision is 0x21 or later. This is
116 * to identify the two byte register address size. However, one
117 * byte transaction can be successful.
118 * Verify if revision is 0x21 or later, if yes, switch to 2 byte
119 * address size.
120 * Continuously using 1 byte address for revision 0x21 or later can lead
121 * to bus corruption.
122 */
123 if (rev >= REV_TWO_BYTE_ADDR) {
124 regmap = devm_regmap_init_i2c(client, &sbrmi_regmap_config_ext);
125 if (IS_ERR(regmap))
126 return PTR_ERR(regmap);
127 }
128 return sbrmi_common_probe(dev, regmap, client->addr);
129 }
130
sbrmi_i2c_remove(struct i2c_client * client)131 static void sbrmi_i2c_remove(struct i2c_client *client)
132 {
133 struct sbrmi_data *data = dev_get_drvdata(&client->dev);
134
135 misc_deregister(&data->sbrmi_misc_dev);
136 /* Assign fops and parent of misc dev to NULL */
137 data->sbrmi_misc_dev.fops = NULL;
138 data->sbrmi_misc_dev.parent = NULL;
139 dev_info(&client->dev, "Removed sbrmi-i2c driver\n");
140 return;
141 }
142
143 static const struct i2c_device_id sbrmi_id[] = {
144 {"sbrmi-i2c"},
145 {}
146 };
147 MODULE_DEVICE_TABLE(i2c, sbrmi_id);
148
149 static const struct of_device_id __maybe_unused sbrmi_of_match[] = {
150 {
151 .compatible = "amd,sbrmi",
152 },
153 { },
154 };
155 MODULE_DEVICE_TABLE(of, sbrmi_of_match);
156
157 static struct i2c_driver sbrmi_driver = {
158 .driver = {
159 .name = "sbrmi-i2c",
160 .of_match_table = of_match_ptr(sbrmi_of_match),
161 },
162 .probe = sbrmi_i2c_probe,
163 .remove = sbrmi_i2c_remove,
164 .id_table = sbrmi_id,
165 };
166
sbrmi_i3c_probe(struct i3c_device * i3cdev)167 static int sbrmi_i3c_probe(struct i3c_device *i3cdev)
168 {
169 struct device *dev = i3cdev_to_dev(i3cdev);
170 struct regmap *regmap;
171 int rev, ret;
172
173 regmap = devm_regmap_init_i3c(i3cdev, &sbrmi_regmap_config);
174 if (IS_ERR(regmap))
175 return PTR_ERR(regmap);
176
177 ret = regmap_read(regmap, SBRMI_REV, &rev);
178 if (ret)
179 return ret;
180
181 /*
182 * For Turin and newer platforms, revision is 0x21 or later. This is
183 * to identify the two byte register address size. However, one
184 * byte transaction can be successful.
185 * Verify if revision is 0x21 or later, if yes, switch to 2 byte
186 * address size.
187 * Continuously using 1 byte address for revision 0x21 or later can lead
188 * to bus corruption.
189 */
190 if (rev >= REV_TWO_BYTE_ADDR) {
191 regmap = devm_regmap_init_i3c(i3cdev, &sbrmi_regmap_config_ext);
192 if (IS_ERR(regmap))
193 return PTR_ERR(regmap);
194 }
195
196 /*
197 * AMD APML I3C devices support static address.
198 * If static address is defined, dynamic address is same as static address.
199 * In case static address is not defined, I3C master controller defined
200 * dynamic address is used.
201 */
202 return sbrmi_common_probe(dev, regmap, i3cdev->desc->info.dyn_addr);
203 }
204
sbrmi_i3c_remove(struct i3c_device * i3cdev)205 static void sbrmi_i3c_remove(struct i3c_device *i3cdev)
206 {
207 struct sbrmi_data *data = dev_get_drvdata(&i3cdev->dev);
208
209 misc_deregister(&data->sbrmi_misc_dev);
210 }
211
212 static const struct i3c_device_id sbrmi_i3c_id[] = {
213 /* PID for AMD SBRMI device */
214 I3C_DEVICE_EXTRA_INFO(0x112, 0x0, 0x2, NULL),
215 {}
216 };
217 MODULE_DEVICE_TABLE(i3c, sbrmi_i3c_id);
218
219 static struct i3c_driver sbrmi_i3c_driver = {
220 .driver = {
221 .name = "sbrmi-i3c",
222 },
223 .probe = sbrmi_i3c_probe,
224 .remove = sbrmi_i3c_remove,
225 .id_table = sbrmi_i3c_id,
226 };
227
228 module_i3c_i2c_driver(sbrmi_i3c_driver, &sbrmi_driver);
229
230 MODULE_AUTHOR("Akshay Gupta <akshay.gupta@amd.com>");
231 MODULE_AUTHOR("Naveen Krishna Chatradhi <naveenkrishna.chatradhi@amd.com>");
232 MODULE_DESCRIPTION("Hwmon driver for AMD SB-RMI emulated sensor");
233 MODULE_LICENSE("GPL");
234