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
tdx_ec_cmd(struct regmap * regmap,u8 cmd)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
tdx_ec_power_off(struct sys_off_data * data)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
tdx_ec_restart(struct sys_off_data * data)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
tdx_ec_register_power_off_restart(struct device * dev,struct regmap * regmap)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
tdx_ec_probe(struct i2c_client * client)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