]> Git Repo - J-linux.git/blob - drivers/hwmon/sg2042-mcu.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / hwmon / sg2042-mcu.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2024 Inochi Amaoto <[email protected]>
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 dentry           *debugfs;
54         struct mutex            mutex;
55 };
56
57 static struct dentry *sgmcu_debugfs;
58
59 static ssize_t reset_count_show(struct device *dev,
60                                 struct device_attribute *attr,
61                                 char *buf)
62 {
63         struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
64         int ret;
65
66         ret = i2c_smbus_read_byte_data(mcu->client, REG_RST_COUNT);
67         if (ret < 0)
68                 return ret;
69
70         return sprintf(buf, "%d\n", ret);
71 }
72
73 static ssize_t uptime_show(struct device *dev,
74                            struct device_attribute *attr,
75                            char *buf)
76 {
77         struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
78         u8 time_val[2];
79         int ret;
80
81         ret = i2c_smbus_read_i2c_block_data(mcu->client, REG_UPTIME,
82                                             sizeof(time_val), time_val);
83         if (ret < 0)
84                 return ret;
85
86         return sprintf(buf, "%d\n",
87                        (time_val[0]) | (time_val[1] << 8));
88 }
89
90 static ssize_t reset_reason_show(struct device *dev,
91                                  struct device_attribute *attr,
92                                  char *buf)
93 {
94         struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
95         int ret;
96
97         ret = i2c_smbus_read_byte_data(mcu->client, REG_RESET_REASON);
98         if (ret < 0)
99                 return ret;
100
101         return sprintf(buf, "0x%02x\n", ret);
102 }
103
104 static ssize_t repower_policy_show(struct device *dev,
105                                    struct device_attribute *attr,
106                                    char *buf)
107 {
108         struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
109         int ret;
110         const char *action;
111
112         ret = i2c_smbus_read_byte_data(mcu->client, REG_REPOWER_POLICY);
113         if (ret < 0)
114                 return ret;
115
116         if (ret == REPOWER_POLICY_REBOOT)
117                 action = "repower";
118         else if (ret == REPOWER_POLICY_KEEP_OFF)
119                 action = "keep";
120         else
121                 action = "unknown";
122
123         return sprintf(buf, "%s\n", action);
124 }
125
126 static ssize_t repower_policy_store(struct device *dev,
127                                     struct device_attribute *attr,
128                                     const char *buf, size_t count)
129 {
130         struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
131         u8 value;
132         int ret;
133
134         if (sysfs_streq("repower", buf))
135                 value = REPOWER_POLICY_REBOOT;
136         else if (sysfs_streq("keep", buf))
137                 value = REPOWER_POLICY_KEEP_OFF;
138         else
139                 return -EINVAL;
140
141         ret = i2c_smbus_write_byte_data(mcu->client,
142                                         REG_REPOWER_POLICY, value);
143         if (ret < 0)
144                 return ret;
145
146         return count;
147 }
148
149 static DEVICE_ATTR_RO(reset_count);
150 static DEVICE_ATTR_RO(uptime);
151 static DEVICE_ATTR_RO(reset_reason);
152 static DEVICE_ATTR_RW(repower_policy);
153
154 DEFINE_MCU_DEBUG_ATTR(firmware_version, REG_MCU_FIRMWARE_VERSION, "0x%02x");
155 DEFINE_MCU_DEBUG_ATTR(pcb_version, REG_PCB_VERSION, "0x%02x");
156 DEFINE_MCU_DEBUG_ATTR(board_type, REG_BOARD_TYPE, "0x%02x");
157 DEFINE_MCU_DEBUG_ATTR(mcu_type, REG_MCU_TYPE, "%d");
158
159 static struct attribute *sg2042_mcu_attrs[] = {
160         &dev_attr_reset_count.attr,
161         &dev_attr_uptime.attr,
162         &dev_attr_reset_reason.attr,
163         &dev_attr_repower_policy.attr,
164         NULL
165 };
166
167 static const struct attribute_group sg2042_mcu_attr_group = {
168         .attrs  = sg2042_mcu_attrs,
169 };
170
171 static const struct attribute_group *sg2042_mcu_groups[] = {
172         &sg2042_mcu_attr_group,
173         NULL
174 };
175
176 static const struct hwmon_channel_info * const sg2042_mcu_info[] = {
177         HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
178         HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_CRIT |
179                                         HWMON_T_CRIT_HYST,
180                                  HWMON_T_INPUT),
181         NULL
182 };
183
184 static int sg2042_mcu_read(struct device *dev,
185                            enum hwmon_sensor_types type,
186                            u32 attr, int channel, long *val)
187 {
188         struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
189         int tmp;
190         u8 reg;
191
192         switch (attr) {
193         case hwmon_temp_input:
194                 reg = channel ? REG_BOARD_TEMP : REG_SOC_TEMP;
195                 break;
196         case hwmon_temp_crit:
197                 reg = REG_CRITICAL_TEMP;
198                 break;
199         case hwmon_temp_crit_hyst:
200                 reg = REG_REPOWER_TEMP;
201                 break;
202         default:
203                 return -EOPNOTSUPP;
204         }
205
206         tmp = i2c_smbus_read_byte_data(mcu->client, reg);
207         if (tmp < 0)
208                 return tmp;
209         *val = tmp * 1000;
210
211         return 0;
212 }
213
214 static int sg2042_mcu_write(struct device *dev,
215                             enum hwmon_sensor_types type,
216                             u32 attr, int channel, long val)
217 {
218         struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
219         int temp = val / 1000;
220         int hyst_temp, crit_temp;
221         u8 reg;
222
223         temp = clamp_val(temp, 0, MCU_POWER_MAX);
224
225         guard(mutex)(&mcu->mutex);
226
227         switch (attr) {
228         case hwmon_temp_crit:
229                 hyst_temp = i2c_smbus_read_byte_data(mcu->client,
230                                                      REG_REPOWER_TEMP);
231                 if (hyst_temp < 0)
232                         return hyst_temp;
233
234                 crit_temp = temp;
235                 reg = REG_CRITICAL_TEMP;
236                 break;
237         case hwmon_temp_crit_hyst:
238                 crit_temp = i2c_smbus_read_byte_data(mcu->client,
239                                                      REG_CRITICAL_TEMP);
240                 if (crit_temp < 0)
241                         return crit_temp;
242
243                 hyst_temp = temp;
244                 reg = REG_REPOWER_TEMP;
245                 break;
246         default:
247                 return -EOPNOTSUPP;
248         }
249
250         /*
251          * ensure hyst_temp is smaller to avoid MCU from
252          * keeping triggering repower event.
253          */
254         if (crit_temp < hyst_temp)
255                 return -EINVAL;
256
257         return i2c_smbus_write_byte_data(mcu->client, reg, temp);
258 }
259
260 static umode_t sg2042_mcu_is_visible(const void *_data,
261                                      enum hwmon_sensor_types type,
262                                      u32 attr, int channel)
263 {
264         switch (type) {
265         case hwmon_temp:
266                 switch (attr) {
267                 case hwmon_temp_input:
268                         return 0444;
269                 case hwmon_temp_crit:
270                 case hwmon_temp_crit_hyst:
271                         if (channel == 0)
272                                 return 0644;
273                         break;
274                 default:
275                         break;
276                 }
277                 break;
278         default:
279                         break;
280         }
281         return 0;
282 }
283
284 static const struct hwmon_ops sg2042_mcu_ops = {
285         .is_visible = sg2042_mcu_is_visible,
286         .read = sg2042_mcu_read,
287         .write = sg2042_mcu_write,
288 };
289
290 static const struct hwmon_chip_info sg2042_mcu_chip_info = {
291         .ops = &sg2042_mcu_ops,
292         .info = sg2042_mcu_info,
293 };
294
295 static void sg2042_mcu_debugfs_init(struct sg2042_mcu_data *mcu,
296                                     struct device *dev)
297 {
298         mcu->debugfs = debugfs_create_dir(dev_name(dev), sgmcu_debugfs);
299
300         debugfs_create_file("firmware_version", 0444, mcu->debugfs,
301                             mcu, &firmware_version_fops);
302         debugfs_create_file("pcb_version", 0444, mcu->debugfs, mcu,
303                             &pcb_version_fops);
304         debugfs_create_file("mcu_type", 0444, mcu->debugfs, mcu,
305                             &mcu_type_fops);
306         debugfs_create_file("board_type", 0444, mcu->debugfs, mcu,
307                             &board_type_fops);
308 }
309
310 static int sg2042_mcu_i2c_probe(struct i2c_client *client)
311 {
312         struct device *dev = &client->dev;
313         struct sg2042_mcu_data *mcu;
314         struct device *hwmon_dev;
315
316         if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
317                                                 I2C_FUNC_SMBUS_BLOCK_DATA))
318                 return -ENODEV;
319
320         mcu = devm_kmalloc(dev, sizeof(*mcu), GFP_KERNEL);
321         if (!mcu)
322                 return -ENOMEM;
323
324         mutex_init(&mcu->mutex);
325         mcu->client = client;
326
327         i2c_set_clientdata(client, mcu);
328
329         hwmon_dev = devm_hwmon_device_register_with_info(dev, "sg2042_mcu",
330                                                          mcu,
331                                                          &sg2042_mcu_chip_info,
332                                                          NULL);
333         if (IS_ERR(hwmon_dev))
334                 return PTR_ERR(hwmon_dev);
335
336         sg2042_mcu_debugfs_init(mcu, dev);
337
338         return 0;
339 }
340
341 static void sg2042_mcu_i2c_remove(struct i2c_client *client)
342 {
343         struct sg2042_mcu_data *mcu = i2c_get_clientdata(client);
344
345         debugfs_remove_recursive(mcu->debugfs);
346 }
347
348 static const struct i2c_device_id sg2042_mcu_id[] = {
349         { "sg2042-hwmon-mcu" },
350         { }
351 };
352 MODULE_DEVICE_TABLE(i2c, sg2042_mcu_id);
353
354 static const struct of_device_id sg2042_mcu_of_id[] = {
355         { .compatible = "sophgo,sg2042-hwmon-mcu" },
356         {},
357 };
358 MODULE_DEVICE_TABLE(of, sg2042_mcu_of_id);
359
360 static struct i2c_driver sg2042_mcu_driver = {
361         .driver = {
362                 .name = "sg2042-mcu",
363                 .of_match_table = sg2042_mcu_of_id,
364                 .dev_groups = sg2042_mcu_groups,
365         },
366         .probe = sg2042_mcu_i2c_probe,
367         .remove = sg2042_mcu_i2c_remove,
368         .id_table = sg2042_mcu_id,
369 };
370
371 static int __init sg2042_mcu_init(void)
372 {
373         sgmcu_debugfs = debugfs_create_dir("sg2042-mcu", NULL);
374         return i2c_add_driver(&sg2042_mcu_driver);
375 }
376
377 static void __exit sg2042_mcu_exit(void)
378 {
379         debugfs_remove_recursive(sgmcu_debugfs);
380         i2c_del_driver(&sg2042_mcu_driver);
381 }
382
383 module_init(sg2042_mcu_init);
384 module_exit(sg2042_mcu_exit);
385
386 MODULE_AUTHOR("Inochi Amaoto <[email protected]>");
387 MODULE_DESCRIPTION("MCU I2C driver for SG2042 soc platform");
388 MODULE_LICENSE("GPL");
This page took 0.077825 seconds and 4 git commands to generate.