Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Acer Iconia Tab A500 Embedded Controller Driver
0004  *
0005  * Copyright 2020 GRATE-driver project
0006  */
0007 
0008 #include <linux/delay.h>
0009 #include <linux/i2c.h>
0010 #include <linux/mfd/core.h>
0011 #include <linux/module.h>
0012 #include <linux/of_device.h>
0013 #include <linux/reboot.h>
0014 #include <linux/regmap.h>
0015 
0016 #define A500_EC_I2C_ERR_TIMEOUT     500
0017 #define A500_EC_POWER_CMD_TIMEOUT   1000
0018 
0019 /*
0020  * Controller's firmware expects specific command opcodes to be used for the
0021  * corresponding registers. Unsupported commands are skipped by the firmware.
0022  */
0023 #define CMD_SHUTDOWN            0x0
0024 #define CMD_WARM_REBOOT         0x0
0025 #define CMD_COLD_REBOOT         0x1
0026 
0027 enum {
0028     REG_CURRENT_NOW = 0x03,
0029     REG_SHUTDOWN = 0x52,
0030     REG_WARM_REBOOT = 0x54,
0031     REG_COLD_REBOOT = 0x55,
0032 };
0033 
0034 static struct i2c_client *a500_ec_client_pm_off;
0035 
0036 static int a500_ec_read(void *context, const void *reg_buf, size_t reg_size,
0037             void *val_buf, size_t val_sizel)
0038 {
0039     struct i2c_client *client = context;
0040     unsigned int reg, retries = 5;
0041     u16 *ret_val = val_buf;
0042     s32 ret = 0;
0043 
0044     reg = *(u8 *)reg_buf;
0045 
0046     while (retries-- > 0) {
0047         ret = i2c_smbus_read_word_data(client, reg);
0048         if (ret >= 0)
0049             break;
0050 
0051         msleep(A500_EC_I2C_ERR_TIMEOUT);
0052     }
0053 
0054     if (ret < 0) {
0055         dev_err(&client->dev, "read 0x%x failed: %d\n", reg, ret);
0056         return ret;
0057     }
0058 
0059     *ret_val = ret;
0060 
0061     if (reg == REG_CURRENT_NOW)
0062         fsleep(10000);
0063 
0064     return 0;
0065 }
0066 
0067 static int a500_ec_write(void *context, const void *data, size_t count)
0068 {
0069     struct i2c_client *client = context;
0070     unsigned int reg, val, retries = 5;
0071     s32 ret = 0;
0072 
0073     reg = *(u8  *)(data + 0);
0074     val = *(u16 *)(data + 1);
0075 
0076     while (retries-- > 0) {
0077         ret = i2c_smbus_write_word_data(client, reg, val);
0078         if (ret >= 0)
0079             break;
0080 
0081         msleep(A500_EC_I2C_ERR_TIMEOUT);
0082     }
0083 
0084     if (ret < 0) {
0085         dev_err(&client->dev, "write 0x%x failed: %d\n", reg, ret);
0086         return ret;
0087     }
0088 
0089     return 0;
0090 }
0091 
0092 static const struct regmap_config a500_ec_regmap_config = {
0093     .name = "KB930",
0094     .reg_bits = 8,
0095     .val_bits = 16,
0096     .max_register = 0xff,
0097 };
0098 
0099 static const struct regmap_bus a500_ec_regmap_bus = {
0100     .reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
0101     .val_format_endian_default = REGMAP_ENDIAN_LITTLE,
0102     .write = a500_ec_write,
0103     .read = a500_ec_read,
0104     .max_raw_read = 2,
0105 };
0106 
0107 static void a500_ec_poweroff(void)
0108 {
0109     i2c_smbus_write_word_data(a500_ec_client_pm_off,
0110                   REG_SHUTDOWN, CMD_SHUTDOWN);
0111 
0112     mdelay(A500_EC_POWER_CMD_TIMEOUT);
0113 }
0114 
0115 static int a500_ec_restart_notify(struct notifier_block *this,
0116                   unsigned long reboot_mode, void *data)
0117 {
0118     if (reboot_mode == REBOOT_WARM)
0119         i2c_smbus_write_word_data(a500_ec_client_pm_off,
0120                       REG_WARM_REBOOT, CMD_WARM_REBOOT);
0121     else
0122         i2c_smbus_write_word_data(a500_ec_client_pm_off,
0123                       REG_COLD_REBOOT, CMD_COLD_REBOOT);
0124 
0125     mdelay(A500_EC_POWER_CMD_TIMEOUT);
0126 
0127     return NOTIFY_DONE;
0128 }
0129 
0130 static struct notifier_block a500_ec_restart_handler = {
0131     .notifier_call = a500_ec_restart_notify,
0132     .priority = 200,
0133 };
0134 
0135 static const struct mfd_cell a500_ec_cells[] = {
0136     { .name = "acer-a500-iconia-battery", },
0137     { .name = "acer-a500-iconia-leds", },
0138 };
0139 
0140 static int a500_ec_probe(struct i2c_client *client)
0141 {
0142     struct regmap *regmap;
0143     int err;
0144 
0145     regmap = devm_regmap_init(&client->dev, &a500_ec_regmap_bus,
0146                   client, &a500_ec_regmap_config);
0147     if (IS_ERR(regmap))
0148         return PTR_ERR(regmap);
0149 
0150     err = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_AUTO,
0151                    a500_ec_cells, ARRAY_SIZE(a500_ec_cells),
0152                    NULL, 0, NULL);
0153     if (err) {
0154         dev_err(&client->dev, "failed to add sub-devices: %d\n", err);
0155         return err;
0156     }
0157 
0158     if (of_device_is_system_power_controller(client->dev.of_node)) {
0159         a500_ec_client_pm_off = client;
0160 
0161         err = register_restart_handler(&a500_ec_restart_handler);
0162         if (err)
0163             return err;
0164 
0165         if (!pm_power_off)
0166             pm_power_off = a500_ec_poweroff;
0167     }
0168 
0169     return 0;
0170 }
0171 
0172 static int a500_ec_remove(struct i2c_client *client)
0173 {
0174     if (of_device_is_system_power_controller(client->dev.of_node)) {
0175         if (pm_power_off == a500_ec_poweroff)
0176             pm_power_off = NULL;
0177 
0178         unregister_restart_handler(&a500_ec_restart_handler);
0179     }
0180 
0181     return 0;
0182 }
0183 
0184 static const struct of_device_id a500_ec_match[] = {
0185     { .compatible = "acer,a500-iconia-ec" },
0186     { }
0187 };
0188 MODULE_DEVICE_TABLE(of, a500_ec_match);
0189 
0190 static struct i2c_driver a500_ec_driver = {
0191     .driver = {
0192         .name = "acer-a500-embedded-controller",
0193         .of_match_table = a500_ec_match,
0194     },
0195     .probe_new = a500_ec_probe,
0196     .remove = a500_ec_remove,
0197 };
0198 module_i2c_driver(a500_ec_driver);
0199 
0200 MODULE_DESCRIPTION("Acer Iconia Tab A500 Embedded Controller driver");
0201 MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>");
0202 MODULE_LICENSE("GPL");