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