Skip to content

Commit 18672fe

Browse files
eghidolisre
authored andcommitted
power: reset: add Toradex Embedded Controller
Toradex SMARC iMX8MP and SMARC iMX95 SoM modules use a small Embedded Controller (EC) to manage power and reset functions and related SMARC signals. This driver implements power-off and reboot handlers, communicating with the EC via I2C to issue the appropriate power management commands. During probe, the driver logs the Embedded Controller ID (unique ID for each SMARC board supported) in hex format along with the firmware version. Signed-off-by: Emanuele Ghidoli <[email protected]> Signed-off-by: Francesco Dolcini <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Sebastian Reichel <[email protected]>
1 parent 1cf87b0 commit 18672fe

File tree

4 files changed

+165
-0
lines changed

4 files changed

+165
-0
lines changed

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24411,6 +24411,7 @@ M: Emanuele Ghidoli <[email protected]>
2441124411
M: Francesco Dolcini <[email protected]>
2441224412
S: Maintained
2441324413
F: Documentation/devicetree/bindings/power/reset/toradex,smarc-ec.yaml
24414+
F: drivers/power/reset/tdx-ec-poweroff.c
2441424415

2441524416
TORTURE-TEST MODULES
2441624417
M: Davidlohr Bueso <[email protected]>

drivers/power/reset/Kconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,19 @@ config POWER_RESET_ST
216216
help
217217
Reset support for STMicroelectronics boards.
218218

219+
config POWER_RESET_TORADEX_EC
220+
tristate "Toradex Embedded Controller power-off and reset driver"
221+
depends on I2C
222+
select REGMAP_I2C
223+
help
224+
This driver supports power-off and reset for SMARC Toradex SoMs,
225+
for example the SMARC iMX8MP and SMARC iMX95, using Toradex
226+
Embedded Controller (EC).
227+
228+
Say Y here if you have a Toradex SMARC SoM.
229+
230+
If unsure, say N.
231+
219232
config POWER_RESET_TPS65086
220233
bool "TPS65086 restart driver"
221234
depends on MFD_TPS65086

drivers/power/reset/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o
2424
obj-$(CONFIG_POWER_RESET_REGULATOR) += regulator-poweroff.o
2525
obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o
2626
obj-$(CONFIG_POWER_RESET_ST) += st-poweroff.o
27+
obj-$(CONFIG_POWER_RESET_TORADEX_EC) += tdx-ec-poweroff.o
2728
obj-$(CONFIG_POWER_RESET_TPS65086) += tps65086-restart.o
2829
obj-$(CONFIG_POWER_RESET_VERSATILE) += arm-versatile-reboot.o
2930
obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o

drivers/power/reset/tdx-ec-poweroff.c

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
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 <[email protected]>
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, &regmap_config);
114+
if (IS_ERR(regmap))
115+
return PTR_ERR(regmap);
116+
117+
err = regmap_bulk_read(regmap, EC_CHIP_ID_REG, &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 <[email protected]>");
149+
MODULE_DESCRIPTION("Toradex SMARC Embedded Controller driver");
150+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)