1 // SPDX-License-Identifier: BSD-2-Clause OR GPL-2.0-or-later
3 * ENE KB3930 Embedded Controller Driver
5 * Copyright (C) 2020 Lubomir Rintel
8 #include <linux/delay.h>
9 #include <linux/gpio/consumer.h>
10 #include <linux/i2c.h>
11 #include <linux/mfd/core.h>
12 #include <linux/module.h>
13 #include <linux/reboot.h>
14 #include <linux/regmap.h>
16 /* I2C registers that are multiplexing access to the EC RAM. */
23 /* EC RAM registers. */
26 EC_VERSION_MAJ
= 0x31,
27 EC_VERSION_MIN
= 0x32,
31 struct i2c_client
*client
;
32 struct regmap
*ram_regmap
;
33 struct gpio_descs
*off_gpios
;
36 struct kb3930
*kb3930_power_off
;
38 #define EC_GPIO_WAVE 0
39 #define EC_GPIO_OFF_MODE 1
41 #define EC_OFF_MODE_REBOOT 0
42 #define EC_OFF_MODE_POWER 1
44 static void kb3930_off(struct kb3930
*ddata
, int off_mode
)
46 gpiod_direction_output(ddata
->off_gpios
->desc
[EC_GPIO_OFF_MODE
],
50 * This creates a 10 Hz wave on EC_GPIO_WAVE that signals a
51 * shutdown request to the EC. Once the EC detects it, it will
52 * proceed to turn the power off or reset the board depending on
53 * the value of EC_GPIO_OFF_MODE.
57 gpiod_direction_output(ddata
->off_gpios
->desc
[EC_GPIO_WAVE
], 0);
59 gpiod_direction_output(ddata
->off_gpios
->desc
[EC_GPIO_WAVE
], 1);
63 static int kb3930_restart(struct notifier_block
*this,
64 unsigned long mode
, void *cmd
)
66 kb3930_off(kb3930_power_off
, EC_OFF_MODE_REBOOT
);
70 static void kb3930_pm_power_off(void)
72 kb3930_off(kb3930_power_off
, EC_OFF_MODE_POWER
);
75 static struct notifier_block kb3930_restart_nb
= {
76 .notifier_call
= kb3930_restart
,
79 static const struct mfd_cell ariel_ec_cells
[] = {
80 { .name
= "dell-wyse-ariel-led", },
81 { .name
= "dell-wyse-ariel-power", },
84 static int kb3930_ec_ram_reg_write(void *context
, unsigned int reg
,
87 struct kb3930
*ddata
= context
;
89 return i2c_smbus_write_word_data(ddata
->client
, EC_RAM_OUT
,
93 static int kb3930_ec_ram_reg_read(void *context
, unsigned int reg
,
96 struct kb3930
*ddata
= context
;
99 ret
= i2c_smbus_write_word_data(ddata
->client
, EC_RAM_IN
, reg
);
103 ret
= i2c_smbus_read_word_data(ddata
->client
, EC_DATA_IN
);
111 static const struct regmap_config kb3930_ram_regmap_config
= {
116 .max_register
= 0xff,
117 .reg_write
= kb3930_ec_ram_reg_write
,
118 .reg_read
= kb3930_ec_ram_reg_read
,
122 static int kb3930_probe(struct i2c_client
*client
)
124 struct device
*dev
= &client
->dev
;
125 struct device_node
*np
= dev
->of_node
;
126 struct kb3930
*ddata
;
130 ddata
= devm_kzalloc(dev
, sizeof(*ddata
), GFP_KERNEL
);
134 kb3930_power_off
= ddata
;
135 ddata
->client
= client
;
136 i2c_set_clientdata(client
, ddata
);
138 ddata
->ram_regmap
= devm_regmap_init(dev
, NULL
, ddata
,
139 &kb3930_ram_regmap_config
);
140 if (IS_ERR(ddata
->ram_regmap
))
141 return PTR_ERR(ddata
->ram_regmap
);
143 ret
= regmap_read(ddata
->ram_regmap
, EC_MODEL
, &model
);
147 /* Currently we only support the cells present on Dell Ariel model. */
149 dev_err(dev
, "unknown board model: %02x\n", model
);
153 ret
= devm_mfd_add_devices(dev
, PLATFORM_DEVID_AUTO
,
155 ARRAY_SIZE(ariel_ec_cells
),
160 if (of_property_read_bool(np
, "system-power-controller")) {
162 devm_gpiod_get_array_optional(dev
, "off", GPIOD_IN
);
163 if (IS_ERR(ddata
->off_gpios
))
164 return PTR_ERR(ddata
->off_gpios
);
165 if (ddata
->off_gpios
->ndescs
< 2) {
166 dev_err(dev
, "invalid off-gpios property\n");
171 if (ddata
->off_gpios
) {
172 register_restart_handler(&kb3930_restart_nb
);
174 pm_power_off
= kb3930_pm_power_off
;
180 static int kb3930_remove(struct i2c_client
*client
)
182 struct kb3930
*ddata
= i2c_get_clientdata(client
);
184 if (ddata
->off_gpios
) {
185 if (pm_power_off
== kb3930_pm_power_off
)
187 unregister_restart_handler(&kb3930_restart_nb
);
189 kb3930_power_off
= NULL
;
194 static const struct of_device_id kb3930_dt_ids
[] = {
195 { .compatible
= "ene,kb3930" },
198 MODULE_DEVICE_TABLE(of
, kb3930_dt_ids
);
200 static struct i2c_driver kb3930_driver
= {
201 .probe_new
= kb3930_probe
,
202 .remove
= kb3930_remove
,
204 .name
= "ene-kb3930",
205 .of_match_table
= kb3930_dt_ids
,
208 module_i2c_driver(kb3930_driver
);
210 MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
211 MODULE_DESCRIPTION("ENE KB3930 Embedded Controller Driver");
212 MODULE_LICENSE("Dual BSD/GPL");