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