1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Toradex Embedded Controller driver 4 * 5 * Copyright (C) 2025 Toradex 6 * 7 * Author: Emanuele Ghidoli <emanuele.ghidoli@toradex.com> 8 */ 9 10 #include <linux/array_size.h> 11 #include <linux/device.h> 12 #include <linux/err.h> 13 #include <linux/i2c.h> 14 #include <linux/mod_devicetable.h> 15 #include <linux/module.h> 16 #include <linux/reboot.h> 17 #include <linux/regmap.h> 18 #include <linux/types.h> 19 20 #define EC_CHIP_ID_REG 0x00 21 #define EC_CHIP_ID_SMARC_IMX95 0x11 22 #define EC_CHIP_ID_SMARC_IMX8MP 0x12 23 24 #define EC_VERSION_REG_MAJOR 0x01 25 #define EC_VERSION_REG_MINOR 0x02 26 #define EC_ID_VERSION_LEN 3 27 28 #define EC_CMD_REG 0xD0 29 #define EC_CMD_POWEROFF 0x01 30 #define EC_CMD_RESET 0x02 31 32 #define EC_REG_MAX 0xD0 33 34 static const struct regmap_range volatile_ranges[] = { 35 regmap_reg_range(EC_CMD_REG, EC_CMD_REG), 36 }; 37 38 static const struct regmap_access_table volatile_table = { 39 .yes_ranges = volatile_ranges, 40 .n_yes_ranges = ARRAY_SIZE(volatile_ranges), 41 }; 42 43 static const struct regmap_range read_ranges[] = { 44 regmap_reg_range(EC_CHIP_ID_REG, EC_VERSION_REG_MINOR), 45 }; 46 47 static const struct regmap_access_table read_table = { 48 .yes_ranges = read_ranges, 49 .n_yes_ranges = ARRAY_SIZE(read_ranges), 50 }; 51 52 static const struct regmap_config regmap_config = { 53 .reg_bits = 8, 54 .val_bits = 8, 55 .max_register = EC_REG_MAX, 56 .cache_type = REGCACHE_RBTREE, 57 .rd_table = &read_table, 58 .volatile_table = &volatile_table, 59 }; 60 61 static int tdx_ec_cmd(struct regmap *regmap, u8 cmd) 62 { 63 int err = regmap_write(regmap, EC_CMD_REG, cmd); 64 65 if (err) 66 dev_err(regmap_get_device(regmap), "Failed to send command 0x%02X: %d\n", cmd, err); 67 68 return err; 69 } 70 71 static int tdx_ec_power_off(struct sys_off_data *data) 72 { 73 struct regmap *regmap = data->cb_data; 74 int err; 75 76 err = tdx_ec_cmd(regmap, EC_CMD_POWEROFF); 77 78 return err ? NOTIFY_BAD : NOTIFY_DONE; 79 } 80 81 static int tdx_ec_restart(struct sys_off_data *data) 82 { 83 struct regmap *regmap = data->cb_data; 84 int err; 85 86 err = tdx_ec_cmd(regmap, EC_CMD_RESET); 87 88 return err ? NOTIFY_BAD : NOTIFY_DONE; 89 } 90 91 static int tdx_ec_register_power_off_restart(struct device *dev, struct regmap *regmap) 92 { 93 int err; 94 95 err = devm_register_sys_off_handler(dev, SYS_OFF_MODE_RESTART, 96 SYS_OFF_PRIO_FIRMWARE, 97 tdx_ec_restart, regmap); 98 if (err) 99 return err; 100 101 return devm_register_sys_off_handler(dev, SYS_OFF_MODE_POWER_OFF, 102 SYS_OFF_PRIO_FIRMWARE, 103 tdx_ec_power_off, regmap); 104 } 105 106 static int tdx_ec_probe(struct i2c_client *client) 107 { 108 struct device *dev = &client->dev; 109 u8 reg_val[EC_ID_VERSION_LEN]; 110 struct regmap *regmap; 111 int err; 112 113 regmap = devm_regmap_init_i2c(client, ®map_config); 114 if (IS_ERR(regmap)) 115 return PTR_ERR(regmap); 116 117 err = regmap_bulk_read(regmap, EC_CHIP_ID_REG, ®_val, EC_ID_VERSION_LEN); 118 if (err) 119 return dev_err_probe(dev, err, 120 "Cannot read id and version registers\n"); 121 122 dev_info(dev, "Toradex Embedded Controller id %x - Firmware %u.%u\n", 123 reg_val[0], reg_val[1], reg_val[2]); 124 125 err = tdx_ec_register_power_off_restart(dev, regmap); 126 if (err) 127 return dev_err_probe(dev, err, 128 "Cannot register system restart handler\n"); 129 130 return 0; 131 } 132 133 static const struct of_device_id __maybe_unused of_tdx_ec_match[] = { 134 { .compatible = "toradex,smarc-ec" }, 135 {} 136 }; 137 MODULE_DEVICE_TABLE(of, of_tdx_ec_match); 138 139 static struct i2c_driver tdx_ec_driver = { 140 .probe = tdx_ec_probe, 141 .driver = { 142 .name = "toradex-smarc-ec", 143 .of_match_table = of_tdx_ec_match, 144 }, 145 }; 146 module_i2c_driver(tdx_ec_driver); 147 148 MODULE_AUTHOR("Emanuele Ghidoli <emanuele.ghidoli@toradex.com>"); 149 MODULE_DESCRIPTION("Toradex SMARC Embedded Controller driver"); 150 MODULE_LICENSE("GPL"); 151