diff mbox

[v2,1/2] power: act8945a: add charger driver for ACT8945A

Message ID 1452242773-18887-2-git-send-email-wenyou.yang@atmel.com (mailing list archive)
State Not Applicable, archived
Headers show

Commit Message

Wenyou Yang Jan. 8, 2016, 8:46 a.m. UTC
This patch adds new driver for Active-semi ACT8945A ActivePath
charger (part of ACT8945A MFD driver) providing power supply class
information to userspace.

The driver is configured through DTS (battery and system related
settings) and sysfs entries (timers and input over-voltage threshold).

Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com>
---

Changes in v2:
 1./ Substitute of_property_read_bool() for of_get_property().
 2./ Substitute devm_power_supply_register() for power_supply_register().
 3./ Use module_platform_driver(), instead of subsys_initcall().
 4./ Substitute MODULE_LICENSE("GPL") for MODULE_LICENSE("GPL v2").

 drivers/power/Kconfig            |    7 +
 drivers/power/Makefile           |    1 +
 drivers/power/act8945a_charger.c |  386 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 394 insertions(+)
 create mode 100644 drivers/power/act8945a_charger.c

Comments

Krzysztof Kozlowski Jan. 8, 2016, 12:17 p.m. UTC | #1
2016-01-08 17:46 GMT+09:00 Wenyou Yang <wenyou.yang@atmel.com>:
> This patch adds new driver for Active-semi ACT8945A ActivePath
> charger (part of ACT8945A MFD driver) providing power supply class
> information to userspace.
>
> The driver is configured through DTS (battery and system related
> settings) and sysfs entries (timers and input over-voltage threshold).
>
> Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com>
> ---
>
> Changes in v2:
>  1./ Substitute of_property_read_bool() for of_get_property().
>  2./ Substitute devm_power_supply_register() for power_supply_register().
>  3./ Use module_platform_driver(), instead of subsys_initcall().
>  4./ Substitute MODULE_LICENSE("GPL") for MODULE_LICENSE("GPL v2").
>
>  drivers/power/Kconfig            |    7 +
>  drivers/power/Makefile           |    1 +
>  drivers/power/act8945a_charger.c |  386 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 394 insertions(+)
>  create mode 100644 drivers/power/act8945a_charger.c
>
> diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
> index 1ddd13c..ae75211 100644
> --- a/drivers/power/Kconfig
> +++ b/drivers/power/Kconfig
> @@ -75,6 +75,13 @@ config BATTERY_88PM860X
>         help
>           Say Y here to enable battery monitor for Marvell 88PM860x chip.
>
> +config BATTERY_ACT8945A
> +       tristate "Active-semi ACT8945A charger driver"
> +       depends on MFD_ACT8945A
> +       help
> +         Say Y here to enable support for power supply provided by
> +         Active-semi ActivePath ACT8945A charger.
> +
>  config BATTERY_DS2760
>         tristate "DS2760 battery driver (HP iPAQ & others)"
>         depends on W1 && W1_SLAVE_DS2760
> diff --git a/drivers/power/Makefile b/drivers/power/Makefile
> index 0e4eab5..e46b75d 100644
> --- a/drivers/power/Makefile
> +++ b/drivers/power/Makefile
> @@ -17,6 +17,7 @@ obj-$(CONFIG_WM8350_POWER)    += wm8350_power.o
>  obj-$(CONFIG_TEST_POWER)       += test_power.o
>
>  obj-$(CONFIG_BATTERY_88PM860X) += 88pm860x_battery.o
> +obj-$(CONFIG_BATTERY_ACT8945A) += act8945a_charger.o
>  obj-$(CONFIG_BATTERY_DS2760)   += ds2760_battery.o
>  obj-$(CONFIG_BATTERY_DS2780)   += ds2780_battery.o
>  obj-$(CONFIG_BATTERY_DS2781)   += ds2781_battery.o
> diff --git a/drivers/power/act8945a_charger.c b/drivers/power/act8945a_charger.c
> new file mode 100644
> index 0000000..fe9c19f
> --- /dev/null
> +++ b/drivers/power/act8945a_charger.c
> @@ -0,0 +1,386 @@
> +/*
> + * Power supply driver for the Active-semi ACT8945A PMIC
> + *
> + * Copyright (C) 2015 Atmel Corporation
> + *                   Wenyou Yang <wenyou.yang@atmel.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/mfd/act8945a.h>
> +#include <linux/of.h>
> +#include <linux/of_gpio.h>
> +#include <linux/platform_device.h>
> +#include <linux/power_supply.h>
> +#include <linux/regmap.h>
> +
> +static const char *act8945a_charger_model = "ACT8945A";
> +static const char *act8945a_charger_manufacturer = "Active-semi";
> +
> +/**
> + * ACT8945A Charger Register Map
> + */
> +
> +/* 0x70: Reserved */
> +#define ACT8945A_APCH_CFG              0x71
> +#define ACT8945A_APCH_STATUS           0x78
> +#define ACT8945A_APCH_CTRL             0x79
> +#define ACT8945A_APCH_STATE            0x7A
> +
> +/* ACT8945A_APCH_CFG */
> +#define APCH_CFG_OVPSET                (0x03 << 0)
> +#define                APCH_CFG_OVPSET_6V6     (0x0 << 0)
> +#define                APCH_CFG_OVPSET_7V      (0x1 << 0)
> +#define                APCH_CFG_OVPSET_7V5     (0x2 << 0)
> +#define                APCH_CFG_OVPSET_8V      (0x3 << 0)
> +#define APCH_CFG_PRETIMO       (0x03 << 2)
> +#define                APCH_CFG_PRETIMO_40_MIN         (0x0 << 2)
> +#define                APCH_CFG_PRETIMO_60_MIN         (0x1 << 2)
> +#define                APCH_CFG_PRETIMO_80_MIN         (0x2 << 2)
> +#define                APCH_CFG_PRETIMO_DISABLED       (0x3 << 2)
> +#define APCH_CFG_TOTTIMO       (0x03 << 4)
> +#define                APCH_CFG_TOTTIMO_3_HOUR         (0x0 << 4)
> +#define                APCH_CFG_TOTTIMO_4_HOUR         (0x1 << 4)
> +#define                APCH_CFG_TOTTIMO_5_HOUR         (0x2 << 4)
> +#define                APCH_CFG_TOTTIMO_DISABLED       (0x3 << 4)
> +#define APCH_CFG_SUSCHG                (0x01 << 7)
> +
> +#define APCH_STATUS_CHGDAT     (0x01 << 0)
> +#define APCH_STATUS_INDAT      (0x01 << 1)
> +#define APCH_STATUS_TEMPDAT    (0x01 << 2)
> +#define APCH_STATUS_TIMRDAT    (0x01 << 3)
> +#define APCH_STATUS_CHGSTAT    (0x01 << 4)
> +#define APCH_STATUS_INSTAT     (0x01 << 5)
> +#define APCH_STATUS_TEMPSTAT   (0x01 << 6)
> +#define APCH_STATUS_TIMRSTAT   (0x01 << 7)
> +
> +#define APCH_CTRL_CHGEOCOUT    (0x01 << 0)
> +#define APCH_CTRL_INDIS                (0x01 << 1)
> +#define APCH_CTRL_TEMPOUT      (0x01 << 2)
> +#define APCH_CTRL_TIMRPRE      (0x01 << 3)
> +#define APCH_CTRL_CHGEOCIN     (0x01 << 4)
> +#define APCH_CTRL_INCON                (0x01 << 5)
> +#define APCH_CTRL_TEMPIN       (0x01 << 6)
> +#define APCH_CTRL_TIMRTOT      (0x01 << 7)
> +
> +#define APCH_STATE_ACINSTAT    (0x01 << 1)
> +#define APCH_STATE_CSTATE      (0x03 << 4)
> +#define APCH_STATE_CSTATE_SHIFT                4
> +#define                APCH_STATE_CSTATE_DISABLED      0x00
> +#define                APCH_STATE_CSTATE_EOC           0x01
> +#define                APCH_STATE_CSTATE_FAST          0x02
> +#define                APCH_STATE_CSTATE_PRE           0x03
> +
> +struct act8945a_charger {
> +       struct device *dev;
> +       struct act8945a_dev *act8945a_dev;
> +       struct power_supply *psy;
> +
> +       u32 tolal_time_out;
> +       u32 pre_time_out;
> +       u32 input_voltage_threshold;
> +       bool battery_temperature;
> +       int chglev_pin;
> +       int chglev_value;
> +};
> +
> +static int act8945a_get_charger_state(struct regmap *regmap, int *val)
> +{
> +       int ret;
> +       unsigned int status, state;
> +
> +       ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status);
> +       if (ret < 0)
> +               return ret;
> +
> +       ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state);
> +       if (ret < 0)
> +               return ret;
> +
> +       state &= APCH_STATE_CSTATE;
> +       state >>= APCH_STATE_CSTATE_SHIFT;
> +
> +       if (state == APCH_STATE_CSTATE_EOC) {
> +               if (status & APCH_STATUS_CHGDAT)
> +                       *val = POWER_SUPPLY_STATUS_FULL;
> +               else
> +                       *val = POWER_SUPPLY_STATUS_NOT_CHARGING;
> +       } else if ((state == APCH_STATE_CSTATE_FAST) ||
> +                  (state == APCH_STATE_CSTATE_PRE)) {
> +               *val = POWER_SUPPLY_STATUS_CHARGING;
> +       } else {
> +               *val = POWER_SUPPLY_STATUS_NOT_CHARGING;
> +       }
> +
> +       return 0;
> +}
> +
> +static int act8945a_get_charge_type(struct regmap *regmap, int *val)
> +{
> +       int ret;
> +       unsigned int state;
> +
> +       ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state);
> +       if (ret < 0)
> +               return ret;
> +
> +       state &= APCH_STATE_CSTATE;
> +       state >>= APCH_STATE_CSTATE_SHIFT;
> +
> +       switch (state) {
> +       case APCH_STATE_CSTATE_PRE:
> +               *val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
> +               break;
> +       case APCH_STATE_CSTATE_FAST:
> +               *val = POWER_SUPPLY_CHARGE_TYPE_FAST;
> +               break;
> +       case APCH_STATE_CSTATE_EOC:
> +       case APCH_STATE_CSTATE_DISABLED:
> +       default:
> +               *val = POWER_SUPPLY_CHARGE_TYPE_NONE;
> +       }
> +
> +       return 0;
> +}
> +
> +static int act8945a_get_battery_health(struct act8945a_charger *charger,
> +                                     struct regmap *regmap, int *val)
> +{
> +       int ret;
> +       unsigned int status;
> +
> +       ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status);
> +       if (ret < 0)
> +               return ret;
> +
> +       if (charger->battery_temperature && !(status & APCH_STATUS_TEMPDAT))
> +               *val = POWER_SUPPLY_HEALTH_OVERHEAT;
> +       else if (!(status & APCH_STATUS_INDAT))
> +               *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
> +       else if (status & APCH_STATUS_TIMRDAT)
> +               *val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
> +       else
> +               *val = POWER_SUPPLY_HEALTH_GOOD;
> +
> +       return 0;
> +}
> +
> +static enum power_supply_property act8945a_charger_props[] = {
> +       POWER_SUPPLY_PROP_STATUS,
> +       POWER_SUPPLY_PROP_CHARGE_TYPE,
> +       POWER_SUPPLY_PROP_TECHNOLOGY,
> +       POWER_SUPPLY_PROP_HEALTH,
> +       POWER_SUPPLY_PROP_MODEL_NAME,
> +       POWER_SUPPLY_PROP_MANUFACTURER
> +};
> +
> +static int act8945a_charger_get_property(struct power_supply *psy,
> +                                   enum power_supply_property prop,
> +                                   union power_supply_propval *val)
> +{
> +       struct act8945a_charger *charger = power_supply_get_drvdata(psy);
> +       struct regmap *regmap = charger->act8945a_dev->regmap;
> +       int ret = 0;
> +
> +       switch (prop) {
> +       case POWER_SUPPLY_PROP_STATUS:
> +               ret = act8945a_get_charger_state(regmap, &val->intval);
> +               break;
> +       case POWER_SUPPLY_PROP_CHARGE_TYPE:
> +               ret = act8945a_get_charge_type(regmap, &val->intval);
> +               break;
> +       case POWER_SUPPLY_PROP_TECHNOLOGY:
> +               val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
> +               break;
> +       case POWER_SUPPLY_PROP_HEALTH:
> +               ret = act8945a_get_battery_health(charger,
> +                                                 regmap, &val->intval);
> +               break;
> +       case POWER_SUPPLY_PROP_MODEL_NAME:
> +               val->strval = act8945a_charger_model;
> +               break;
> +       case POWER_SUPPLY_PROP_MANUFACTURER:
> +               val->strval = act8945a_charger_manufacturer;
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       return ret;
> +}
> +
> +static const struct power_supply_desc act8945a_charger_desc = {
> +       .name           = "act8945a-charger",
> +       .type           = POWER_SUPPLY_TYPE_BATTERY,
> +       .get_property   = act8945a_charger_get_property,
> +       .properties     = act8945a_charger_props,
> +       .num_properties = ARRAY_SIZE(act8945a_charger_props),
> +};
> +
> +#define DEFAULT_TOTAL_TIME_OUT         3
> +#define DEFAULT_PRE_TIME_OUT           40
> +#define DEFAULT_INPUT_OVP_THRESHOLD    6600
> +
> +static int act8945a_charger_parse_dt(struct device *dev,
> +                                   struct act8945a_charger *charger)
> +{
> +       struct device_node *np = dev->of_node;
> +       enum of_gpio_flags flags;
> +
> +       if (!np) {
> +               dev_err(dev, "no charger of node\n");
> +               return -EINVAL;
> +       }
> +
> +       charger->chglev_pin = of_get_named_gpio_flags(np,
> +                               "active-semi,chglev-gpio", 0, &flags);
> +
> +       charger->chglev_value = (flags == OF_GPIO_ACTIVE_LOW) ? 0 : 1;
> +
> +       charger->battery_temperature = of_property_read_bool(np,
> +                                       "active-semi,battery_temperature");
> +
> +       if (of_property_read_u32(np, "active-semi,input_voltage_threshold",
> +                                &charger->input_voltage_threshold))
> +               charger->input_voltage_threshold = DEFAULT_PRE_TIME_OUT;
> +
> +       if (of_property_read_u32(np, "active-semi,precondition_timeout",
> +                                &charger->pre_time_out))
> +               charger->pre_time_out = DEFAULT_PRE_TIME_OUT;
> +
> +       if (of_property_read_u32(np, "active-semi,total_timeout",
> +                                &charger->tolal_time_out))
> +               charger->tolal_time_out = DEFAULT_TOTAL_TIME_OUT;
> +
> +       return 0;
> +}
> +
> +static int act8945a_charger_config(struct act8945a_charger *charger)
> +{
> +       struct regmap *regmap = charger->act8945a_dev->regmap;
> +       u8 value = 0;
> +
> +       if (gpio_is_valid(charger->chglev_pin))
> +               gpio_set_value(charger->chglev_pin, charger->chglev_value);
> +
> +       switch (charger->input_voltage_threshold) {
> +       case 8000:
> +               value |= APCH_CFG_OVPSET_8V;
> +               break;
> +       case 7500:
> +               value |= APCH_CFG_OVPSET_7V5;
> +               break;
> +       case 7000:
> +               value |= APCH_CFG_OVPSET_7V;
> +               break;
> +       case 6600:
> +       default:
> +               value |= APCH_CFG_OVPSET_6V6;
> +               break;
> +       }
> +
> +       switch (charger->pre_time_out) {
> +       case 60:
> +               value |= APCH_CFG_PRETIMO_60_MIN;
> +               break;
> +       case 80:
> +               value |= APCH_CFG_PRETIMO_80_MIN;
> +               break;
> +       case 0:
> +               value |= APCH_CFG_PRETIMO_DISABLED;
> +               break;
> +       case 40:
> +       default:
> +               value |= APCH_CFG_PRETIMO_40_MIN;
> +               break;
> +       }
> +
> +       switch (charger->tolal_time_out) {
> +       case 4:
> +               value |= APCH_CFG_TOTTIMO_4_HOUR;
> +               break;
> +       case 5:
> +               value |= APCH_CFG_TOTTIMO_5_HOUR;
> +               break;
> +       case 0:
> +               value |= APCH_CFG_TOTTIMO_DISABLED;
> +               break;
> +       case 3:
> +       default:
> +               value |= APCH_CFG_TOTTIMO_3_HOUR;
> +               break;
> +       }
> +
> +       return regmap_write(regmap, ACT8945A_APCH_CFG, value);
> +}
> +
> +static int act8945a_charger_probe(struct platform_device *pdev)
> +{
> +       struct act8945a_dev *act8945a_dev = dev_get_drvdata(pdev->dev.parent);
> +       struct act8945a_charger *charger;
> +       struct power_supply_config psy_cfg = {};
> +       int ret;
> +
> +       charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);
> +       if (!charger)
> +               return -ENOMEM;
> +
> +       platform_set_drvdata(pdev, charger);
> +
> +       charger->dev = &pdev->dev;
> +       charger->act8945a_dev = act8945a_dev;
> +
> +       ret = act8945a_charger_parse_dt(&pdev->dev, charger);
> +       if (ret)
> +               return ret;
> +
> +       ret = act8945a_charger_config(charger);
> +       if (ret)
> +               return ret;
> +
> +       psy_cfg.of_node = pdev->dev.of_node;
> +       psy_cfg.drv_data = charger;
> +
> +       charger->psy = devm_power_supply_register(&pdev->dev,
> +                                            &act8945a_charger_desc,
> +                                            &psy_cfg);
> +       if (IS_ERR(charger->psy)) {
> +               dev_err(act8945a_dev->dev, "failed to register power supply\n");
> +               return PTR_ERR(charger->psy);
> +       }
> +
> +       dev_info(act8945a_dev->dev, "charger driver registered\n");
> +
> +       return 0;
> +}
> +
> +static int act8945a_charger_remove(struct platform_device *pdev)
> +{
> +       struct act8945a_charger *charger = platform_get_drvdata(pdev);
> +
> +       power_supply_unregister(charger->psy);
>

The point of devm-like functions is to get rid of error and removal
paths. This won't work... actually this should oops. Have you tested
unbinding of driver?

Best regards,
Krzysztof
--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Wenyou Yang Jan. 12, 2016, 7:49 a.m. UTC | #2
SGkgS3J6eXN6dG9mLA0KDQo+IC0tLS0tT3JpZ2luYWwgTWVzc2FnZS0tLS0tDQo+IEZyb206IGsu
a296bG93c2tpLmtAZ21haWwuY29tIFttYWlsdG86ay5rb3psb3dza2kua0BnbWFpbC5jb21dIE9u
IEJlaGFsZiBPZg0KPiBLcnp5c3p0b2YgS296bG93c2tpDQo+IFNlbnQ6IDIwMTblubQx5pyIOOaX
pSAyMDoxNw0KPiBUbzogWWFuZywgV2VueW91IDxXZW55b3UuWWFuZ0BhdG1lbC5jb20+DQo+IENj
OiBTZWJhc3RpYW4gUmVpY2hlbCA8c3JlQGtlcm5lbC5vcmc+OyBEbWl0cnkgRXJlbWluLVNvbGVu
aWtvdg0KPiA8ZGJhcnlzaGtvdkBnbWFpbC5jb20+OyBEYXZpZCBXb29kaG91c2UgPGR3bXcyQGlu
ZnJhZGVhZC5vcmc+OyBSb2INCj4gSGVycmluZyA8cm9iaCtkdEBrZXJuZWwub3JnPjsgUGF3ZWwg
TW9sbCA8cGF3ZWwubW9sbEBhcm0uY29tPjsgTWFyaw0KPiBSdXRsYW5kIDxtYXJrLnJ1dGxhbmRA
YXJtLmNvbT47IElhbiBDYW1wYmVsbCA8aWpjK2RldmljZXRyZWVAaGVsbGlvbi5vcmcudWs+Ow0K
PiBLdW1hciBHYWxhIDxnYWxha0Bjb2RlYXVyb3JhLm9yZz47IEZlcnJlLCBOaWNvbGFzDQo+IDxO
aWNvbGFzLkZFUlJFQGF0bWVsLmNvbT47IGxpbnV4LWFybS1rZXJuZWxAbGlzdHMuaW5mcmFkZWFk
Lm9yZzsgbGludXgtDQo+IGtlcm5lbEB2Z2VyLmtlcm5lbC5vcmc7IGxpbnV4LXBtQHZnZXIua2Vy
bmVsLm9yZw0KPiBTdWJqZWN0OiBSZTogW1BBVENIIHYyIDEvMl0gcG93ZXI6IGFjdDg5NDVhOiBh
ZGQgY2hhcmdlciBkcml2ZXIgZm9yIEFDVDg5NDVBDQo+IA0KPiAyMDE2LTAxLTA4IDE3OjQ2IEdN
VCswOTowMCBXZW55b3UgWWFuZyA8d2VueW91LnlhbmdAYXRtZWwuY29tPjoNCj4gPiBUaGlzIHBh
dGNoIGFkZHMgbmV3IGRyaXZlciBmb3IgQWN0aXZlLXNlbWkgQUNUODk0NUEgQWN0aXZlUGF0aCBj
aGFyZ2VyDQo+ID4gKHBhcnQgb2YgQUNUODk0NUEgTUZEIGRyaXZlcikgcHJvdmlkaW5nIHBvd2Vy
IHN1cHBseSBjbGFzcyBpbmZvcm1hdGlvbg0KPiA+IHRvIHVzZXJzcGFjZS4NCj4gPg0KPiA+IFRo
ZSBkcml2ZXIgaXMgY29uZmlndXJlZCB0aHJvdWdoIERUUyAoYmF0dGVyeSBhbmQgc3lzdGVtIHJl
bGF0ZWQNCj4gPiBzZXR0aW5ncykgYW5kIHN5c2ZzIGVudHJpZXMgKHRpbWVycyBhbmQgaW5wdXQg
b3Zlci12b2x0YWdlIHRocmVzaG9sZCkuDQo+ID4NCj4gPiBTaWduZWQtb2ZmLWJ5OiBXZW55b3Ug
WWFuZyA8d2VueW91LnlhbmdAYXRtZWwuY29tPg0KPiA+IC0tLQ0KPiA+DQo+ID4gQ2hhbmdlcyBp
biB2MjoNCj4gPiAgMS4vIFN1YnN0aXR1dGUgb2ZfcHJvcGVydHlfcmVhZF9ib29sKCkgZm9yIG9m
X2dldF9wcm9wZXJ0eSgpLg0KPiA+ICAyLi8gU3Vic3RpdHV0ZSBkZXZtX3Bvd2VyX3N1cHBseV9y
ZWdpc3RlcigpIGZvciBwb3dlcl9zdXBwbHlfcmVnaXN0ZXIoKS4NCj4gPiAgMy4vIFVzZSBtb2R1
bGVfcGxhdGZvcm1fZHJpdmVyKCksIGluc3RlYWQgb2Ygc3Vic3lzX2luaXRjYWxsKCkuDQo+ID4g
IDQuLyBTdWJzdGl0dXRlIE1PRFVMRV9MSUNFTlNFKCJHUEwiKSBmb3IgTU9EVUxFX0xJQ0VOU0Uo
IkdQTCB2MiIpLg0KPiA+DQo+ID4gIGRyaXZlcnMvcG93ZXIvS2NvbmZpZyAgICAgICAgICAgIHwg
ICAgNyArDQo+ID4gIGRyaXZlcnMvcG93ZXIvTWFrZWZpbGUgICAgICAgICAgIHwgICAgMSArDQo+
ID4gIGRyaXZlcnMvcG93ZXIvYWN0ODk0NWFfY2hhcmdlci5jIHwgIDM4Ng0KPiA+ICsrKysrKysr
KysrKysrKysrKysrKysrKysrKysrKysrKysrKysrDQo+ID4gIDMgZmlsZXMgY2hhbmdlZCwgMzk0
IGluc2VydGlvbnMoKykNCj4gPiAgY3JlYXRlIG1vZGUgMTAwNjQ0IGRyaXZlcnMvcG93ZXIvYWN0
ODk0NWFfY2hhcmdlci5jDQo+ID4NCj4gPiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9wb3dlci9LY29u
ZmlnIGIvZHJpdmVycy9wb3dlci9LY29uZmlnIGluZGV4DQo+ID4gMWRkZDEzYy4uYWU3NTIxMSAx
MDA2NDQNCj4gPiAtLS0gYS9kcml2ZXJzL3Bvd2VyL0tjb25maWcNCj4gPiArKysgYi9kcml2ZXJz
L3Bvd2VyL0tjb25maWcNCj4gPiBAQCAtNzUsNiArNzUsMTMgQEAgY29uZmlnIEJBVFRFUllfODhQ
TTg2MFgNCj4gPiAgICAgICAgIGhlbHANCj4gPiAgICAgICAgICAgU2F5IFkgaGVyZSB0byBlbmFi
bGUgYmF0dGVyeSBtb25pdG9yIGZvciBNYXJ2ZWxsIDg4UE04NjB4IGNoaXAuDQo+ID4NCj4gPiAr
Y29uZmlnIEJBVFRFUllfQUNUODk0NUENCj4gPiArICAgICAgIHRyaXN0YXRlICJBY3RpdmUtc2Vt
aSBBQ1Q4OTQ1QSBjaGFyZ2VyIGRyaXZlciINCj4gPiArICAgICAgIGRlcGVuZHMgb24gTUZEX0FD
VDg5NDVBDQo+ID4gKyAgICAgICBoZWxwDQo+ID4gKyAgICAgICAgIFNheSBZIGhlcmUgdG8gZW5h
YmxlIHN1cHBvcnQgZm9yIHBvd2VyIHN1cHBseSBwcm92aWRlZCBieQ0KPiA+ICsgICAgICAgICBB
Y3RpdmUtc2VtaSBBY3RpdmVQYXRoIEFDVDg5NDVBIGNoYXJnZXIuDQo+ID4gKw0KPiA+ICBjb25m
aWcgQkFUVEVSWV9EUzI3NjANCj4gPiAgICAgICAgIHRyaXN0YXRlICJEUzI3NjAgYmF0dGVyeSBk
cml2ZXIgKEhQIGlQQVEgJiBvdGhlcnMpIg0KPiA+ICAgICAgICAgZGVwZW5kcyBvbiBXMSAmJiBX
MV9TTEFWRV9EUzI3NjAgZGlmZiAtLWdpdA0KPiA+IGEvZHJpdmVycy9wb3dlci9NYWtlZmlsZSBi
L2RyaXZlcnMvcG93ZXIvTWFrZWZpbGUgaW5kZXgNCj4gPiAwZTRlYWI1Li5lNDZiNzVkIDEwMDY0
NA0KPiA+IC0tLSBhL2RyaXZlcnMvcG93ZXIvTWFrZWZpbGUNCj4gPiArKysgYi9kcml2ZXJzL3Bv
d2VyL01ha2VmaWxlDQo+ID4gQEAgLTE3LDYgKzE3LDcgQEAgb2JqLSQoQ09ORklHX1dNODM1MF9Q
T1dFUikgICAgKz0NCj4gd204MzUwX3Bvd2VyLm8NCj4gPiAgb2JqLSQoQ09ORklHX1RFU1RfUE9X
RVIpICAgICAgICs9IHRlc3RfcG93ZXIubw0KPiA+DQo+ID4gIG9iai0kKENPTkZJR19CQVRURVJZ
Xzg4UE04NjBYKSArPSA4OHBtODYweF9iYXR0ZXJ5Lm8NCj4gPiArb2JqLSQoQ09ORklHX0JBVFRF
UllfQUNUODk0NUEpICs9IGFjdDg5NDVhX2NoYXJnZXIubw0KPiA+ICBvYmotJChDT05GSUdfQkFU
VEVSWV9EUzI3NjApICAgKz0gZHMyNzYwX2JhdHRlcnkubw0KPiA+ICBvYmotJChDT05GSUdfQkFU
VEVSWV9EUzI3ODApICAgKz0gZHMyNzgwX2JhdHRlcnkubw0KPiA+ICBvYmotJChDT05GSUdfQkFU
VEVSWV9EUzI3ODEpICAgKz0gZHMyNzgxX2JhdHRlcnkubw0KPiA+IGRpZmYgLS1naXQgYS9kcml2
ZXJzL3Bvd2VyL2FjdDg5NDVhX2NoYXJnZXIuYw0KPiA+IGIvZHJpdmVycy9wb3dlci9hY3Q4OTQ1
YV9jaGFyZ2VyLmMNCj4gPiBuZXcgZmlsZSBtb2RlIDEwMDY0NA0KPiA+IGluZGV4IDAwMDAwMDAu
LmZlOWMxOWYNCj4gPiAtLS0gL2Rldi9udWxsDQo+ID4gKysrIGIvZHJpdmVycy9wb3dlci9hY3Q4
OTQ1YV9jaGFyZ2VyLmMNCj4gPiBAQCAtMCwwICsxLDM4NiBAQA0KPiA+ICsvKg0KPiA+ICsgKiBQ
b3dlciBzdXBwbHkgZHJpdmVyIGZvciB0aGUgQWN0aXZlLXNlbWkgQUNUODk0NUEgUE1JQw0KPiA+
ICsgKg0KPiA+ICsgKiBDb3B5cmlnaHQgKEMpIDIwMTUgQXRtZWwgQ29ycG9yYXRpb24NCj4gPiAr
ICogICAgICAgICAgICAgICAgICAgV2VueW91IFlhbmcgPHdlbnlvdS55YW5nQGF0bWVsLmNvbT4N
Cj4gPiArICoNCj4gPiArICogVGhpcyBwcm9ncmFtIGlzIGZyZWUgc29mdHdhcmU7IHlvdSBjYW4g
cmVkaXN0cmlidXRlIGl0IGFuZC9vcg0KPiA+ICttb2RpZnkNCj4gPiArICogaXQgdW5kZXIgdGhl
IHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQNCj4g
PiArYnkNCj4gPiArICogdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbjsgZWl0aGVyIHZlcnNp
b24gMiBvZiB0aGUgTGljZW5zZSwgb3INCj4gPiArICogKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0
ZXIgdmVyc2lvbi4NCj4gPiArICoNCj4gPiArICogVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1dGVk
IGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsDQo+ID4gKyAqIGJ1dCBXSVRIT1VU
IEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mDQo+ID4g
KyAqIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4g
IFNlZQ0KPiB0aGUNCj4gPiArICogR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUg
ZGV0YWlscy4NCj4gPiArICovDQo+ID4gKw0KPiA+ICsjaW5jbHVkZSA8bGludXgvbW9kdWxlLmg+
DQo+ID4gKyNpbmNsdWRlIDxsaW51eC9tZmQvYWN0ODk0NWEuaD4NCj4gPiArI2luY2x1ZGUgPGxp
bnV4L29mLmg+DQo+ID4gKyNpbmNsdWRlIDxsaW51eC9vZl9ncGlvLmg+DQo+ID4gKyNpbmNsdWRl
IDxsaW51eC9wbGF0Zm9ybV9kZXZpY2UuaD4NCj4gPiArI2luY2x1ZGUgPGxpbnV4L3Bvd2VyX3N1
cHBseS5oPg0KPiA+ICsjaW5jbHVkZSA8bGludXgvcmVnbWFwLmg+DQo+ID4gKw0KPiA+ICtzdGF0
aWMgY29uc3QgY2hhciAqYWN0ODk0NWFfY2hhcmdlcl9tb2RlbCA9ICJBQ1Q4OTQ1QSI7IHN0YXRp
YyBjb25zdA0KPiA+ICtjaGFyICphY3Q4OTQ1YV9jaGFyZ2VyX21hbnVmYWN0dXJlciA9ICJBY3Rp
dmUtc2VtaSI7DQo+ID4gKw0KPiA+ICsvKioNCj4gPiArICogQUNUODk0NUEgQ2hhcmdlciBSZWdp
c3RlciBNYXANCj4gPiArICovDQo+ID4gKw0KPiA+ICsvKiAweDcwOiBSZXNlcnZlZCAqLw0KPiA+
ICsjZGVmaW5lIEFDVDg5NDVBX0FQQ0hfQ0ZHICAgICAgICAgICAgICAweDcxDQo+ID4gKyNkZWZp
bmUgQUNUODk0NUFfQVBDSF9TVEFUVVMgICAgICAgICAgIDB4NzgNCj4gPiArI2RlZmluZSBBQ1Q4
OTQ1QV9BUENIX0NUUkwgICAgICAgICAgICAgMHg3OQ0KPiA+ICsjZGVmaW5lIEFDVDg5NDVBX0FQ
Q0hfU1RBVEUgICAgICAgICAgICAweDdBDQo+ID4gKw0KPiA+ICsvKiBBQ1Q4OTQ1QV9BUENIX0NG
RyAqLw0KPiA+ICsjZGVmaW5lIEFQQ0hfQ0ZHX09WUFNFVCAgICAgICAgICAgICAgICAoMHgwMyA8
PCAwKQ0KPiA+ICsjZGVmaW5lICAgICAgICAgICAgICAgIEFQQ0hfQ0ZHX09WUFNFVF82VjYgICAg
ICgweDAgPDwgMCkNCj4gPiArI2RlZmluZSAgICAgICAgICAgICAgICBBUENIX0NGR19PVlBTRVRf
N1YgICAgICAoMHgxIDw8IDApDQo+ID4gKyNkZWZpbmUgICAgICAgICAgICAgICAgQVBDSF9DRkdf
T1ZQU0VUXzdWNSAgICAgKDB4MiA8PCAwKQ0KPiA+ICsjZGVmaW5lICAgICAgICAgICAgICAgIEFQ
Q0hfQ0ZHX09WUFNFVF84ViAgICAgICgweDMgPDwgMCkNCj4gPiArI2RlZmluZSBBUENIX0NGR19Q
UkVUSU1PICAgICAgICgweDAzIDw8IDIpDQo+ID4gKyNkZWZpbmUgICAgICAgICAgICAgICAgQVBD
SF9DRkdfUFJFVElNT180MF9NSU4gICAgICAgICAoMHgwIDw8IDIpDQo+ID4gKyNkZWZpbmUgICAg
ICAgICAgICAgICAgQVBDSF9DRkdfUFJFVElNT182MF9NSU4gICAgICAgICAoMHgxIDw8IDIpDQo+
ID4gKyNkZWZpbmUgICAgICAgICAgICAgICAgQVBDSF9DRkdfUFJFVElNT184MF9NSU4gICAgICAg
ICAoMHgyIDw8IDIpDQo+ID4gKyNkZWZpbmUgICAgICAgICAgICAgICAgQVBDSF9DRkdfUFJFVElN
T19ESVNBQkxFRCAgICAgICAoMHgzIDw8IDIpDQo+ID4gKyNkZWZpbmUgQVBDSF9DRkdfVE9UVElN
TyAgICAgICAoMHgwMyA8PCA0KQ0KPiA+ICsjZGVmaW5lICAgICAgICAgICAgICAgIEFQQ0hfQ0ZH
X1RPVFRJTU9fM19IT1VSICAgICAgICAgKDB4MCA8PCA0KQ0KPiA+ICsjZGVmaW5lICAgICAgICAg
ICAgICAgIEFQQ0hfQ0ZHX1RPVFRJTU9fNF9IT1VSICAgICAgICAgKDB4MSA8PCA0KQ0KPiA+ICsj
ZGVmaW5lICAgICAgICAgICAgICAgIEFQQ0hfQ0ZHX1RPVFRJTU9fNV9IT1VSICAgICAgICAgKDB4
MiA8PCA0KQ0KPiA+ICsjZGVmaW5lICAgICAgICAgICAgICAgIEFQQ0hfQ0ZHX1RPVFRJTU9fRElT
QUJMRUQgICAgICAgKDB4MyA8PCA0KQ0KPiA+ICsjZGVmaW5lIEFQQ0hfQ0ZHX1NVU0NIRyAgICAg
ICAgICAgICAgICAoMHgwMSA8PCA3KQ0KPiA+ICsNCj4gPiArI2RlZmluZSBBUENIX1NUQVRVU19D
SEdEQVQgICAgICgweDAxIDw8IDApDQo+ID4gKyNkZWZpbmUgQVBDSF9TVEFUVVNfSU5EQVQgICAg
ICAoMHgwMSA8PCAxKQ0KPiA+ICsjZGVmaW5lIEFQQ0hfU1RBVFVTX1RFTVBEQVQgICAgKDB4MDEg
PDwgMikNCj4gPiArI2RlZmluZSBBUENIX1NUQVRVU19USU1SREFUICAgICgweDAxIDw8IDMpDQo+
ID4gKyNkZWZpbmUgQVBDSF9TVEFUVVNfQ0hHU1RBVCAgICAoMHgwMSA8PCA0KQ0KPiA+ICsjZGVm
aW5lIEFQQ0hfU1RBVFVTX0lOU1RBVCAgICAgKDB4MDEgPDwgNSkNCj4gPiArI2RlZmluZSBBUENI
X1NUQVRVU19URU1QU1RBVCAgICgweDAxIDw8IDYpDQo+ID4gKyNkZWZpbmUgQVBDSF9TVEFUVVNf
VElNUlNUQVQgICAoMHgwMSA8PCA3KQ0KPiA+ICsNCj4gPiArI2RlZmluZSBBUENIX0NUUkxfQ0hH
RU9DT1VUICAgICgweDAxIDw8IDApDQo+ID4gKyNkZWZpbmUgQVBDSF9DVFJMX0lORElTICAgICAg
ICAgICAgICAgICgweDAxIDw8IDEpDQo+ID4gKyNkZWZpbmUgQVBDSF9DVFJMX1RFTVBPVVQgICAg
ICAoMHgwMSA8PCAyKQ0KPiA+ICsjZGVmaW5lIEFQQ0hfQ1RSTF9USU1SUFJFICAgICAgKDB4MDEg
PDwgMykNCj4gPiArI2RlZmluZSBBUENIX0NUUkxfQ0hHRU9DSU4gICAgICgweDAxIDw8IDQpDQo+
ID4gKyNkZWZpbmUgQVBDSF9DVFJMX0lOQ09OICAgICAgICAgICAgICAgICgweDAxIDw8IDUpDQo+
ID4gKyNkZWZpbmUgQVBDSF9DVFJMX1RFTVBJTiAgICAgICAoMHgwMSA8PCA2KQ0KPiA+ICsjZGVm
aW5lIEFQQ0hfQ1RSTF9USU1SVE9UICAgICAgKDB4MDEgPDwgNykNCj4gPiArDQo+ID4gKyNkZWZp
bmUgQVBDSF9TVEFURV9BQ0lOU1RBVCAgICAoMHgwMSA8PCAxKQ0KPiA+ICsjZGVmaW5lIEFQQ0hf
U1RBVEVfQ1NUQVRFICAgICAgKDB4MDMgPDwgNCkNCj4gPiArI2RlZmluZSBBUENIX1NUQVRFX0NT
VEFURV9TSElGVCAgICAgICAgICAgICAgICA0DQo+ID4gKyNkZWZpbmUgICAgICAgICAgICAgICAg
QVBDSF9TVEFURV9DU1RBVEVfRElTQUJMRUQgICAgICAweDAwDQo+ID4gKyNkZWZpbmUgICAgICAg
ICAgICAgICAgQVBDSF9TVEFURV9DU1RBVEVfRU9DICAgICAgICAgICAweDAxDQo+ID4gKyNkZWZp
bmUgICAgICAgICAgICAgICAgQVBDSF9TVEFURV9DU1RBVEVfRkFTVCAgICAgICAgICAweDAyDQo+
ID4gKyNkZWZpbmUgICAgICAgICAgICAgICAgQVBDSF9TVEFURV9DU1RBVEVfUFJFICAgICAgICAg
ICAweDAzDQo+ID4gKw0KPiA+ICtzdHJ1Y3QgYWN0ODk0NWFfY2hhcmdlciB7DQo+ID4gKyAgICAg
ICBzdHJ1Y3QgZGV2aWNlICpkZXY7DQo+ID4gKyAgICAgICBzdHJ1Y3QgYWN0ODk0NWFfZGV2ICph
Y3Q4OTQ1YV9kZXY7DQo+ID4gKyAgICAgICBzdHJ1Y3QgcG93ZXJfc3VwcGx5ICpwc3k7DQo+ID4g
Kw0KPiA+ICsgICAgICAgdTMyIHRvbGFsX3RpbWVfb3V0Ow0KPiA+ICsgICAgICAgdTMyIHByZV90
aW1lX291dDsNCj4gPiArICAgICAgIHUzMiBpbnB1dF92b2x0YWdlX3RocmVzaG9sZDsNCj4gPiAr
ICAgICAgIGJvb2wgYmF0dGVyeV90ZW1wZXJhdHVyZTsNCj4gPiArICAgICAgIGludCBjaGdsZXZf
cGluOw0KPiA+ICsgICAgICAgaW50IGNoZ2xldl92YWx1ZTsNCj4gPiArfTsNCj4gPiArDQo+ID4g
K3N0YXRpYyBpbnQgYWN0ODk0NWFfZ2V0X2NoYXJnZXJfc3RhdGUoc3RydWN0IHJlZ21hcCAqcmVn
bWFwLCBpbnQNCj4gPiArKnZhbCkgew0KPiA+ICsgICAgICAgaW50IHJldDsNCj4gPiArICAgICAg
IHVuc2lnbmVkIGludCBzdGF0dXMsIHN0YXRlOw0KPiA+ICsNCj4gPiArICAgICAgIHJldCA9IHJl
Z21hcF9yZWFkKHJlZ21hcCwgQUNUODk0NUFfQVBDSF9TVEFUVVMsICZzdGF0dXMpOw0KPiA+ICsg
ICAgICAgaWYgKHJldCA8IDApDQo+ID4gKyAgICAgICAgICAgICAgIHJldHVybiByZXQ7DQo+ID4g
Kw0KPiA+ICsgICAgICAgcmV0ID0gcmVnbWFwX3JlYWQocmVnbWFwLCBBQ1Q4OTQ1QV9BUENIX1NU
QVRFLCAmc3RhdGUpOw0KPiA+ICsgICAgICAgaWYgKHJldCA8IDApDQo+ID4gKyAgICAgICAgICAg
ICAgIHJldHVybiByZXQ7DQo+ID4gKw0KPiA+ICsgICAgICAgc3RhdGUgJj0gQVBDSF9TVEFURV9D
U1RBVEU7DQo+ID4gKyAgICAgICBzdGF0ZSA+Pj0gQVBDSF9TVEFURV9DU1RBVEVfU0hJRlQ7DQo+
ID4gKw0KPiA+ICsgICAgICAgaWYgKHN0YXRlID09IEFQQ0hfU1RBVEVfQ1NUQVRFX0VPQykgew0K
PiA+ICsgICAgICAgICAgICAgICBpZiAoc3RhdHVzICYgQVBDSF9TVEFUVVNfQ0hHREFUKQ0KPiA+
ICsgICAgICAgICAgICAgICAgICAgICAgICp2YWwgPSBQT1dFUl9TVVBQTFlfU1RBVFVTX0ZVTEw7
DQo+ID4gKyAgICAgICAgICAgICAgIGVsc2UNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICAq
dmFsID0gUE9XRVJfU1VQUExZX1NUQVRVU19OT1RfQ0hBUkdJTkc7DQo+ID4gKyAgICAgICB9IGVs
c2UgaWYgKChzdGF0ZSA9PSBBUENIX1NUQVRFX0NTVEFURV9GQVNUKSB8fA0KPiA+ICsgICAgICAg
ICAgICAgICAgICAoc3RhdGUgPT0gQVBDSF9TVEFURV9DU1RBVEVfUFJFKSkgew0KPiA+ICsgICAg
ICAgICAgICAgICAqdmFsID0gUE9XRVJfU1VQUExZX1NUQVRVU19DSEFSR0lORzsNCj4gPiArICAg
ICAgIH0gZWxzZSB7DQo+ID4gKyAgICAgICAgICAgICAgICp2YWwgPSBQT1dFUl9TVVBQTFlfU1RB
VFVTX05PVF9DSEFSR0lORzsNCj4gPiArICAgICAgIH0NCj4gPiArDQo+ID4gKyAgICAgICByZXR1
cm4gMDsNCj4gPiArfQ0KPiA+ICsNCj4gPiArc3RhdGljIGludCBhY3Q4OTQ1YV9nZXRfY2hhcmdl
X3R5cGUoc3RydWN0IHJlZ21hcCAqcmVnbWFwLCBpbnQgKnZhbCkNCj4gPiArew0KPiA+ICsgICAg
ICAgaW50IHJldDsNCj4gPiArICAgICAgIHVuc2lnbmVkIGludCBzdGF0ZTsNCj4gPiArDQo+ID4g
KyAgICAgICByZXQgPSByZWdtYXBfcmVhZChyZWdtYXAsIEFDVDg5NDVBX0FQQ0hfU1RBVEUsICZz
dGF0ZSk7DQo+ID4gKyAgICAgICBpZiAocmV0IDwgMCkNCj4gPiArICAgICAgICAgICAgICAgcmV0
dXJuIHJldDsNCj4gPiArDQo+ID4gKyAgICAgICBzdGF0ZSAmPSBBUENIX1NUQVRFX0NTVEFURTsN
Cj4gPiArICAgICAgIHN0YXRlID4+PSBBUENIX1NUQVRFX0NTVEFURV9TSElGVDsNCj4gPiArDQo+
ID4gKyAgICAgICBzd2l0Y2ggKHN0YXRlKSB7DQo+ID4gKyAgICAgICBjYXNlIEFQQ0hfU1RBVEVf
Q1NUQVRFX1BSRToNCj4gPiArICAgICAgICAgICAgICAgKnZhbCA9IFBPV0VSX1NVUFBMWV9DSEFS
R0VfVFlQRV9UUklDS0xFOw0KPiA+ICsgICAgICAgICAgICAgICBicmVhazsNCj4gPiArICAgICAg
IGNhc2UgQVBDSF9TVEFURV9DU1RBVEVfRkFTVDoNCj4gPiArICAgICAgICAgICAgICAgKnZhbCA9
IFBPV0VSX1NVUFBMWV9DSEFSR0VfVFlQRV9GQVNUOw0KPiA+ICsgICAgICAgICAgICAgICBicmVh
azsNCj4gPiArICAgICAgIGNhc2UgQVBDSF9TVEFURV9DU1RBVEVfRU9DOg0KPiA+ICsgICAgICAg
Y2FzZSBBUENIX1NUQVRFX0NTVEFURV9ESVNBQkxFRDoNCj4gPiArICAgICAgIGRlZmF1bHQ6DQo+
ID4gKyAgICAgICAgICAgICAgICp2YWwgPSBQT1dFUl9TVVBQTFlfQ0hBUkdFX1RZUEVfTk9ORTsN
Cj4gPiArICAgICAgIH0NCj4gPiArDQo+ID4gKyAgICAgICByZXR1cm4gMDsNCj4gPiArfQ0KPiA+
ICsNCj4gPiArc3RhdGljIGludCBhY3Q4OTQ1YV9nZXRfYmF0dGVyeV9oZWFsdGgoc3RydWN0IGFj
dDg5NDVhX2NoYXJnZXIgKmNoYXJnZXIsDQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICBzdHJ1Y3QgcmVnbWFwICpyZWdtYXAsIGludCAqdmFsKQ0KPiA+ICt7DQo+ID4g
KyAgICAgICBpbnQgcmV0Ow0KPiA+ICsgICAgICAgdW5zaWduZWQgaW50IHN0YXR1czsNCj4gPiAr
DQo+ID4gKyAgICAgICByZXQgPSByZWdtYXBfcmVhZChyZWdtYXAsIEFDVDg5NDVBX0FQQ0hfU1RB
VFVTLCAmc3RhdHVzKTsNCj4gPiArICAgICAgIGlmIChyZXQgPCAwKQ0KPiA+ICsgICAgICAgICAg
ICAgICByZXR1cm4gcmV0Ow0KPiA+ICsNCj4gPiArICAgICAgIGlmIChjaGFyZ2VyLT5iYXR0ZXJ5
X3RlbXBlcmF0dXJlICYmICEoc3RhdHVzICYNCj4gQVBDSF9TVEFUVVNfVEVNUERBVCkpDQo+ID4g
KyAgICAgICAgICAgICAgICp2YWwgPSBQT1dFUl9TVVBQTFlfSEVBTFRIX09WRVJIRUFUOw0KPiA+
ICsgICAgICAgZWxzZSBpZiAoIShzdGF0dXMgJiBBUENIX1NUQVRVU19JTkRBVCkpDQo+ID4gKyAg
ICAgICAgICAgICAgICp2YWwgPSBQT1dFUl9TVVBQTFlfSEVBTFRIX09WRVJWT0xUQUdFOw0KPiA+
ICsgICAgICAgZWxzZSBpZiAoc3RhdHVzICYgQVBDSF9TVEFUVVNfVElNUkRBVCkNCj4gPiArICAg
ICAgICAgICAgICAgKnZhbCA9IFBPV0VSX1NVUFBMWV9IRUFMVEhfU0FGRVRZX1RJTUVSX0VYUElS
RTsNCj4gPiArICAgICAgIGVsc2UNCj4gPiArICAgICAgICAgICAgICAgKnZhbCA9IFBPV0VSX1NV
UFBMWV9IRUFMVEhfR09PRDsNCj4gPiArDQo+ID4gKyAgICAgICByZXR1cm4gMDsNCj4gPiArfQ0K
PiA+ICsNCj4gPiArc3RhdGljIGVudW0gcG93ZXJfc3VwcGx5X3Byb3BlcnR5IGFjdDg5NDVhX2No
YXJnZXJfcHJvcHNbXSA9IHsNCj4gPiArICAgICAgIFBPV0VSX1NVUFBMWV9QUk9QX1NUQVRVUywN
Cj4gPiArICAgICAgIFBPV0VSX1NVUFBMWV9QUk9QX0NIQVJHRV9UWVBFLA0KPiA+ICsgICAgICAg
UE9XRVJfU1VQUExZX1BST1BfVEVDSE5PTE9HWSwNCj4gPiArICAgICAgIFBPV0VSX1NVUFBMWV9Q
Uk9QX0hFQUxUSCwNCj4gPiArICAgICAgIFBPV0VSX1NVUFBMWV9QUk9QX01PREVMX05BTUUsDQo+
ID4gKyAgICAgICBQT1dFUl9TVVBQTFlfUFJPUF9NQU5VRkFDVFVSRVIgfTsNCj4gPiArDQo+ID4g
K3N0YXRpYyBpbnQgYWN0ODk0NWFfY2hhcmdlcl9nZXRfcHJvcGVydHkoc3RydWN0IHBvd2VyX3N1
cHBseSAqcHN5LA0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVudW0g
cG93ZXJfc3VwcGx5X3Byb3BlcnR5IHByb3AsDQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgdW5pb24gcG93ZXJfc3VwcGx5X3Byb3B2YWwgKnZhbCkgew0KPiA+ICsgICAg
ICAgc3RydWN0IGFjdDg5NDVhX2NoYXJnZXIgKmNoYXJnZXIgPSBwb3dlcl9zdXBwbHlfZ2V0X2Ry
dmRhdGEocHN5KTsNCj4gPiArICAgICAgIHN0cnVjdCByZWdtYXAgKnJlZ21hcCA9IGNoYXJnZXIt
PmFjdDg5NDVhX2Rldi0+cmVnbWFwOw0KPiA+ICsgICAgICAgaW50IHJldCA9IDA7DQo+ID4gKw0K
PiA+ICsgICAgICAgc3dpdGNoIChwcm9wKSB7DQo+ID4gKyAgICAgICBjYXNlIFBPV0VSX1NVUFBM
WV9QUk9QX1NUQVRVUzoNCj4gPiArICAgICAgICAgICAgICAgcmV0ID0gYWN0ODk0NWFfZ2V0X2No
YXJnZXJfc3RhdGUocmVnbWFwLCAmdmFsLT5pbnR2YWwpOw0KPiA+ICsgICAgICAgICAgICAgICBi
cmVhazsNCj4gPiArICAgICAgIGNhc2UgUE9XRVJfU1VQUExZX1BST1BfQ0hBUkdFX1RZUEU6DQo+
ID4gKyAgICAgICAgICAgICAgIHJldCA9IGFjdDg5NDVhX2dldF9jaGFyZ2VfdHlwZShyZWdtYXAs
ICZ2YWwtPmludHZhbCk7DQo+ID4gKyAgICAgICAgICAgICAgIGJyZWFrOw0KPiA+ICsgICAgICAg
Y2FzZSBQT1dFUl9TVVBQTFlfUFJPUF9URUNITk9MT0dZOg0KPiA+ICsgICAgICAgICAgICAgICB2
YWwtPmludHZhbCA9IFBPV0VSX1NVUFBMWV9URUNITk9MT0dZX0xJT047DQo+ID4gKyAgICAgICAg
ICAgICAgIGJyZWFrOw0KPiA+ICsgICAgICAgY2FzZSBQT1dFUl9TVVBQTFlfUFJPUF9IRUFMVEg6
DQo+ID4gKyAgICAgICAgICAgICAgIHJldCA9IGFjdDg5NDVhX2dldF9iYXR0ZXJ5X2hlYWx0aChj
aGFyZ2VyLA0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgcmVnbWFwLCAmdmFsLT5pbnR2YWwpOw0KPiA+ICsgICAgICAgICAgICAgICBicmVhazsN
Cj4gPiArICAgICAgIGNhc2UgUE9XRVJfU1VQUExZX1BST1BfTU9ERUxfTkFNRToNCj4gPiArICAg
ICAgICAgICAgICAgdmFsLT5zdHJ2YWwgPSBhY3Q4OTQ1YV9jaGFyZ2VyX21vZGVsOw0KPiA+ICsg
ICAgICAgICAgICAgICBicmVhazsNCj4gPiArICAgICAgIGNhc2UgUE9XRVJfU1VQUExZX1BST1Bf
TUFOVUZBQ1RVUkVSOg0KPiA+ICsgICAgICAgICAgICAgICB2YWwtPnN0cnZhbCA9IGFjdDg5NDVh
X2NoYXJnZXJfbWFudWZhY3R1cmVyOw0KPiA+ICsgICAgICAgICAgICAgICBicmVhazsNCj4gPiAr
ICAgICAgIGRlZmF1bHQ6DQo+ID4gKyAgICAgICAgICAgICAgIHJldHVybiAtRUlOVkFMOw0KPiA+
ICsgICAgICAgfQ0KPiA+ICsNCj4gPiArICAgICAgIHJldHVybiByZXQ7DQo+ID4gK30NCj4gPiAr
DQo+ID4gK3N0YXRpYyBjb25zdCBzdHJ1Y3QgcG93ZXJfc3VwcGx5X2Rlc2MgYWN0ODk0NWFfY2hh
cmdlcl9kZXNjID0gew0KPiA+ICsgICAgICAgLm5hbWUgICAgICAgICAgID0gImFjdDg5NDVhLWNo
YXJnZXIiLA0KPiA+ICsgICAgICAgLnR5cGUgICAgICAgICAgID0gUE9XRVJfU1VQUExZX1RZUEVf
QkFUVEVSWSwNCj4gPiArICAgICAgIC5nZXRfcHJvcGVydHkgICA9IGFjdDg5NDVhX2NoYXJnZXJf
Z2V0X3Byb3BlcnR5LA0KPiA+ICsgICAgICAgLnByb3BlcnRpZXMgICAgID0gYWN0ODk0NWFfY2hh
cmdlcl9wcm9wcywNCj4gPiArICAgICAgIC5udW1fcHJvcGVydGllcyA9IEFSUkFZX1NJWkUoYWN0
ODk0NWFfY2hhcmdlcl9wcm9wcyksDQo+ID4gK307DQo+ID4gKw0KPiA+ICsjZGVmaW5lIERFRkFV
TFRfVE9UQUxfVElNRV9PVVQgICAgICAgICAzDQo+ID4gKyNkZWZpbmUgREVGQVVMVF9QUkVfVElN
RV9PVVQgICAgICAgICAgIDQwDQo+ID4gKyNkZWZpbmUgREVGQVVMVF9JTlBVVF9PVlBfVEhSRVNI
T0xEICAgIDY2MDANCj4gPiArDQo+ID4gK3N0YXRpYyBpbnQgYWN0ODk0NWFfY2hhcmdlcl9wYXJz
ZV9kdChzdHJ1Y3QgZGV2aWNlICpkZXYsDQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgc3RydWN0IGFjdDg5NDVhX2NoYXJnZXIgKmNoYXJnZXIpDQo+ID4gK3sNCj4gPiAr
ICAgICAgIHN0cnVjdCBkZXZpY2Vfbm9kZSAqbnAgPSBkZXYtPm9mX25vZGU7DQo+ID4gKyAgICAg
ICBlbnVtIG9mX2dwaW9fZmxhZ3MgZmxhZ3M7DQo+ID4gKw0KPiA+ICsgICAgICAgaWYgKCFucCkg
ew0KPiA+ICsgICAgICAgICAgICAgICBkZXZfZXJyKGRldiwgIm5vIGNoYXJnZXIgb2Ygbm9kZVxu
Iik7DQo+ID4gKyAgICAgICAgICAgICAgIHJldHVybiAtRUlOVkFMOw0KPiA+ICsgICAgICAgfQ0K
PiA+ICsNCj4gPiArICAgICAgIGNoYXJnZXItPmNoZ2xldl9waW4gPSBvZl9nZXRfbmFtZWRfZ3Bp
b19mbGFncyhucCwNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhY3RpdmUt
c2VtaSxjaGdsZXYtZ3BpbyIsIDAsICZmbGFncyk7DQo+ID4gKw0KPiA+ICsgICAgICAgY2hhcmdl
ci0+Y2hnbGV2X3ZhbHVlID0gKGZsYWdzID09IE9GX0dQSU9fQUNUSVZFX0xPVykgPyAwIDogMTsN
Cj4gPiArDQo+ID4gKyAgICAgICBjaGFyZ2VyLT5iYXR0ZXJ5X3RlbXBlcmF0dXJlID0gb2ZfcHJv
cGVydHlfcmVhZF9ib29sKG5wLA0KPiA+ICsNCj4gPiArICJhY3RpdmUtc2VtaSxiYXR0ZXJ5X3Rl
bXBlcmF0dXJlIik7DQo+ID4gKw0KPiA+ICsgICAgICAgaWYgKG9mX3Byb3BlcnR5X3JlYWRfdTMy
KG5wLCAiYWN0aXZlLXNlbWksaW5wdXRfdm9sdGFnZV90aHJlc2hvbGQiLA0KPiA+ICsgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICZjaGFyZ2VyLT5pbnB1dF92b2x0YWdlX3RocmVzaG9s
ZCkpDQo+ID4gKyAgICAgICAgICAgICAgIGNoYXJnZXItPmlucHV0X3ZvbHRhZ2VfdGhyZXNob2xk
ID0NCj4gPiArIERFRkFVTFRfUFJFX1RJTUVfT1VUOw0KPiA+ICsNCj4gPiArICAgICAgIGlmIChv
Zl9wcm9wZXJ0eV9yZWFkX3UzMihucCwgImFjdGl2ZS1zZW1pLHByZWNvbmRpdGlvbl90aW1lb3V0
IiwNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAmY2hhcmdlci0+cHJlX3Rp
bWVfb3V0KSkNCj4gPiArICAgICAgICAgICAgICAgY2hhcmdlci0+cHJlX3RpbWVfb3V0ID0gREVG
QVVMVF9QUkVfVElNRV9PVVQ7DQo+ID4gKw0KPiA+ICsgICAgICAgaWYgKG9mX3Byb3BlcnR5X3Jl
YWRfdTMyKG5wLCAiYWN0aXZlLXNlbWksdG90YWxfdGltZW91dCIsDQo+ID4gKyAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgJmNoYXJnZXItPnRvbGFsX3RpbWVfb3V0KSkNCj4gPiArICAg
ICAgICAgICAgICAgY2hhcmdlci0+dG9sYWxfdGltZV9vdXQgPSBERUZBVUxUX1RPVEFMX1RJTUVf
T1VUOw0KPiA+ICsNCj4gPiArICAgICAgIHJldHVybiAwOw0KPiA+ICt9DQo+ID4gKw0KPiA+ICtz
dGF0aWMgaW50IGFjdDg5NDVhX2NoYXJnZXJfY29uZmlnKHN0cnVjdCBhY3Q4OTQ1YV9jaGFyZ2Vy
ICpjaGFyZ2VyKQ0KPiA+ICt7DQo+ID4gKyAgICAgICBzdHJ1Y3QgcmVnbWFwICpyZWdtYXAgPSBj
aGFyZ2VyLT5hY3Q4OTQ1YV9kZXYtPnJlZ21hcDsNCj4gPiArICAgICAgIHU4IHZhbHVlID0gMDsN
Cj4gPiArDQo+ID4gKyAgICAgICBpZiAoZ3Bpb19pc192YWxpZChjaGFyZ2VyLT5jaGdsZXZfcGlu
KSkNCj4gPiArICAgICAgICAgICAgICAgZ3Bpb19zZXRfdmFsdWUoY2hhcmdlci0+Y2hnbGV2X3Bp
biwNCj4gPiArIGNoYXJnZXItPmNoZ2xldl92YWx1ZSk7DQo+ID4gKw0KPiA+ICsgICAgICAgc3dp
dGNoIChjaGFyZ2VyLT5pbnB1dF92b2x0YWdlX3RocmVzaG9sZCkgew0KPiA+ICsgICAgICAgY2Fz
ZSA4MDAwOg0KPiA+ICsgICAgICAgICAgICAgICB2YWx1ZSB8PSBBUENIX0NGR19PVlBTRVRfOFY7
DQo+ID4gKyAgICAgICAgICAgICAgIGJyZWFrOw0KPiA+ICsgICAgICAgY2FzZSA3NTAwOg0KPiA+
ICsgICAgICAgICAgICAgICB2YWx1ZSB8PSBBUENIX0NGR19PVlBTRVRfN1Y1Ow0KPiA+ICsgICAg
ICAgICAgICAgICBicmVhazsNCj4gPiArICAgICAgIGNhc2UgNzAwMDoNCj4gPiArICAgICAgICAg
ICAgICAgdmFsdWUgfD0gQVBDSF9DRkdfT1ZQU0VUXzdWOw0KPiA+ICsgICAgICAgICAgICAgICBi
cmVhazsNCj4gPiArICAgICAgIGNhc2UgNjYwMDoNCj4gPiArICAgICAgIGRlZmF1bHQ6DQo+ID4g
KyAgICAgICAgICAgICAgIHZhbHVlIHw9IEFQQ0hfQ0ZHX09WUFNFVF82VjY7DQo+ID4gKyAgICAg
ICAgICAgICAgIGJyZWFrOw0KPiA+ICsgICAgICAgfQ0KPiA+ICsNCj4gPiArICAgICAgIHN3aXRj
aCAoY2hhcmdlci0+cHJlX3RpbWVfb3V0KSB7DQo+ID4gKyAgICAgICBjYXNlIDYwOg0KPiA+ICsg
ICAgICAgICAgICAgICB2YWx1ZSB8PSBBUENIX0NGR19QUkVUSU1PXzYwX01JTjsNCj4gPiArICAg
ICAgICAgICAgICAgYnJlYWs7DQo+ID4gKyAgICAgICBjYXNlIDgwOg0KPiA+ICsgICAgICAgICAg
ICAgICB2YWx1ZSB8PSBBUENIX0NGR19QUkVUSU1PXzgwX01JTjsNCj4gPiArICAgICAgICAgICAg
ICAgYnJlYWs7DQo+ID4gKyAgICAgICBjYXNlIDA6DQo+ID4gKyAgICAgICAgICAgICAgIHZhbHVl
IHw9IEFQQ0hfQ0ZHX1BSRVRJTU9fRElTQUJMRUQ7DQo+ID4gKyAgICAgICAgICAgICAgIGJyZWFr
Ow0KPiA+ICsgICAgICAgY2FzZSA0MDoNCj4gPiArICAgICAgIGRlZmF1bHQ6DQo+ID4gKyAgICAg
ICAgICAgICAgIHZhbHVlIHw9IEFQQ0hfQ0ZHX1BSRVRJTU9fNDBfTUlOOw0KPiA+ICsgICAgICAg
ICAgICAgICBicmVhazsNCj4gPiArICAgICAgIH0NCj4gPiArDQo+ID4gKyAgICAgICBzd2l0Y2gg
KGNoYXJnZXItPnRvbGFsX3RpbWVfb3V0KSB7DQo+ID4gKyAgICAgICBjYXNlIDQ6DQo+ID4gKyAg
ICAgICAgICAgICAgIHZhbHVlIHw9IEFQQ0hfQ0ZHX1RPVFRJTU9fNF9IT1VSOw0KPiA+ICsgICAg
ICAgICAgICAgICBicmVhazsNCj4gPiArICAgICAgIGNhc2UgNToNCj4gPiArICAgICAgICAgICAg
ICAgdmFsdWUgfD0gQVBDSF9DRkdfVE9UVElNT181X0hPVVI7DQo+ID4gKyAgICAgICAgICAgICAg
IGJyZWFrOw0KPiA+ICsgICAgICAgY2FzZSAwOg0KPiA+ICsgICAgICAgICAgICAgICB2YWx1ZSB8
PSBBUENIX0NGR19UT1RUSU1PX0RJU0FCTEVEOw0KPiA+ICsgICAgICAgICAgICAgICBicmVhazsN
Cj4gPiArICAgICAgIGNhc2UgMzoNCj4gPiArICAgICAgIGRlZmF1bHQ6DQo+ID4gKyAgICAgICAg
ICAgICAgIHZhbHVlIHw9IEFQQ0hfQ0ZHX1RPVFRJTU9fM19IT1VSOw0KPiA+ICsgICAgICAgICAg
ICAgICBicmVhazsNCj4gPiArICAgICAgIH0NCj4gPiArDQo+ID4gKyAgICAgICByZXR1cm4gcmVn
bWFwX3dyaXRlKHJlZ21hcCwgQUNUODk0NUFfQVBDSF9DRkcsIHZhbHVlKTsgfQ0KPiA+ICsNCj4g
PiArc3RhdGljIGludCBhY3Q4OTQ1YV9jaGFyZ2VyX3Byb2JlKHN0cnVjdCBwbGF0Zm9ybV9kZXZp
Y2UgKnBkZXYpIHsNCj4gPiArICAgICAgIHN0cnVjdCBhY3Q4OTQ1YV9kZXYgKmFjdDg5NDVhX2Rl
diA9IGRldl9nZXRfZHJ2ZGF0YShwZGV2LT5kZXYucGFyZW50KTsNCj4gPiArICAgICAgIHN0cnVj
dCBhY3Q4OTQ1YV9jaGFyZ2VyICpjaGFyZ2VyOw0KPiA+ICsgICAgICAgc3RydWN0IHBvd2VyX3N1
cHBseV9jb25maWcgcHN5X2NmZyA9IHt9Ow0KPiA+ICsgICAgICAgaW50IHJldDsNCj4gPiArDQo+
ID4gKyAgICAgICBjaGFyZ2VyID0gZGV2bV9remFsbG9jKCZwZGV2LT5kZXYsIHNpemVvZigqY2hh
cmdlciksIEdGUF9LRVJORUwpOw0KPiA+ICsgICAgICAgaWYgKCFjaGFyZ2VyKQ0KPiA+ICsgICAg
ICAgICAgICAgICByZXR1cm4gLUVOT01FTTsNCj4gPiArDQo+ID4gKyAgICAgICBwbGF0Zm9ybV9z
ZXRfZHJ2ZGF0YShwZGV2LCBjaGFyZ2VyKTsNCj4gPiArDQo+ID4gKyAgICAgICBjaGFyZ2VyLT5k
ZXYgPSAmcGRldi0+ZGV2Ow0KPiA+ICsgICAgICAgY2hhcmdlci0+YWN0ODk0NWFfZGV2ID0gYWN0
ODk0NWFfZGV2Ow0KPiA+ICsNCj4gPiArICAgICAgIHJldCA9IGFjdDg5NDVhX2NoYXJnZXJfcGFy
c2VfZHQoJnBkZXYtPmRldiwgY2hhcmdlcik7DQo+ID4gKyAgICAgICBpZiAocmV0KQ0KPiA+ICsg
ICAgICAgICAgICAgICByZXR1cm4gcmV0Ow0KPiA+ICsNCj4gPiArICAgICAgIHJldCA9IGFjdDg5
NDVhX2NoYXJnZXJfY29uZmlnKGNoYXJnZXIpOw0KPiA+ICsgICAgICAgaWYgKHJldCkNCj4gPiAr
ICAgICAgICAgICAgICAgcmV0dXJuIHJldDsNCj4gPiArDQo+ID4gKyAgICAgICBwc3lfY2ZnLm9m
X25vZGUgPSBwZGV2LT5kZXYub2Zfbm9kZTsNCj4gPiArICAgICAgIHBzeV9jZmcuZHJ2X2RhdGEg
PSBjaGFyZ2VyOw0KPiA+ICsNCj4gPiArICAgICAgIGNoYXJnZXItPnBzeSA9IGRldm1fcG93ZXJf
c3VwcGx5X3JlZ2lzdGVyKCZwZGV2LT5kZXYsDQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgJmFjdDg5NDVhX2NoYXJnZXJfZGVzYywNCj4gPiArICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAmcHN5X2NmZyk7DQo+ID4gKyAg
ICAgICBpZiAoSVNfRVJSKGNoYXJnZXItPnBzeSkpIHsNCj4gPiArICAgICAgICAgICAgICAgZGV2
X2VycihhY3Q4OTQ1YV9kZXYtPmRldiwgImZhaWxlZCB0byByZWdpc3RlciBwb3dlciBzdXBwbHlc
biIpOw0KPiA+ICsgICAgICAgICAgICAgICByZXR1cm4gUFRSX0VSUihjaGFyZ2VyLT5wc3kpOw0K
PiA+ICsgICAgICAgfQ0KPiA+ICsNCj4gPiArICAgICAgIGRldl9pbmZvKGFjdDg5NDVhX2Rldi0+
ZGV2LCAiY2hhcmdlciBkcml2ZXIgcmVnaXN0ZXJlZFxuIik7DQo+ID4gKw0KPiA+ICsgICAgICAg
cmV0dXJuIDA7DQo+ID4gK30NCj4gPiArDQo+ID4gK3N0YXRpYyBpbnQgYWN0ODk0NWFfY2hhcmdl
cl9yZW1vdmUoc3RydWN0IHBsYXRmb3JtX2RldmljZSAqcGRldikgew0KPiA+ICsgICAgICAgc3Ry
dWN0IGFjdDg5NDVhX2NoYXJnZXIgKmNoYXJnZXIgPSBwbGF0Zm9ybV9nZXRfZHJ2ZGF0YShwZGV2
KTsNCj4gPiArDQo+ID4gKyAgICAgICBwb3dlcl9zdXBwbHlfdW5yZWdpc3RlcihjaGFyZ2VyLT5w
c3kpOw0KPiA+DQo+IA0KPiBUaGUgcG9pbnQgb2YgZGV2bS1saWtlIGZ1bmN0aW9ucyBpcyB0byBn
ZXQgcmlkIG9mIGVycm9yIGFuZCByZW1vdmFsIHBhdGhzLiBUaGlzDQo+IHdvbid0IHdvcmsuLi4g
YWN0dWFsbHkgdGhpcyBzaG91bGQgb29wcy4gSGF2ZSB5b3UgdGVzdGVkIHVuYmluZGluZyBvZiBk
cml2ZXI/DQoNClNvcnJ5IGFib3V0IGl0Lg0KDQpJIGZvcmdvdCBkbyB0aGlzIHRlc3QgZm9yIHYy
Lg0KDQpJIHdpbGwgcmVtb3ZlIGl0IGluIHRoZSBuZXh0IHZlcmlvbi4NCg0KPiANCj4gQmVzdCBy
ZWdhcmRzLA0KPiBLcnp5c3p0b2YNCg0KDQpCZXN0IFJlZ2FyZHMsDQpXZW55b3UgWWFuZw0K
--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 1ddd13c..ae75211 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -75,6 +75,13 @@  config BATTERY_88PM860X
 	help
 	  Say Y here to enable battery monitor for Marvell 88PM860x chip.
 
+config BATTERY_ACT8945A
+	tristate "Active-semi ACT8945A charger driver"
+	depends on MFD_ACT8945A
+	help
+	  Say Y here to enable support for power supply provided by
+	  Active-semi ActivePath ACT8945A charger.
+
 config BATTERY_DS2760
 	tristate "DS2760 battery driver (HP iPAQ & others)"
 	depends on W1 && W1_SLAVE_DS2760
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 0e4eab5..e46b75d 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -17,6 +17,7 @@  obj-$(CONFIG_WM8350_POWER)	+= wm8350_power.o
 obj-$(CONFIG_TEST_POWER)	+= test_power.o
 
 obj-$(CONFIG_BATTERY_88PM860X)	+= 88pm860x_battery.o
+obj-$(CONFIG_BATTERY_ACT8945A)	+= act8945a_charger.o
 obj-$(CONFIG_BATTERY_DS2760)	+= ds2760_battery.o
 obj-$(CONFIG_BATTERY_DS2780)	+= ds2780_battery.o
 obj-$(CONFIG_BATTERY_DS2781)	+= ds2781_battery.o
diff --git a/drivers/power/act8945a_charger.c b/drivers/power/act8945a_charger.c
new file mode 100644
index 0000000..fe9c19f
--- /dev/null
+++ b/drivers/power/act8945a_charger.c
@@ -0,0 +1,386 @@ 
+/*
+ * Power supply driver for the Active-semi ACT8945A PMIC
+ *
+ * Copyright (C) 2015 Atmel Corporation
+ *		      Wenyou Yang <wenyou.yang@atmel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/mfd/act8945a.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+
+static const char *act8945a_charger_model = "ACT8945A";
+static const char *act8945a_charger_manufacturer = "Active-semi";
+
+/**
+ * ACT8945A Charger Register Map
+ */
+
+/* 0x70: Reserved */
+#define ACT8945A_APCH_CFG		0x71
+#define ACT8945A_APCH_STATUS		0x78
+#define ACT8945A_APCH_CTRL		0x79
+#define ACT8945A_APCH_STATE		0x7A
+
+/* ACT8945A_APCH_CFG */
+#define APCH_CFG_OVPSET		(0x03 << 0)
+#define		APCH_CFG_OVPSET_6V6	(0x0 << 0)
+#define		APCH_CFG_OVPSET_7V	(0x1 << 0)
+#define		APCH_CFG_OVPSET_7V5	(0x2 << 0)
+#define		APCH_CFG_OVPSET_8V	(0x3 << 0)
+#define APCH_CFG_PRETIMO	(0x03 << 2)
+#define		APCH_CFG_PRETIMO_40_MIN		(0x0 << 2)
+#define		APCH_CFG_PRETIMO_60_MIN		(0x1 << 2)
+#define		APCH_CFG_PRETIMO_80_MIN		(0x2 << 2)
+#define		APCH_CFG_PRETIMO_DISABLED	(0x3 << 2)
+#define APCH_CFG_TOTTIMO	(0x03 << 4)
+#define		APCH_CFG_TOTTIMO_3_HOUR		(0x0 << 4)
+#define		APCH_CFG_TOTTIMO_4_HOUR		(0x1 << 4)
+#define		APCH_CFG_TOTTIMO_5_HOUR		(0x2 << 4)
+#define		APCH_CFG_TOTTIMO_DISABLED	(0x3 << 4)
+#define APCH_CFG_SUSCHG		(0x01 << 7)
+
+#define APCH_STATUS_CHGDAT	(0x01 << 0)
+#define APCH_STATUS_INDAT	(0x01 << 1)
+#define APCH_STATUS_TEMPDAT	(0x01 << 2)
+#define APCH_STATUS_TIMRDAT	(0x01 << 3)
+#define APCH_STATUS_CHGSTAT	(0x01 << 4)
+#define APCH_STATUS_INSTAT	(0x01 << 5)
+#define APCH_STATUS_TEMPSTAT	(0x01 << 6)
+#define APCH_STATUS_TIMRSTAT	(0x01 << 7)
+
+#define APCH_CTRL_CHGEOCOUT	(0x01 << 0)
+#define APCH_CTRL_INDIS		(0x01 << 1)
+#define APCH_CTRL_TEMPOUT	(0x01 << 2)
+#define APCH_CTRL_TIMRPRE	(0x01 << 3)
+#define APCH_CTRL_CHGEOCIN	(0x01 << 4)
+#define APCH_CTRL_INCON		(0x01 << 5)
+#define APCH_CTRL_TEMPIN	(0x01 << 6)
+#define APCH_CTRL_TIMRTOT	(0x01 << 7)
+
+#define APCH_STATE_ACINSTAT	(0x01 << 1)
+#define APCH_STATE_CSTATE	(0x03 << 4)
+#define APCH_STATE_CSTATE_SHIFT		4
+#define		APCH_STATE_CSTATE_DISABLED	0x00
+#define		APCH_STATE_CSTATE_EOC		0x01
+#define		APCH_STATE_CSTATE_FAST		0x02
+#define		APCH_STATE_CSTATE_PRE		0x03
+
+struct act8945a_charger {
+	struct device *dev;
+	struct act8945a_dev *act8945a_dev;
+	struct power_supply *psy;
+
+	u32 tolal_time_out;
+	u32 pre_time_out;
+	u32 input_voltage_threshold;
+	bool battery_temperature;
+	int chglev_pin;
+	int chglev_value;
+};
+
+static int act8945a_get_charger_state(struct regmap *regmap, int *val)
+{
+	int ret;
+	unsigned int status, state;
+
+	ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status);
+	if (ret < 0)
+		return ret;
+
+	ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state);
+	if (ret < 0)
+		return ret;
+
+	state &= APCH_STATE_CSTATE;
+	state >>= APCH_STATE_CSTATE_SHIFT;
+
+	if (state == APCH_STATE_CSTATE_EOC) {
+		if (status & APCH_STATUS_CHGDAT)
+			*val = POWER_SUPPLY_STATUS_FULL;
+		else
+			*val = POWER_SUPPLY_STATUS_NOT_CHARGING;
+	} else if ((state == APCH_STATE_CSTATE_FAST) ||
+		   (state == APCH_STATE_CSTATE_PRE)) {
+		*val = POWER_SUPPLY_STATUS_CHARGING;
+	} else {
+		*val = POWER_SUPPLY_STATUS_NOT_CHARGING;
+	}
+
+	return 0;
+}
+
+static int act8945a_get_charge_type(struct regmap *regmap, int *val)
+{
+	int ret;
+	unsigned int state;
+
+	ret = regmap_read(regmap, ACT8945A_APCH_STATE, &state);
+	if (ret < 0)
+		return ret;
+
+	state &= APCH_STATE_CSTATE;
+	state >>= APCH_STATE_CSTATE_SHIFT;
+
+	switch (state) {
+	case APCH_STATE_CSTATE_PRE:
+		*val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+		break;
+	case APCH_STATE_CSTATE_FAST:
+		*val = POWER_SUPPLY_CHARGE_TYPE_FAST;
+		break;
+	case APCH_STATE_CSTATE_EOC:
+	case APCH_STATE_CSTATE_DISABLED:
+	default:
+		*val = POWER_SUPPLY_CHARGE_TYPE_NONE;
+	}
+
+	return 0;
+}
+
+static int act8945a_get_battery_health(struct act8945a_charger *charger,
+				      struct regmap *regmap, int *val)
+{
+	int ret;
+	unsigned int status;
+
+	ret = regmap_read(regmap, ACT8945A_APCH_STATUS, &status);
+	if (ret < 0)
+		return ret;
+
+	if (charger->battery_temperature && !(status & APCH_STATUS_TEMPDAT))
+		*val = POWER_SUPPLY_HEALTH_OVERHEAT;
+	else if (!(status & APCH_STATUS_INDAT))
+		*val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+	else if (status & APCH_STATUS_TIMRDAT)
+		*val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
+	else
+		*val = POWER_SUPPLY_HEALTH_GOOD;
+
+	return 0;
+}
+
+static enum power_supply_property act8945a_charger_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_CHARGE_TYPE,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_MODEL_NAME,
+	POWER_SUPPLY_PROP_MANUFACTURER
+};
+
+static int act8945a_charger_get_property(struct power_supply *psy,
+				    enum power_supply_property prop,
+				    union power_supply_propval *val)
+{
+	struct act8945a_charger *charger = power_supply_get_drvdata(psy);
+	struct regmap *regmap = charger->act8945a_dev->regmap;
+	int ret = 0;
+
+	switch (prop) {
+	case POWER_SUPPLY_PROP_STATUS:
+		ret = act8945a_get_charger_state(regmap, &val->intval);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		ret = act8945a_get_charge_type(regmap, &val->intval);
+		break;
+	case POWER_SUPPLY_PROP_TECHNOLOGY:
+		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+		break;
+	case POWER_SUPPLY_PROP_HEALTH:
+		ret = act8945a_get_battery_health(charger,
+						  regmap, &val->intval);
+		break;
+	case POWER_SUPPLY_PROP_MODEL_NAME:
+		val->strval = act8945a_charger_model;
+		break;
+	case POWER_SUPPLY_PROP_MANUFACTURER:
+		val->strval = act8945a_charger_manufacturer;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static const struct power_supply_desc act8945a_charger_desc = {
+	.name		= "act8945a-charger",
+	.type		= POWER_SUPPLY_TYPE_BATTERY,
+	.get_property	= act8945a_charger_get_property,
+	.properties	= act8945a_charger_props,
+	.num_properties	= ARRAY_SIZE(act8945a_charger_props),
+};
+
+#define DEFAULT_TOTAL_TIME_OUT		3
+#define DEFAULT_PRE_TIME_OUT		40
+#define DEFAULT_INPUT_OVP_THRESHOLD	6600
+
+static int act8945a_charger_parse_dt(struct device *dev,
+				    struct act8945a_charger *charger)
+{
+	struct device_node *np = dev->of_node;
+	enum of_gpio_flags flags;
+
+	if (!np) {
+		dev_err(dev, "no charger of node\n");
+		return -EINVAL;
+	}
+
+	charger->chglev_pin = of_get_named_gpio_flags(np,
+				"active-semi,chglev-gpio", 0, &flags);
+
+	charger->chglev_value = (flags == OF_GPIO_ACTIVE_LOW) ? 0 : 1;
+
+	charger->battery_temperature = of_property_read_bool(np,
+					"active-semi,battery_temperature");
+
+	if (of_property_read_u32(np, "active-semi,input_voltage_threshold",
+				 &charger->input_voltage_threshold))
+		charger->input_voltage_threshold = DEFAULT_PRE_TIME_OUT;
+
+	if (of_property_read_u32(np, "active-semi,precondition_timeout",
+				 &charger->pre_time_out))
+		charger->pre_time_out = DEFAULT_PRE_TIME_OUT;
+
+	if (of_property_read_u32(np, "active-semi,total_timeout",
+				 &charger->tolal_time_out))
+		charger->tolal_time_out = DEFAULT_TOTAL_TIME_OUT;
+
+	return 0;
+}
+
+static int act8945a_charger_config(struct act8945a_charger *charger)
+{
+	struct regmap *regmap = charger->act8945a_dev->regmap;
+	u8 value = 0;
+
+	if (gpio_is_valid(charger->chglev_pin))
+		gpio_set_value(charger->chglev_pin, charger->chglev_value);
+
+	switch (charger->input_voltage_threshold) {
+	case 8000:
+		value |= APCH_CFG_OVPSET_8V;
+		break;
+	case 7500:
+		value |= APCH_CFG_OVPSET_7V5;
+		break;
+	case 7000:
+		value |= APCH_CFG_OVPSET_7V;
+		break;
+	case 6600:
+	default:
+		value |= APCH_CFG_OVPSET_6V6;
+		break;
+	}
+
+	switch (charger->pre_time_out) {
+	case 60:
+		value |= APCH_CFG_PRETIMO_60_MIN;
+		break;
+	case 80:
+		value |= APCH_CFG_PRETIMO_80_MIN;
+		break;
+	case 0:
+		value |= APCH_CFG_PRETIMO_DISABLED;
+		break;
+	case 40:
+	default:
+		value |= APCH_CFG_PRETIMO_40_MIN;
+		break;
+	}
+
+	switch (charger->tolal_time_out) {
+	case 4:
+		value |= APCH_CFG_TOTTIMO_4_HOUR;
+		break;
+	case 5:
+		value |= APCH_CFG_TOTTIMO_5_HOUR;
+		break;
+	case 0:
+		value |= APCH_CFG_TOTTIMO_DISABLED;
+		break;
+	case 3:
+	default:
+		value |= APCH_CFG_TOTTIMO_3_HOUR;
+		break;
+	}
+
+	return regmap_write(regmap, ACT8945A_APCH_CFG, value);
+}
+
+static int act8945a_charger_probe(struct platform_device *pdev)
+{
+	struct act8945a_dev *act8945a_dev = dev_get_drvdata(pdev->dev.parent);
+	struct act8945a_charger *charger;
+	struct power_supply_config psy_cfg = {};
+	int ret;
+
+	charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);
+	if (!charger)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, charger);
+
+	charger->dev = &pdev->dev;
+	charger->act8945a_dev = act8945a_dev;
+
+	ret = act8945a_charger_parse_dt(&pdev->dev, charger);
+	if (ret)
+		return ret;
+
+	ret = act8945a_charger_config(charger);
+	if (ret)
+		return ret;
+
+	psy_cfg.of_node	= pdev->dev.of_node;
+	psy_cfg.drv_data = charger;
+
+	charger->psy = devm_power_supply_register(&pdev->dev,
+					     &act8945a_charger_desc,
+					     &psy_cfg);
+	if (IS_ERR(charger->psy)) {
+		dev_err(act8945a_dev->dev, "failed to register power supply\n");
+		return PTR_ERR(charger->psy);
+	}
+
+	dev_info(act8945a_dev->dev, "charger driver registered\n");
+
+	return 0;
+}
+
+static int act8945a_charger_remove(struct platform_device *pdev)
+{
+	struct act8945a_charger *charger = platform_get_drvdata(pdev);
+
+	power_supply_unregister(charger->psy);
+
+	return 0;
+}
+
+static struct platform_driver act8945a_charger_driver = {
+	.driver	= {
+		.name = "act8945a-charger",
+	},
+	.probe	= act8945a_charger_probe,
+	.remove = act8945a_charger_remove,
+};
+module_platform_driver(act8945a_charger_driver);
+
+MODULE_DESCRIPTION("Active-semi ACT8945A ActivePath charger driver");
+MODULE_AUTHOR("Wenyou Yang <wenyou.yang@atmel.com>");
+MODULE_LICENSE("GPL");