Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
34static const struct regmap_range volatile_ranges[] = {
35 regmap_reg_range(EC_CMD_REG, EC_CMD_REG),
36};
37
38static const struct regmap_access_table volatile_table = {
39 .yes_ranges = volatile_ranges,
40 .n_yes_ranges = ARRAY_SIZE(volatile_ranges),
41};
42
43static const struct regmap_range read_ranges[] = {
44 regmap_reg_range(EC_CHIP_ID_REG, EC_VERSION_REG_MINOR),
45};
46
47static const struct regmap_access_table read_table = {
48 .yes_ranges = read_ranges,
49 .n_yes_ranges = ARRAY_SIZE(read_ranges),
50};
51
52static 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
61static 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
71static 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
81static 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
91static 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
106static 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
133static const struct of_device_id __maybe_unused of_tdx_ec_match[] = {
134 { .compatible = "toradex,smarc-ec" },
135 {}
136};
137MODULE_DEVICE_TABLE(of, of_tdx_ec_match);
138
139static 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};
146module_i2c_driver(tdx_ec_driver);
147
148MODULE_AUTHOR("Emanuele Ghidoli <emanuele.ghidoli@toradex.com>");
149MODULE_DESCRIPTION("Toradex SMARC Embedded Controller driver");
150MODULE_LICENSE("GPL");