1ede6b2d1SLubomir Rintel // SPDX-License-Identifier: BSD-2-Clause OR GPL-2.0-or-later
2ede6b2d1SLubomir Rintel /*
3ede6b2d1SLubomir Rintel * ENE KB3930 Embedded Controller Driver
4ede6b2d1SLubomir Rintel *
5ede6b2d1SLubomir Rintel * Copyright (C) 2020 Lubomir Rintel
6ede6b2d1SLubomir Rintel */
7ede6b2d1SLubomir Rintel
8ede6b2d1SLubomir Rintel #include <linux/delay.h>
9ede6b2d1SLubomir Rintel #include <linux/gpio/consumer.h>
10ede6b2d1SLubomir Rintel #include <linux/i2c.h>
11ede6b2d1SLubomir Rintel #include <linux/mfd/core.h>
12ede6b2d1SLubomir Rintel #include <linux/module.h>
13ede6b2d1SLubomir Rintel #include <linux/reboot.h>
14ede6b2d1SLubomir Rintel #include <linux/regmap.h>
15ede6b2d1SLubomir Rintel
16ede6b2d1SLubomir Rintel /* I2C registers that are multiplexing access to the EC RAM. */
17ede6b2d1SLubomir Rintel enum {
18ede6b2d1SLubomir Rintel EC_DATA_IN = 0x00,
19ede6b2d1SLubomir Rintel EC_RAM_OUT = 0x80,
20ede6b2d1SLubomir Rintel EC_RAM_IN = 0x81,
21ede6b2d1SLubomir Rintel };
22ede6b2d1SLubomir Rintel
23ede6b2d1SLubomir Rintel /* EC RAM registers. */
24ede6b2d1SLubomir Rintel enum {
25ede6b2d1SLubomir Rintel EC_MODEL = 0x30,
26ede6b2d1SLubomir Rintel EC_VERSION_MAJ = 0x31,
27ede6b2d1SLubomir Rintel EC_VERSION_MIN = 0x32,
28ede6b2d1SLubomir Rintel };
29ede6b2d1SLubomir Rintel
30ede6b2d1SLubomir Rintel struct kb3930 {
31ede6b2d1SLubomir Rintel struct i2c_client *client;
32ede6b2d1SLubomir Rintel struct regmap *ram_regmap;
33ede6b2d1SLubomir Rintel struct gpio_descs *off_gpios;
34ede6b2d1SLubomir Rintel };
35ede6b2d1SLubomir Rintel
365a2cf054SWei Yongjun static struct kb3930 *kb3930_power_off;
37ede6b2d1SLubomir Rintel
38ede6b2d1SLubomir Rintel #define EC_GPIO_WAVE 0
39ede6b2d1SLubomir Rintel #define EC_GPIO_OFF_MODE 1
40ede6b2d1SLubomir Rintel
41ede6b2d1SLubomir Rintel #define EC_OFF_MODE_REBOOT 0
42ede6b2d1SLubomir Rintel #define EC_OFF_MODE_POWER 1
43ede6b2d1SLubomir Rintel
kb3930_off(struct kb3930 * ddata,int off_mode)44ede6b2d1SLubomir Rintel static void kb3930_off(struct kb3930 *ddata, int off_mode)
45ede6b2d1SLubomir Rintel {
46ede6b2d1SLubomir Rintel gpiod_direction_output(ddata->off_gpios->desc[EC_GPIO_OFF_MODE],
47ede6b2d1SLubomir Rintel off_mode);
48ede6b2d1SLubomir Rintel
49ede6b2d1SLubomir Rintel /*
50ede6b2d1SLubomir Rintel * This creates a 10 Hz wave on EC_GPIO_WAVE that signals a
51ede6b2d1SLubomir Rintel * shutdown request to the EC. Once the EC detects it, it will
52ede6b2d1SLubomir Rintel * proceed to turn the power off or reset the board depending on
53ede6b2d1SLubomir Rintel * the value of EC_GPIO_OFF_MODE.
54ede6b2d1SLubomir Rintel */
55ede6b2d1SLubomir Rintel while (1) {
56ede6b2d1SLubomir Rintel mdelay(50);
57ede6b2d1SLubomir Rintel gpiod_direction_output(ddata->off_gpios->desc[EC_GPIO_WAVE], 0);
58ede6b2d1SLubomir Rintel mdelay(50);
59ede6b2d1SLubomir Rintel gpiod_direction_output(ddata->off_gpios->desc[EC_GPIO_WAVE], 1);
60ede6b2d1SLubomir Rintel }
61ede6b2d1SLubomir Rintel }
62ede6b2d1SLubomir Rintel
kb3930_restart(struct notifier_block * this,unsigned long mode,void * cmd)63ede6b2d1SLubomir Rintel static int kb3930_restart(struct notifier_block *this,
64ede6b2d1SLubomir Rintel unsigned long mode, void *cmd)
65ede6b2d1SLubomir Rintel {
66ede6b2d1SLubomir Rintel kb3930_off(kb3930_power_off, EC_OFF_MODE_REBOOT);
67ede6b2d1SLubomir Rintel return NOTIFY_DONE;
68ede6b2d1SLubomir Rintel }
69ede6b2d1SLubomir Rintel
kb3930_pm_power_off(void)70ede6b2d1SLubomir Rintel static void kb3930_pm_power_off(void)
71ede6b2d1SLubomir Rintel {
72ede6b2d1SLubomir Rintel kb3930_off(kb3930_power_off, EC_OFF_MODE_POWER);
73ede6b2d1SLubomir Rintel }
74ede6b2d1SLubomir Rintel
75ede6b2d1SLubomir Rintel static struct notifier_block kb3930_restart_nb = {
76ede6b2d1SLubomir Rintel .notifier_call = kb3930_restart,
77ede6b2d1SLubomir Rintel };
78ede6b2d1SLubomir Rintel
79ede6b2d1SLubomir Rintel static const struct mfd_cell ariel_ec_cells[] = {
80ede6b2d1SLubomir Rintel { .name = "dell-wyse-ariel-led", },
81ede6b2d1SLubomir Rintel { .name = "dell-wyse-ariel-power", },
82ede6b2d1SLubomir Rintel };
83ede6b2d1SLubomir Rintel
kb3930_ec_ram_reg_write(void * context,unsigned int reg,unsigned int val)84ede6b2d1SLubomir Rintel static int kb3930_ec_ram_reg_write(void *context, unsigned int reg,
85ede6b2d1SLubomir Rintel unsigned int val)
86ede6b2d1SLubomir Rintel {
87ede6b2d1SLubomir Rintel struct kb3930 *ddata = context;
88ede6b2d1SLubomir Rintel
89ede6b2d1SLubomir Rintel return i2c_smbus_write_word_data(ddata->client, EC_RAM_OUT,
90ede6b2d1SLubomir Rintel (val << 8) | reg);
91ede6b2d1SLubomir Rintel }
92ede6b2d1SLubomir Rintel
kb3930_ec_ram_reg_read(void * context,unsigned int reg,unsigned int * val)93ede6b2d1SLubomir Rintel static int kb3930_ec_ram_reg_read(void *context, unsigned int reg,
94ede6b2d1SLubomir Rintel unsigned int *val)
95ede6b2d1SLubomir Rintel {
96ede6b2d1SLubomir Rintel struct kb3930 *ddata = context;
97ede6b2d1SLubomir Rintel int ret;
98ede6b2d1SLubomir Rintel
99ede6b2d1SLubomir Rintel ret = i2c_smbus_write_word_data(ddata->client, EC_RAM_IN, reg);
100ede6b2d1SLubomir Rintel if (ret < 0)
101ede6b2d1SLubomir Rintel return ret;
102ede6b2d1SLubomir Rintel
103ede6b2d1SLubomir Rintel ret = i2c_smbus_read_word_data(ddata->client, EC_DATA_IN);
104ede6b2d1SLubomir Rintel if (ret < 0)
105ede6b2d1SLubomir Rintel return ret;
106ede6b2d1SLubomir Rintel
107ede6b2d1SLubomir Rintel *val = ret >> 8;
108ede6b2d1SLubomir Rintel return 0;
109ede6b2d1SLubomir Rintel }
110ede6b2d1SLubomir Rintel
111ede6b2d1SLubomir Rintel static const struct regmap_config kb3930_ram_regmap_config = {
112ede6b2d1SLubomir Rintel .name = "ec_ram",
113ede6b2d1SLubomir Rintel .reg_bits = 8,
114ede6b2d1SLubomir Rintel .val_bits = 8,
115ede6b2d1SLubomir Rintel .reg_stride = 1,
116ede6b2d1SLubomir Rintel .max_register = 0xff,
117ede6b2d1SLubomir Rintel .reg_write = kb3930_ec_ram_reg_write,
118ede6b2d1SLubomir Rintel .reg_read = kb3930_ec_ram_reg_read,
119ede6b2d1SLubomir Rintel .fast_io = false,
120ede6b2d1SLubomir Rintel };
121ede6b2d1SLubomir Rintel
kb3930_probe(struct i2c_client * client)122ede6b2d1SLubomir Rintel static int kb3930_probe(struct i2c_client *client)
123ede6b2d1SLubomir Rintel {
124ede6b2d1SLubomir Rintel struct device *dev = &client->dev;
125ede6b2d1SLubomir Rintel struct device_node *np = dev->of_node;
126ede6b2d1SLubomir Rintel struct kb3930 *ddata;
127ede6b2d1SLubomir Rintel unsigned int model;
128ede6b2d1SLubomir Rintel int ret;
129ede6b2d1SLubomir Rintel
130ede6b2d1SLubomir Rintel ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
131ede6b2d1SLubomir Rintel if (!ddata)
132ede6b2d1SLubomir Rintel return -ENOMEM;
133ede6b2d1SLubomir Rintel
134ede6b2d1SLubomir Rintel kb3930_power_off = ddata;
135ede6b2d1SLubomir Rintel ddata->client = client;
136ede6b2d1SLubomir Rintel i2c_set_clientdata(client, ddata);
137ede6b2d1SLubomir Rintel
138ede6b2d1SLubomir Rintel ddata->ram_regmap = devm_regmap_init(dev, NULL, ddata,
139ede6b2d1SLubomir Rintel &kb3930_ram_regmap_config);
140ede6b2d1SLubomir Rintel if (IS_ERR(ddata->ram_regmap))
141ede6b2d1SLubomir Rintel return PTR_ERR(ddata->ram_regmap);
142ede6b2d1SLubomir Rintel
143ede6b2d1SLubomir Rintel ret = regmap_read(ddata->ram_regmap, EC_MODEL, &model);
144ede6b2d1SLubomir Rintel if (ret < 0)
145ede6b2d1SLubomir Rintel return ret;
146ede6b2d1SLubomir Rintel
147ede6b2d1SLubomir Rintel /* Currently we only support the cells present on Dell Ariel model. */
148ede6b2d1SLubomir Rintel if (model != 'J') {
149ede6b2d1SLubomir Rintel dev_err(dev, "unknown board model: %02x\n", model);
150ede6b2d1SLubomir Rintel return -ENODEV;
151ede6b2d1SLubomir Rintel }
152ede6b2d1SLubomir Rintel
153ede6b2d1SLubomir Rintel ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
154ede6b2d1SLubomir Rintel ariel_ec_cells,
155ede6b2d1SLubomir Rintel ARRAY_SIZE(ariel_ec_cells),
156ede6b2d1SLubomir Rintel NULL, 0, NULL);
157ede6b2d1SLubomir Rintel if (ret)
158ede6b2d1SLubomir Rintel return ret;
159ede6b2d1SLubomir Rintel
160ede6b2d1SLubomir Rintel if (of_property_read_bool(np, "system-power-controller")) {
161ede6b2d1SLubomir Rintel ddata->off_gpios =
162ede6b2d1SLubomir Rintel devm_gpiod_get_array_optional(dev, "off", GPIOD_IN);
163ede6b2d1SLubomir Rintel if (IS_ERR(ddata->off_gpios))
164ede6b2d1SLubomir Rintel return PTR_ERR(ddata->off_gpios);
165ede6b2d1SLubomir Rintel if (ddata->off_gpios->ndescs < 2) {
166ede6b2d1SLubomir Rintel dev_err(dev, "invalid off-gpios property\n");
167ede6b2d1SLubomir Rintel return -EINVAL;
168ede6b2d1SLubomir Rintel }
169ede6b2d1SLubomir Rintel }
170ede6b2d1SLubomir Rintel
171ede6b2d1SLubomir Rintel if (ddata->off_gpios) {
172ede6b2d1SLubomir Rintel register_restart_handler(&kb3930_restart_nb);
173ede6b2d1SLubomir Rintel if (!pm_power_off)
174ede6b2d1SLubomir Rintel pm_power_off = kb3930_pm_power_off;
175ede6b2d1SLubomir Rintel }
176ede6b2d1SLubomir Rintel
177ede6b2d1SLubomir Rintel return 0;
178ede6b2d1SLubomir Rintel }
179ede6b2d1SLubomir Rintel
kb3930_remove(struct i2c_client * client)180ed5c2f5fSUwe Kleine-König static void kb3930_remove(struct i2c_client *client)
181ede6b2d1SLubomir Rintel {
182ede6b2d1SLubomir Rintel struct kb3930 *ddata = i2c_get_clientdata(client);
183ede6b2d1SLubomir Rintel
184ede6b2d1SLubomir Rintel if (ddata->off_gpios) {
185ede6b2d1SLubomir Rintel if (pm_power_off == kb3930_pm_power_off)
186ede6b2d1SLubomir Rintel pm_power_off = NULL;
187ede6b2d1SLubomir Rintel unregister_restart_handler(&kb3930_restart_nb);
188ede6b2d1SLubomir Rintel }
189ede6b2d1SLubomir Rintel kb3930_power_off = NULL;
190ede6b2d1SLubomir Rintel }
191ede6b2d1SLubomir Rintel
192ede6b2d1SLubomir Rintel static const struct of_device_id kb3930_dt_ids[] = {
193ede6b2d1SLubomir Rintel { .compatible = "ene,kb3930" },
194ede6b2d1SLubomir Rintel { }
195ede6b2d1SLubomir Rintel };
196ede6b2d1SLubomir Rintel MODULE_DEVICE_TABLE(of, kb3930_dt_ids);
197ede6b2d1SLubomir Rintel
198ede6b2d1SLubomir Rintel static struct i2c_driver kb3930_driver = {
199*9816d859SUwe Kleine-König .probe = kb3930_probe,
200ede6b2d1SLubomir Rintel .remove = kb3930_remove,
201ede6b2d1SLubomir Rintel .driver = {
202ede6b2d1SLubomir Rintel .name = "ene-kb3930",
203e9063feeSKrzysztof Kozlowski .of_match_table = kb3930_dt_ids,
204ede6b2d1SLubomir Rintel },
205ede6b2d1SLubomir Rintel };
206ede6b2d1SLubomir Rintel module_i2c_driver(kb3930_driver);
207ede6b2d1SLubomir Rintel
208ede6b2d1SLubomir Rintel MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
209ede6b2d1SLubomir Rintel MODULE_DESCRIPTION("ENE KB3930 Embedded Controller Driver");
210ede6b2d1SLubomir Rintel MODULE_LICENSE("Dual BSD/GPL");
211