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