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 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", 0 }, 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 <inochiama@outlook.com>"); 387 MODULE_DESCRIPTION("MCU I2C driver for SG2042 soc platform"); 388 MODULE_LICENSE("GPL"); 389