xref: /openbmc/linux/drivers/mfd/ene-kb3930.c (revision 9816d859)
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