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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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