brintos

brintos / linux-shallow public Read only

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