xref: /openbmc/linux/drivers/mfd/acer-ec-a500.c (revision 92eba6802c2b1ffb30f1454e9d99ef980b94bbbf)
1*92eba680SDmitry Osipenko // SPDX-License-Identifier: GPL-2.0+
2*92eba680SDmitry Osipenko /*
3*92eba680SDmitry Osipenko  * Acer Iconia Tab A500 Embedded Controller Driver
4*92eba680SDmitry Osipenko  *
5*92eba680SDmitry Osipenko  * Copyright 2020 GRATE-driver project
6*92eba680SDmitry Osipenko  */
7*92eba680SDmitry Osipenko 
8*92eba680SDmitry Osipenko #include <linux/delay.h>
9*92eba680SDmitry Osipenko #include <linux/i2c.h>
10*92eba680SDmitry Osipenko #include <linux/mfd/core.h>
11*92eba680SDmitry Osipenko #include <linux/module.h>
12*92eba680SDmitry Osipenko #include <linux/of_device.h>
13*92eba680SDmitry Osipenko #include <linux/reboot.h>
14*92eba680SDmitry Osipenko #include <linux/regmap.h>
15*92eba680SDmitry Osipenko 
16*92eba680SDmitry Osipenko #define A500_EC_I2C_ERR_TIMEOUT		500
17*92eba680SDmitry Osipenko #define A500_EC_POWER_CMD_TIMEOUT	1000
18*92eba680SDmitry Osipenko 
19*92eba680SDmitry Osipenko /*
20*92eba680SDmitry Osipenko  * Controller's firmware expects specific command opcodes to be used for the
21*92eba680SDmitry Osipenko  * corresponding registers. Unsupported commands are skipped by the firmware.
22*92eba680SDmitry Osipenko  */
23*92eba680SDmitry Osipenko #define CMD_SHUTDOWN			0x0
24*92eba680SDmitry Osipenko #define CMD_WARM_REBOOT			0x0
25*92eba680SDmitry Osipenko #define CMD_COLD_REBOOT			0x1
26*92eba680SDmitry Osipenko 
27*92eba680SDmitry Osipenko enum {
28*92eba680SDmitry Osipenko 	REG_CURRENT_NOW = 0x03,
29*92eba680SDmitry Osipenko 	REG_SHUTDOWN = 0x52,
30*92eba680SDmitry Osipenko 	REG_WARM_REBOOT = 0x54,
31*92eba680SDmitry Osipenko 	REG_COLD_REBOOT = 0x55,
32*92eba680SDmitry Osipenko };
33*92eba680SDmitry Osipenko 
34*92eba680SDmitry Osipenko static struct i2c_client *a500_ec_client_pm_off;
35*92eba680SDmitry Osipenko 
36*92eba680SDmitry Osipenko static int a500_ec_read(void *context, const void *reg_buf, size_t reg_size,
37*92eba680SDmitry Osipenko 			void *val_buf, size_t val_sizel)
38*92eba680SDmitry Osipenko {
39*92eba680SDmitry Osipenko 	struct i2c_client *client = context;
40*92eba680SDmitry Osipenko 	unsigned int reg, retries = 5;
41*92eba680SDmitry Osipenko 	u16 *ret_val = val_buf;
42*92eba680SDmitry Osipenko 	s32 ret = 0;
43*92eba680SDmitry Osipenko 
44*92eba680SDmitry Osipenko 	reg = *(u8 *)reg_buf;
45*92eba680SDmitry Osipenko 
46*92eba680SDmitry Osipenko 	while (retries-- > 0) {
47*92eba680SDmitry Osipenko 		ret = i2c_smbus_read_word_data(client, reg);
48*92eba680SDmitry Osipenko 		if (ret >= 0)
49*92eba680SDmitry Osipenko 			break;
50*92eba680SDmitry Osipenko 
51*92eba680SDmitry Osipenko 		msleep(A500_EC_I2C_ERR_TIMEOUT);
52*92eba680SDmitry Osipenko 	}
53*92eba680SDmitry Osipenko 
54*92eba680SDmitry Osipenko 	if (ret < 0) {
55*92eba680SDmitry Osipenko 		dev_err(&client->dev, "read 0x%x failed: %d\n", reg, ret);
56*92eba680SDmitry Osipenko 		return ret;
57*92eba680SDmitry Osipenko 	}
58*92eba680SDmitry Osipenko 
59*92eba680SDmitry Osipenko 	*ret_val = ret;
60*92eba680SDmitry Osipenko 
61*92eba680SDmitry Osipenko 	if (reg == REG_CURRENT_NOW)
62*92eba680SDmitry Osipenko 		fsleep(10000);
63*92eba680SDmitry Osipenko 
64*92eba680SDmitry Osipenko 	return 0;
65*92eba680SDmitry Osipenko }
66*92eba680SDmitry Osipenko 
67*92eba680SDmitry Osipenko static int a500_ec_write(void *context, const void *data, size_t count)
68*92eba680SDmitry Osipenko {
69*92eba680SDmitry Osipenko 	struct i2c_client *client = context;
70*92eba680SDmitry Osipenko 	unsigned int reg, val, retries = 5;
71*92eba680SDmitry Osipenko 	s32 ret = 0;
72*92eba680SDmitry Osipenko 
73*92eba680SDmitry Osipenko 	reg = *(u8  *)(data + 0);
74*92eba680SDmitry Osipenko 	val = *(u16 *)(data + 1);
75*92eba680SDmitry Osipenko 
76*92eba680SDmitry Osipenko 	while (retries-- > 0) {
77*92eba680SDmitry Osipenko 		ret = i2c_smbus_write_word_data(client, reg, val);
78*92eba680SDmitry Osipenko 		if (ret >= 0)
79*92eba680SDmitry Osipenko 			break;
80*92eba680SDmitry Osipenko 
81*92eba680SDmitry Osipenko 		msleep(A500_EC_I2C_ERR_TIMEOUT);
82*92eba680SDmitry Osipenko 	}
83*92eba680SDmitry Osipenko 
84*92eba680SDmitry Osipenko 	if (ret < 0) {
85*92eba680SDmitry Osipenko 		dev_err(&client->dev, "write 0x%x failed: %d\n", reg, ret);
86*92eba680SDmitry Osipenko 		return ret;
87*92eba680SDmitry Osipenko 	}
88*92eba680SDmitry Osipenko 
89*92eba680SDmitry Osipenko 	return 0;
90*92eba680SDmitry Osipenko }
91*92eba680SDmitry Osipenko 
92*92eba680SDmitry Osipenko static const struct regmap_config a500_ec_regmap_config = {
93*92eba680SDmitry Osipenko 	.name = "KB930",
94*92eba680SDmitry Osipenko 	.reg_bits = 8,
95*92eba680SDmitry Osipenko 	.val_bits = 16,
96*92eba680SDmitry Osipenko 	.max_register = 0xff,
97*92eba680SDmitry Osipenko };
98*92eba680SDmitry Osipenko 
99*92eba680SDmitry Osipenko static const struct regmap_bus a500_ec_regmap_bus = {
100*92eba680SDmitry Osipenko 	.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
101*92eba680SDmitry Osipenko 	.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
102*92eba680SDmitry Osipenko 	.write = a500_ec_write,
103*92eba680SDmitry Osipenko 	.read = a500_ec_read,
104*92eba680SDmitry Osipenko 	.max_raw_read = 2,
105*92eba680SDmitry Osipenko };
106*92eba680SDmitry Osipenko 
107*92eba680SDmitry Osipenko static void a500_ec_poweroff(void)
108*92eba680SDmitry Osipenko {
109*92eba680SDmitry Osipenko 	i2c_smbus_write_word_data(a500_ec_client_pm_off,
110*92eba680SDmitry Osipenko 				  REG_SHUTDOWN, CMD_SHUTDOWN);
111*92eba680SDmitry Osipenko 
112*92eba680SDmitry Osipenko 	mdelay(A500_EC_POWER_CMD_TIMEOUT);
113*92eba680SDmitry Osipenko }
114*92eba680SDmitry Osipenko 
115*92eba680SDmitry Osipenko static int a500_ec_restart_notify(struct notifier_block *this,
116*92eba680SDmitry Osipenko 				  unsigned long reboot_mode, void *data)
117*92eba680SDmitry Osipenko {
118*92eba680SDmitry Osipenko 	if (reboot_mode == REBOOT_WARM)
119*92eba680SDmitry Osipenko 		i2c_smbus_write_word_data(a500_ec_client_pm_off,
120*92eba680SDmitry Osipenko 					  REG_WARM_REBOOT, CMD_WARM_REBOOT);
121*92eba680SDmitry Osipenko 	else
122*92eba680SDmitry Osipenko 		i2c_smbus_write_word_data(a500_ec_client_pm_off,
123*92eba680SDmitry Osipenko 					  REG_COLD_REBOOT, CMD_COLD_REBOOT);
124*92eba680SDmitry Osipenko 
125*92eba680SDmitry Osipenko 	mdelay(A500_EC_POWER_CMD_TIMEOUT);
126*92eba680SDmitry Osipenko 
127*92eba680SDmitry Osipenko 	return NOTIFY_DONE;
128*92eba680SDmitry Osipenko }
129*92eba680SDmitry Osipenko 
130*92eba680SDmitry Osipenko static struct notifier_block a500_ec_restart_handler = {
131*92eba680SDmitry Osipenko 	.notifier_call = a500_ec_restart_notify,
132*92eba680SDmitry Osipenko 	.priority = 200,
133*92eba680SDmitry Osipenko };
134*92eba680SDmitry Osipenko 
135*92eba680SDmitry Osipenko static const struct mfd_cell a500_ec_cells[] = {
136*92eba680SDmitry Osipenko 	{ .name = "acer-a500-iconia-battery", },
137*92eba680SDmitry Osipenko 	{ .name = "acer-a500-iconia-leds", },
138*92eba680SDmitry Osipenko };
139*92eba680SDmitry Osipenko 
140*92eba680SDmitry Osipenko static int a500_ec_probe(struct i2c_client *client)
141*92eba680SDmitry Osipenko {
142*92eba680SDmitry Osipenko 	struct regmap *regmap;
143*92eba680SDmitry Osipenko 	int err;
144*92eba680SDmitry Osipenko 
145*92eba680SDmitry Osipenko 	regmap = devm_regmap_init(&client->dev, &a500_ec_regmap_bus,
146*92eba680SDmitry Osipenko 				  client, &a500_ec_regmap_config);
147*92eba680SDmitry Osipenko 	if (IS_ERR(regmap))
148*92eba680SDmitry Osipenko 		return PTR_ERR(regmap);
149*92eba680SDmitry Osipenko 
150*92eba680SDmitry Osipenko 	err = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_AUTO,
151*92eba680SDmitry Osipenko 				   a500_ec_cells, ARRAY_SIZE(a500_ec_cells),
152*92eba680SDmitry Osipenko 				   NULL, 0, NULL);
153*92eba680SDmitry Osipenko 	if (err) {
154*92eba680SDmitry Osipenko 		dev_err(&client->dev, "failed to add sub-devices: %d\n", err);
155*92eba680SDmitry Osipenko 		return err;
156*92eba680SDmitry Osipenko 	}
157*92eba680SDmitry Osipenko 
158*92eba680SDmitry Osipenko 	if (of_device_is_system_power_controller(client->dev.of_node)) {
159*92eba680SDmitry Osipenko 		a500_ec_client_pm_off = client;
160*92eba680SDmitry Osipenko 
161*92eba680SDmitry Osipenko 		err = register_restart_handler(&a500_ec_restart_handler);
162*92eba680SDmitry Osipenko 		if (err)
163*92eba680SDmitry Osipenko 			return err;
164*92eba680SDmitry Osipenko 
165*92eba680SDmitry Osipenko 		if (!pm_power_off)
166*92eba680SDmitry Osipenko 			pm_power_off = a500_ec_poweroff;
167*92eba680SDmitry Osipenko 	}
168*92eba680SDmitry Osipenko 
169*92eba680SDmitry Osipenko 	return 0;
170*92eba680SDmitry Osipenko }
171*92eba680SDmitry Osipenko 
172*92eba680SDmitry Osipenko static int a500_ec_remove(struct i2c_client *client)
173*92eba680SDmitry Osipenko {
174*92eba680SDmitry Osipenko 	if (of_device_is_system_power_controller(client->dev.of_node)) {
175*92eba680SDmitry Osipenko 		if (pm_power_off == a500_ec_poweroff)
176*92eba680SDmitry Osipenko 			pm_power_off = NULL;
177*92eba680SDmitry Osipenko 
178*92eba680SDmitry Osipenko 		unregister_restart_handler(&a500_ec_restart_handler);
179*92eba680SDmitry Osipenko 	}
180*92eba680SDmitry Osipenko 
181*92eba680SDmitry Osipenko 	return 0;
182*92eba680SDmitry Osipenko }
183*92eba680SDmitry Osipenko 
184*92eba680SDmitry Osipenko static const struct of_device_id a500_ec_match[] = {
185*92eba680SDmitry Osipenko 	{ .compatible = "acer,a500-iconia-ec" },
186*92eba680SDmitry Osipenko 	{ }
187*92eba680SDmitry Osipenko };
188*92eba680SDmitry Osipenko MODULE_DEVICE_TABLE(of, a500_ec_match);
189*92eba680SDmitry Osipenko 
190*92eba680SDmitry Osipenko static struct i2c_driver a500_ec_driver = {
191*92eba680SDmitry Osipenko 	.driver = {
192*92eba680SDmitry Osipenko 		.name = "acer-a500-embedded-controller",
193*92eba680SDmitry Osipenko 		.of_match_table = a500_ec_match,
194*92eba680SDmitry Osipenko 	},
195*92eba680SDmitry Osipenko 	.probe_new = a500_ec_probe,
196*92eba680SDmitry Osipenko 	.remove = a500_ec_remove,
197*92eba680SDmitry Osipenko };
198*92eba680SDmitry Osipenko module_i2c_driver(a500_ec_driver);
199*92eba680SDmitry Osipenko 
200*92eba680SDmitry Osipenko MODULE_DESCRIPTION("Acer Iconia Tab A500 Embedded Controller driver");
201*92eba680SDmitry Osipenko MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>");
202*92eba680SDmitry Osipenko MODULE_LICENSE("GPL");
203