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