Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * The Netronix embedded controller is a microcontroller found in some
0004  * e-book readers designed by the original design manufacturer Netronix, Inc.
0005  * It contains RTC, battery monitoring, system power management, and PWM
0006  * functionality.
0007  *
0008  * This driver implements register access, version detection, and system
0009  * power-off/reset.
0010  *
0011  * Copyright 2020 Jonathan Neuschäfer <j.neuschaefer@gmx.net>
0012  */
0013 
0014 #include <linux/delay.h>
0015 #include <linux/errno.h>
0016 #include <linux/i2c.h>
0017 #include <linux/mfd/core.h>
0018 #include <linux/mfd/ntxec.h>
0019 #include <linux/module.h>
0020 #include <linux/pm.h>
0021 #include <linux/reboot.h>
0022 #include <linux/regmap.h>
0023 #include <linux/types.h>
0024 #include <asm/unaligned.h>
0025 
0026 #define NTXEC_REG_VERSION   0x00
0027 #define NTXEC_REG_POWEROFF  0x50
0028 #define NTXEC_REG_POWERKEEP 0x70
0029 #define NTXEC_REG_RESET     0x90
0030 
0031 #define NTXEC_POWEROFF_VALUE    0x0100
0032 #define NTXEC_POWERKEEP_VALUE   0x0800
0033 #define NTXEC_RESET_VALUE   0xff00
0034 
0035 static struct i2c_client *poweroff_restart_client;
0036 
0037 static void ntxec_poweroff(void)
0038 {
0039     int res;
0040     u8 buf[3] = { NTXEC_REG_POWEROFF };
0041     struct i2c_msg msgs[] = {
0042         {
0043             .addr = poweroff_restart_client->addr,
0044             .flags = 0,
0045             .len = sizeof(buf),
0046             .buf = buf,
0047         },
0048     };
0049 
0050     put_unaligned_be16(NTXEC_POWEROFF_VALUE, buf + 1);
0051 
0052     res = i2c_transfer(poweroff_restart_client->adapter, msgs, ARRAY_SIZE(msgs));
0053     if (res < 0)
0054         dev_warn(&poweroff_restart_client->dev,
0055              "Failed to power off (err = %d)\n", res);
0056 
0057     /*
0058      * The time from the register write until the host CPU is powered off
0059      * has been observed to be about 2.5 to 3 seconds. Sleep long enough to
0060      * safely avoid returning from the poweroff handler.
0061      */
0062     msleep(5000);
0063 }
0064 
0065 static int ntxec_restart(struct notifier_block *nb,
0066              unsigned long action, void *data)
0067 {
0068     int res;
0069     u8 buf[3] = { NTXEC_REG_RESET };
0070     /*
0071      * NOTE: The lower half of the reset value is not sent, because sending
0072      * it causes an I2C error. (The reset handler in the downstream driver
0073      * does send the full two-byte value, but doesn't check the result).
0074      */
0075     struct i2c_msg msgs[] = {
0076         {
0077             .addr = poweroff_restart_client->addr,
0078             .flags = 0,
0079             .len = sizeof(buf) - 1,
0080             .buf = buf,
0081         },
0082     };
0083 
0084     put_unaligned_be16(NTXEC_RESET_VALUE, buf + 1);
0085 
0086     res = i2c_transfer(poweroff_restart_client->adapter, msgs, ARRAY_SIZE(msgs));
0087     if (res < 0)
0088         dev_warn(&poweroff_restart_client->dev,
0089              "Failed to restart (err = %d)\n", res);
0090 
0091     return NOTIFY_DONE;
0092 }
0093 
0094 static struct notifier_block ntxec_restart_handler = {
0095     .notifier_call = ntxec_restart,
0096     .priority = 128,
0097 };
0098 
0099 static int regmap_ignore_write(void *context,
0100                    unsigned int reg, unsigned int val)
0101 
0102 {
0103     struct regmap *regmap = context;
0104 
0105     regmap_write(regmap, reg, val);
0106 
0107     return 0;
0108 }
0109 
0110 static int regmap_wrap_read(void *context, unsigned int reg,
0111                 unsigned int *val)
0112 {
0113     struct regmap *regmap = context;
0114 
0115     return regmap_read(regmap, reg, val);
0116 }
0117 
0118 /*
0119  * Some firmware versions do not ack written data, add a wrapper. It
0120  * is used to stack another regmap on top.
0121  */
0122 static const struct regmap_config regmap_config_noack = {
0123     .name = "ntxec_noack",
0124     .reg_bits = 8,
0125     .val_bits = 16,
0126     .cache_type = REGCACHE_NONE,
0127     .reg_write = regmap_ignore_write,
0128     .reg_read = regmap_wrap_read
0129 };
0130 
0131 static const struct regmap_config regmap_config = {
0132     .name = "ntxec",
0133     .reg_bits = 8,
0134     .val_bits = 16,
0135     .cache_type = REGCACHE_NONE,
0136     .val_format_endian = REGMAP_ENDIAN_BIG,
0137 };
0138 
0139 static const struct mfd_cell ntxec_subdev[] = {
0140     { .name = "ntxec-rtc" },
0141     { .name = "ntxec-pwm" },
0142 };
0143 
0144 static const struct mfd_cell ntxec_subdev_pwm[] = {
0145     { .name = "ntxec-pwm" },
0146 };
0147 
0148 static int ntxec_probe(struct i2c_client *client)
0149 {
0150     struct ntxec *ec;
0151     unsigned int version;
0152     int res;
0153     const struct mfd_cell *subdevs;
0154     size_t n_subdevs;
0155 
0156     ec = devm_kmalloc(&client->dev, sizeof(*ec), GFP_KERNEL);
0157     if (!ec)
0158         return -ENOMEM;
0159 
0160     ec->dev = &client->dev;
0161 
0162     ec->regmap = devm_regmap_init_i2c(client, &regmap_config);
0163     if (IS_ERR(ec->regmap)) {
0164         dev_err(ec->dev, "Failed to set up regmap for device\n");
0165         return PTR_ERR(ec->regmap);
0166     }
0167 
0168     /* Determine the firmware version */
0169     res = regmap_read(ec->regmap, NTXEC_REG_VERSION, &version);
0170     if (res < 0) {
0171         dev_err(ec->dev, "Failed to read firmware version number\n");
0172         return res;
0173     }
0174 
0175     /* Bail out if we encounter an unknown firmware version */
0176     switch (version) {
0177     case NTXEC_VERSION_KOBO_AURA:
0178         subdevs = ntxec_subdev;
0179         n_subdevs = ARRAY_SIZE(ntxec_subdev);
0180         break;
0181     case NTXEC_VERSION_TOLINO_SHINE2:
0182         subdevs = ntxec_subdev_pwm;
0183         n_subdevs = ARRAY_SIZE(ntxec_subdev_pwm);
0184         /* Another regmap stacked on top of the other */
0185         ec->regmap = devm_regmap_init(ec->dev, NULL,
0186                           ec->regmap,
0187                           &regmap_config_noack);
0188         if (IS_ERR(ec->regmap))
0189             return PTR_ERR(ec->regmap);
0190         break;
0191     default:
0192         dev_err(ec->dev,
0193             "Netronix embedded controller version %04x is not supported.\n",
0194             version);
0195         return -ENODEV;
0196     }
0197 
0198     dev_info(ec->dev,
0199          "Netronix embedded controller version %04x detected.\n", version);
0200 
0201     if (of_device_is_system_power_controller(ec->dev->of_node)) {
0202         /*
0203          * Set the 'powerkeep' bit. This is necessary on some boards
0204          * in order to keep the system running.
0205          */
0206         res = regmap_write(ec->regmap, NTXEC_REG_POWERKEEP,
0207                    NTXEC_POWERKEEP_VALUE);
0208         if (res < 0)
0209             return res;
0210 
0211         if (poweroff_restart_client)
0212             /*
0213              * Another instance of the driver already took
0214              * poweroff/restart duties.
0215              */
0216             dev_err(ec->dev, "poweroff_restart_client already assigned\n");
0217         else
0218             poweroff_restart_client = client;
0219 
0220         if (pm_power_off)
0221             /* Another driver already registered a poweroff handler. */
0222             dev_err(ec->dev, "pm_power_off already assigned\n");
0223         else
0224             pm_power_off = ntxec_poweroff;
0225 
0226         res = register_restart_handler(&ntxec_restart_handler);
0227         if (res)
0228             dev_err(ec->dev,
0229                 "Failed to register restart handler: %d\n", res);
0230     }
0231 
0232     i2c_set_clientdata(client, ec);
0233 
0234     res = devm_mfd_add_devices(ec->dev, PLATFORM_DEVID_NONE,
0235                    subdevs, n_subdevs, NULL, 0, NULL);
0236     if (res)
0237         dev_err(ec->dev, "Failed to add subdevices: %d\n", res);
0238 
0239     return res;
0240 }
0241 
0242 static int ntxec_remove(struct i2c_client *client)
0243 {
0244     if (client == poweroff_restart_client) {
0245         poweroff_restart_client = NULL;
0246         pm_power_off = NULL;
0247         unregister_restart_handler(&ntxec_restart_handler);
0248     }
0249 
0250     return 0;
0251 }
0252 
0253 static const struct of_device_id of_ntxec_match_table[] = {
0254     { .compatible = "netronix,ntxec", },
0255     {}
0256 };
0257 MODULE_DEVICE_TABLE(of, of_ntxec_match_table);
0258 
0259 static struct i2c_driver ntxec_driver = {
0260     .driver = {
0261         .name = "ntxec",
0262         .of_match_table = of_ntxec_match_table,
0263     },
0264     .probe_new = ntxec_probe,
0265     .remove = ntxec_remove,
0266 };
0267 module_i2c_driver(ntxec_driver);
0268 
0269 MODULE_AUTHOR("Jonathan Neuschäfer <j.neuschaefer@gmx.net>");
0270 MODULE_DESCRIPTION("Core driver for Netronix EC");
0271 MODULE_LICENSE("GPL");