1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (c) 2024 Inochi Amaoto <inochiama@outlook.com>
4 *
5 * Sophgo power control mcu for SG2042
6 */
7
8 #include <linux/cleanup.h>
9 #include <linux/debugfs.h>
10 #include <linux/err.h>
11 #include <linux/hwmon.h>
12 #include <linux/i2c.h>
13 #include <linux/kernel.h>
14 #include <linux/module.h>
15 #include <linux/mutex.h>
16
17 /* fixed MCU registers */
18 #define REG_BOARD_TYPE 0x00
19 #define REG_MCU_FIRMWARE_VERSION 0x01
20 #define REG_PCB_VERSION 0x02
21 #define REG_PWR_CTRL 0x03
22 #define REG_SOC_TEMP 0x04
23 #define REG_BOARD_TEMP 0x05
24 #define REG_RST_COUNT 0x0a
25 #define REG_UPTIME 0x0b
26 #define REG_RESET_REASON 0x0d
27 #define REG_MCU_TYPE 0x18
28 #define REG_REPOWER_POLICY 0x65
29 #define REG_CRITICAL_TEMP 0x66
30 #define REG_REPOWER_TEMP 0x67
31
32 #define REPOWER_POLICY_REBOOT 1
33 #define REPOWER_POLICY_KEEP_OFF 2
34
35 #define MCU_POWER_MAX 0xff
36
37 #define DEFINE_MCU_DEBUG_ATTR(_name, _reg, _format) \
38 static int _name##_show(struct seq_file *seqf, \
39 void *unused) \
40 { \
41 struct sg2042_mcu_data *mcu = seqf->private; \
42 int ret; \
43 ret = i2c_smbus_read_byte_data(mcu->client, (_reg)); \
44 if (ret < 0) \
45 return ret; \
46 seq_printf(seqf, _format "\n", ret); \
47 return 0; \
48 } \
49 DEFINE_SHOW_ATTRIBUTE(_name) \
50
51 struct sg2042_mcu_data {
52 struct i2c_client *client;
53 struct mutex mutex;
54 };
55
reset_count_show(struct device * dev,struct device_attribute * attr,char * buf)56 static ssize_t reset_count_show(struct device *dev,
57 struct device_attribute *attr,
58 char *buf)
59 {
60 struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
61 int ret;
62
63 ret = i2c_smbus_read_byte_data(mcu->client, REG_RST_COUNT);
64 if (ret < 0)
65 return ret;
66
67 return sprintf(buf, "%d\n", ret);
68 }
69
uptime_show(struct device * dev,struct device_attribute * attr,char * buf)70 static ssize_t uptime_show(struct device *dev,
71 struct device_attribute *attr,
72 char *buf)
73 {
74 struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
75 u8 time_val[2];
76 int ret;
77
78 ret = i2c_smbus_read_i2c_block_data(mcu->client, REG_UPTIME,
79 sizeof(time_val), time_val);
80 if (ret < 0)
81 return ret;
82
83 return sprintf(buf, "%d\n",
84 (time_val[0]) | (time_val[1] << 8));
85 }
86
reset_reason_show(struct device * dev,struct device_attribute * attr,char * buf)87 static ssize_t reset_reason_show(struct device *dev,
88 struct device_attribute *attr,
89 char *buf)
90 {
91 struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
92 int ret;
93
94 ret = i2c_smbus_read_byte_data(mcu->client, REG_RESET_REASON);
95 if (ret < 0)
96 return ret;
97
98 return sprintf(buf, "0x%02x\n", ret);
99 }
100
repower_policy_show(struct device * dev,struct device_attribute * attr,char * buf)101 static ssize_t repower_policy_show(struct device *dev,
102 struct device_attribute *attr,
103 char *buf)
104 {
105 struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
106 int ret;
107 const char *action;
108
109 ret = i2c_smbus_read_byte_data(mcu->client, REG_REPOWER_POLICY);
110 if (ret < 0)
111 return ret;
112
113 if (ret == REPOWER_POLICY_REBOOT)
114 action = "repower";
115 else if (ret == REPOWER_POLICY_KEEP_OFF)
116 action = "keep";
117 else
118 action = "unknown";
119
120 return sprintf(buf, "%s\n", action);
121 }
122
repower_policy_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)123 static ssize_t repower_policy_store(struct device *dev,
124 struct device_attribute *attr,
125 const char *buf, size_t count)
126 {
127 struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
128 u8 value;
129 int ret;
130
131 if (sysfs_streq("repower", buf))
132 value = REPOWER_POLICY_REBOOT;
133 else if (sysfs_streq("keep", buf))
134 value = REPOWER_POLICY_KEEP_OFF;
135 else
136 return -EINVAL;
137
138 ret = i2c_smbus_write_byte_data(mcu->client,
139 REG_REPOWER_POLICY, value);
140 if (ret < 0)
141 return ret;
142
143 return count;
144 }
145
146 static DEVICE_ATTR_RO(reset_count);
147 static DEVICE_ATTR_RO(uptime);
148 static DEVICE_ATTR_RO(reset_reason);
149 static DEVICE_ATTR_RW(repower_policy);
150
151 DEFINE_MCU_DEBUG_ATTR(firmware_version, REG_MCU_FIRMWARE_VERSION, "0x%02x");
152 DEFINE_MCU_DEBUG_ATTR(pcb_version, REG_PCB_VERSION, "0x%02x");
153 DEFINE_MCU_DEBUG_ATTR(board_type, REG_BOARD_TYPE, "0x%02x");
154 DEFINE_MCU_DEBUG_ATTR(mcu_type, REG_MCU_TYPE, "%d");
155
156 static struct attribute *sg2042_mcu_attrs[] = {
157 &dev_attr_reset_count.attr,
158 &dev_attr_uptime.attr,
159 &dev_attr_reset_reason.attr,
160 &dev_attr_repower_policy.attr,
161 NULL
162 };
163
164 static const struct attribute_group sg2042_mcu_attr_group = {
165 .attrs = sg2042_mcu_attrs,
166 };
167
168 static const struct attribute_group *sg2042_mcu_groups[] = {
169 &sg2042_mcu_attr_group,
170 NULL
171 };
172
173 static const struct hwmon_channel_info * const sg2042_mcu_info[] = {
174 HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
175 HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_CRIT |
176 HWMON_T_CRIT_HYST,
177 HWMON_T_INPUT),
178 NULL
179 };
180
sg2042_mcu_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * val)181 static int sg2042_mcu_read(struct device *dev,
182 enum hwmon_sensor_types type,
183 u32 attr, int channel, long *val)
184 {
185 struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
186 int tmp;
187 u8 reg;
188
189 switch (attr) {
190 case hwmon_temp_input:
191 reg = channel ? REG_BOARD_TEMP : REG_SOC_TEMP;
192 break;
193 case hwmon_temp_crit:
194 reg = REG_CRITICAL_TEMP;
195 break;
196 case hwmon_temp_crit_hyst:
197 reg = REG_REPOWER_TEMP;
198 break;
199 default:
200 return -EOPNOTSUPP;
201 }
202
203 tmp = i2c_smbus_read_byte_data(mcu->client, reg);
204 if (tmp < 0)
205 return tmp;
206 *val = tmp * 1000;
207
208 return 0;
209 }
210
sg2042_mcu_write(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long val)211 static int sg2042_mcu_write(struct device *dev,
212 enum hwmon_sensor_types type,
213 u32 attr, int channel, long val)
214 {
215 struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
216 int temp = val / 1000;
217 int hyst_temp, crit_temp;
218 u8 reg;
219
220 temp = clamp_val(temp, 0, MCU_POWER_MAX);
221
222 guard(mutex)(&mcu->mutex);
223
224 switch (attr) {
225 case hwmon_temp_crit:
226 hyst_temp = i2c_smbus_read_byte_data(mcu->client,
227 REG_REPOWER_TEMP);
228 if (hyst_temp < 0)
229 return hyst_temp;
230
231 crit_temp = temp;
232 reg = REG_CRITICAL_TEMP;
233 break;
234 case hwmon_temp_crit_hyst:
235 crit_temp = i2c_smbus_read_byte_data(mcu->client,
236 REG_CRITICAL_TEMP);
237 if (crit_temp < 0)
238 return crit_temp;
239
240 hyst_temp = temp;
241 reg = REG_REPOWER_TEMP;
242 break;
243 default:
244 return -EOPNOTSUPP;
245 }
246
247 /*
248 * ensure hyst_temp is smaller to avoid MCU from
249 * keeping triggering repower event.
250 */
251 if (crit_temp < hyst_temp)
252 return -EINVAL;
253
254 return i2c_smbus_write_byte_data(mcu->client, reg, temp);
255 }
256
sg2042_mcu_is_visible(const void * _data,enum hwmon_sensor_types type,u32 attr,int channel)257 static umode_t sg2042_mcu_is_visible(const void *_data,
258 enum hwmon_sensor_types type,
259 u32 attr, int channel)
260 {
261 switch (type) {
262 case hwmon_temp:
263 switch (attr) {
264 case hwmon_temp_input:
265 return 0444;
266 case hwmon_temp_crit:
267 case hwmon_temp_crit_hyst:
268 if (channel == 0)
269 return 0644;
270 break;
271 default:
272 break;
273 }
274 break;
275 default:
276 break;
277 }
278 return 0;
279 }
280
281 static const struct hwmon_ops sg2042_mcu_ops = {
282 .is_visible = sg2042_mcu_is_visible,
283 .read = sg2042_mcu_read,
284 .write = sg2042_mcu_write,
285 };
286
287 static const struct hwmon_chip_info sg2042_mcu_chip_info = {
288 .ops = &sg2042_mcu_ops,
289 .info = sg2042_mcu_info,
290 };
291
sg2042_mcu_debugfs_init(struct sg2042_mcu_data * mcu)292 static void sg2042_mcu_debugfs_init(struct sg2042_mcu_data *mcu)
293 {
294 debugfs_create_file("firmware_version", 0444, mcu->client->debugfs,
295 mcu, &firmware_version_fops);
296 debugfs_create_file("pcb_version", 0444, mcu->client->debugfs, mcu,
297 &pcb_version_fops);
298 debugfs_create_file("mcu_type", 0444, mcu->client->debugfs, mcu,
299 &mcu_type_fops);
300 debugfs_create_file("board_type", 0444, mcu->client->debugfs, mcu,
301 &board_type_fops);
302 }
303
sg2042_mcu_i2c_probe(struct i2c_client * client)304 static int sg2042_mcu_i2c_probe(struct i2c_client *client)
305 {
306 struct device *dev = &client->dev;
307 struct sg2042_mcu_data *mcu;
308 struct device *hwmon_dev;
309
310 if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
311 I2C_FUNC_SMBUS_BLOCK_DATA))
312 return -ENODEV;
313
314 mcu = devm_kmalloc(dev, sizeof(*mcu), GFP_KERNEL);
315 if (!mcu)
316 return -ENOMEM;
317
318 mutex_init(&mcu->mutex);
319 mcu->client = client;
320
321 i2c_set_clientdata(client, mcu);
322
323 hwmon_dev = devm_hwmon_device_register_with_info(dev, "sg2042_mcu",
324 mcu,
325 &sg2042_mcu_chip_info,
326 NULL);
327 if (IS_ERR(hwmon_dev))
328 return PTR_ERR(hwmon_dev);
329
330 sg2042_mcu_debugfs_init(mcu);
331
332 return 0;
333 }
334
335 static const struct i2c_device_id sg2042_mcu_id[] = {
336 { "sg2042-hwmon-mcu" },
337 { }
338 };
339 MODULE_DEVICE_TABLE(i2c, sg2042_mcu_id);
340
341 static const struct of_device_id sg2042_mcu_of_id[] = {
342 { .compatible = "sophgo,sg2042-hwmon-mcu" },
343 {},
344 };
345 MODULE_DEVICE_TABLE(of, sg2042_mcu_of_id);
346
347 static struct i2c_driver sg2042_mcu_driver = {
348 .driver = {
349 .name = "sg2042-mcu",
350 .of_match_table = sg2042_mcu_of_id,
351 .dev_groups = sg2042_mcu_groups,
352 },
353 .probe = sg2042_mcu_i2c_probe,
354 .id_table = sg2042_mcu_id,
355 };
356 module_i2c_driver(sg2042_mcu_driver);
357
358 MODULE_AUTHOR("Inochi Amaoto <inochiama@outlook.com>");
359 MODULE_DESCRIPTION("MCU I2C driver for SG2042 soc platform");
360 MODULE_LICENSE("GPL");
361