diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index e0a677bcc01f..4a0d366b2185 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -610,6 +610,11 @@ config CHARGER_RT9455 help Say Y to enable support for Richtek RT9455 battery charger. +config CHARGER_NU1618 + tristate "nuvolta nu1618 wireless charger driver" + help + say y here to enable support for nu1618 charger driver. + source "drivers/power/supply/qcom/Kconfig" endif # POWER_SUPPLY diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index 4187af3ff90f..b6fff02b39ba 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -82,4 +82,5 @@ obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o obj-$(CONFIG_CHARGER_TPS65217) += tps65217_charger.o obj-$(CONFIG_AXP288_FUEL_GAUGE) += axp288_fuel_gauge.o obj-$(CONFIG_AXP288_CHARGER) += axp288_charger.o +obj-$(CONFIG_CHARGER_NU1618) += rx1618.o obj-$(CONFIG_ARCH_QCOM) += qcom/ diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index 97d338f71a61..4bcfcf67dcf6 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -354,7 +354,7 @@ static int __power_supply_is_system_supplied(struct device *dev, void *data) unsigned int *count = data; (*count)++; - if (psy->desc->type != POWER_SUPPLY_TYPE_BATTERY) + if ((psy->desc->type != POWER_SUPPLY_TYPE_BATTERY) && (psy->desc->type != POWER_SUPPLY_TYPE_BMS)) if (!psy->desc->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &ret)) return ret.intval; diff --git a/drivers/power/supply/power_supply_leds.c b/drivers/power/supply/power_supply_leds.c index 2277ad9c2f68..af2f76f8b2db 100644 --- a/drivers/power/supply/power_supply_leds.c +++ b/drivers/power/supply/power_supply_leds.c @@ -113,6 +113,8 @@ static void power_supply_remove_bat_triggers(struct power_supply *psy) static void power_supply_update_gen_leds(struct power_supply *psy) { + /* xiaomi project don't support this feature, return skip this feature*/ + #if 0 union power_supply_propval online; if (power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE, &online)) @@ -124,6 +126,7 @@ static void power_supply_update_gen_leds(struct power_supply *psy) led_trigger_event(psy->online_trig, LED_FULL); else led_trigger_event(psy->online_trig, LED_OFF); + #endif } static int power_supply_create_gen_triggers(struct power_supply *psy) diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index e702131d8ebd..5582791952dd 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -46,7 +46,7 @@ static const char * const power_supply_type_text[] = { "USB_PD", "USB_PD_DRP", "BrickID", "USB_HVDCP", "USB_HVDCP_3", "USB_HVDCP_3P5", "Wireless", "USB_FLOAT", "BMS", "Parallel", "Main", "Wipower", "USB_C_UFP", "USB_C_DFP", - "Charge_Pump", + "Charge_Pump","ZIMI_CAR_POWER" }; static const char * const power_supply_status_text[] = { @@ -164,6 +164,21 @@ static ssize_t power_supply_show_property(struct device *dev, if (off == POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT) return sprintf(buf, "%lld\n", value.int64val); + else if (off == POWER_SUPPLY_PROP_WIRELESS_VERSION) + return scnprintf(buf, PAGE_SIZE, "0x%x\n", + value.intval); + else if (off == POWER_SUPPLY_PROP_WIRELESS_WAKELOCK) + return scnprintf(buf, PAGE_SIZE, "%d\n", + value.intval); + else if (off == POWER_SUPPLY_PROP_SIGNAL_STRENGTH) + return scnprintf(buf, PAGE_SIZE, "%d\n", + value.intval); + else if (off == POWER_SUPPLY_PROP_WIRELESS_CP_EN) + return scnprintf(buf, PAGE_SIZE, "%d\n", + value.intval); + else if (off == POWER_SUPPLY_PROP_TYPE_RECHECK) + return scnprintf(buf, PAGE_SIZE, "0x%x\n", + value.intval); else return sprintf(buf, "%d\n", value.intval); } @@ -295,6 +310,8 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(charge_enabled), POWER_SUPPLY_ATTR(set_ship_mode), POWER_SUPPLY_ATTR(real_type), + POWER_SUPPLY_ATTR(hvdcp3_type), + POWER_SUPPLY_ATTR(quick_charge_type), POWER_SUPPLY_ATTR(charge_now_raw), POWER_SUPPLY_ATTR(charge_now_error), POWER_SUPPLY_ATTR(capacity_raw), @@ -349,6 +366,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(typec_src_rp), POWER_SUPPLY_ATTR(pd_allowed), POWER_SUPPLY_ATTR(pd_active), + POWER_SUPPLY_ATTR(pd_authentication), POWER_SUPPLY_ATTR(pd_in_hard_reset), POWER_SUPPLY_ATTR(pd_current_max), POWER_SUPPLY_ATTR(pd_usb_suspend_supported), @@ -372,9 +390,16 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(pd_voltage_max), POWER_SUPPLY_ATTR(pd_voltage_min), POWER_SUPPLY_ATTR(sdp_current_max), + POWER_SUPPLY_ATTR(dc_thermal_levels), POWER_SUPPLY_ATTR(connector_type), POWER_SUPPLY_ATTR(parallel_batfet_mode), POWER_SUPPLY_ATTR(parallel_fcc_max), + POWER_SUPPLY_ATTR(wireless_version), + POWER_SUPPLY_ATTR(signal_strength), + POWER_SUPPLY_ATTR(wireless_cp_en), + POWER_SUPPLY_ATTR(wireless_power_good_en), + POWER_SUPPLY_ATTR(wireless_wakelock), + POWER_SUPPLY_ATTR(tx_adapter), POWER_SUPPLY_ATTR(min_icl), POWER_SUPPLY_ATTR(moisture_detected), POWER_SUPPLY_ATTR(batt_profile_version), @@ -390,6 +415,9 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(force_recharge), POWER_SUPPLY_ATTR(fcc_stepper_enable), POWER_SUPPLY_ATTR(toggle_stat), + POWER_SUPPLY_ATTR(type_recheck), + POWER_SUPPLY_ATTR(liquid_detection), + POWER_SUPPLY_ATTR(dynamic_fv_enabled), POWER_SUPPLY_ATTR(main_fcc_max), POWER_SUPPLY_ATTR(fg_reset), POWER_SUPPLY_ATTR(qc_opti_disable), diff --git a/drivers/power/supply/qcom/Kconfig b/drivers/power/supply/qcom/Kconfig index 691f64258c17..16cfe6dbc976 100644 --- a/drivers/power/supply/qcom/Kconfig +++ b/drivers/power/supply/qcom/Kconfig @@ -48,6 +48,17 @@ config SMB1355_SLAVE_CHARGER The driver reports the charger status via the power supply framework. A charger status change triggers an IRQ via the device STAT pin. +config IDT_P9220 + tristate "idtp9220 wireless Charger" + depends on I2C + help + Say Y to include support for idtp9220 wireless Charger. + idtp9220 is a wireless battery charger. + The driver supports charger enable/disable. + The driver reports the charger status via the power supply framework. + The driver controls idtp9220 via I2C and + supports device-tree interface. + config QPNP_SMB2 tristate "SMB2 Battery Charger" depends on MFD_SPMI_PMIC diff --git a/drivers/power/supply/qcom/Makefile b/drivers/power/supply/qcom/Makefile index 7bd936244b54..00ffcdb071a2 100644 --- a/drivers/power/supply/qcom/Makefile +++ b/drivers/power/supply/qcom/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_QPNP_QNOVO) += qpnp-qnovo.o battery.o obj-$(CONFIG_QPNP_QNOVO5) += qpnp-qnovo5.o battery.o obj-$(CONFIG_QPNP_SMB5) += step-chg-jeita.o battery.o qpnp-smb5.o smb5-lib.o pmic-voter.o storm-watch.o schgm-flash.o obj-$(CONFIG_SMB1390_CHARGE_PUMP) += smb1390-charger.o pmic-voter.o +obj-$(CONFIG_IDT_P9220) += idtp9220.o obj-$(CONFIG_SMB1390_CHARGE_PUMP_PSY) += smb1390-charger-psy.o pmic-voter.o obj-$(CONFIG_SMB1398_CHARGER) += smb1398-charger.o pmic-voter.o obj-$(CONFIG_SMB358_CHARGER) += smb358-charger.o diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c index ccb0565a1c49..e266a25d859b 100644 --- a/drivers/power/supply/qcom/battery.c +++ b/drivers/power/supply/qcom/battery.c @@ -1,4 +1,5 @@ /* Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. + * Copyright (C) 2019 XiaoMi, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1314,6 +1315,27 @@ static int usb_icl_vote_callback(struct votable *votable, void *data, vote(chip->pl_disable_votable, ICL_CHANGE_VOTER, false, 0); + if (!chip->usb_psy) + chip->usb_psy = power_supply_get_by_name("usb"); + if (!chip->usb_psy) { + pr_err("Couldn't get usb psy\n"); + return -ENODEV; + } + + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_SMB_EN_REASON, &pval); + if (rc < 0) { + pr_err("Couldn't get cp reason rc=%d\n", rc); + return rc; + } + + if (chip->cp_ilim_votable) { + if (pval.intval != POWER_SUPPLY_CP_WIRELESS) + vote(chip->cp_ilim_votable, ICL_CHANGE_VOTER, true, icl_ua); + else + vote(chip->cp_ilim_votable, ICL_CHANGE_VOTER, false, 0); + } + /* Configure ILIM based on AICL result only if input mode is USBMID */ if (cp_get_parallel_mode(chip, PARALLEL_INPUT_MODE) == POWER_SUPPLY_PL_USBMID_USBMID) diff --git a/drivers/power/supply/qcom/fg-alg.c b/drivers/power/supply/qcom/fg-alg.c index c27f1d481cf3..9d6b1488d5ab 100644 --- a/drivers/power/supply/qcom/fg-alg.c +++ b/drivers/power/supply/qcom/fg-alg.c @@ -1,4 +1,5 @@ /* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. + * Copyright (C) 2019 XiaoMi, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -277,7 +278,28 @@ int get_cycle_counts(struct cycle_counter *counter, const char **buf) return 0; } -/** + /** + * set_cycle_count - + * @counter: Cycle counter object + * @value: The cycle count value to be set + * + * Get average cycle count for all buckets + * + */ +int set_cycle_count(struct cycle_counter *counter, u16 count) +{ + int rc, id; + + for (id = 0; id < BUCKET_COUNT; id++) { + rc = counter->store_count(counter->data, &count, id, 2); + if (rc < 0) + pr_err("failed to clear cycle counter rc=%d\n", rc); + } + + return 0; +} + + /** * cycle_count_init - * @counter: Cycle counter object * diff --git a/drivers/power/supply/qcom/fg-alg.h b/drivers/power/supply/qcom/fg-alg.h index 0bf466cce465..ad6ac01c898c 100644 --- a/drivers/power/supply/qcom/fg-alg.h +++ b/drivers/power/supply/qcom/fg-alg.h @@ -1,4 +1,5 @@ /* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. + * Copyright (C) 2019 XiaoMi, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -157,6 +158,7 @@ void cycle_count_update(struct cycle_counter *counter, int batt_soc, int charge_status, bool charge_done, bool input_present); int get_cycle_count(struct cycle_counter *counter, int *count); int get_cycle_counts(struct cycle_counter *counter, const char **buf); +int set_cycle_count(struct cycle_counter *counter, u16 count); int cycle_count_init(struct cycle_counter *counter); void cap_learning_abort(struct cap_learning *cl); void cap_learning_update(struct cap_learning *cl, int batt_temp, diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h index 267cdaf3b926..b3749be8b078 100644 --- a/drivers/power/supply/qcom/fg-core.h +++ b/drivers/power/supply/qcom/fg-core.h @@ -1,4 +1,5 @@ /* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. + * Copyright (C) 2019 XiaoMi, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -92,12 +93,22 @@ #define FULL_CAPACITY 100 #define FULL_SOC_RAW 255 +#define FULL_SOC_REPORT_THR 250 #define DEBUG_BATT_SOC 67 #define BATT_MISS_SOC 50 #define ESR_SOH_SOC 50 #define EMPTY_SOC 0 +#define VBAT_RESTART_FG_EMPTY_UV 3700000 +#define TEMP_THR_RESTART_FG 150 +#define RESTART_FG_START_WORK_MS 1000 +#define RESTART_FG_WORK_MS 2000 +#define EMPTY_REPORT_SOC 1 + +#define VBAT_CRITICAL_LOW_THR 2800 +#define EMPTY_DEBOUNCE_TIME_COUNT_MAX 5 + enum prof_load_status { PROFILE_MISSING, PROFILE_LOADED, @@ -333,6 +344,7 @@ struct fg_batt_props { int float_volt_uv; int vbatt_full_mv; int fastchg_curr_ma; + int nom_cap_uah; int *therm_coeffs; int therm_ctr_offset; int therm_pull_up_kohms; @@ -480,6 +492,8 @@ struct fg_dev { int last_recharge_volt_mv; int delta_temp_irq_count; enum esr_filter_status esr_flt_sts; + int vbatt_full_volt_uv; + int vbat_critical_low_count; bool profile_available; enum prof_load_status profile_load_status; bool battery_missing; @@ -490,8 +504,11 @@ struct fg_dev { bool use_ima_single_mode; bool usb_present; bool twm_state; + bool report_full; bool use_dma; bool qnovo_enable; + bool empty_restart_fg; + bool input_present; enum fg_version version; bool suspended; struct completion soc_update; @@ -503,6 +520,8 @@ struct fg_dev { struct work_struct esr_filter_work; struct alarm esr_filter_alarm; ktime_t last_delta_temp_time; + struct delayed_work empty_restart_fg_work; + struct delayed_work soc_work; }; /* Debugfs data structures are below */ diff --git a/drivers/power/supply/qcom/fg-util.c b/drivers/power/supply/qcom/fg-util.c index c1b66cc1f400..8020b15e3f92 100644 --- a/drivers/power/supply/qcom/fg-util.c +++ b/drivers/power/supply/qcom/fg-util.c @@ -1,4 +1,5 @@ /* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. + * Copyright (C) 2019 XiaoMi, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -407,7 +408,9 @@ void fg_notify_charger(struct fg_dev *fg) } } - if (fg->bp.fastchg_curr_ma > 0) { + fg_dbg(fg, FG_STATUS, "Notified charger on float voltage and FCC\n"); + + /*if (fg->bp.fastchg_curr_ma > 0) { prop.intval = fg->bp.fastchg_curr_ma * 1000; rc = power_supply_set_property(fg->batt_psy, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, @@ -417,7 +420,7 @@ void fg_notify_charger(struct fg_dev *fg) rc); return; } - } + }*/ } bool batt_psy_initialized(struct fg_dev *fg) @@ -867,6 +870,8 @@ wait: goto out; } out: + if (fg->empty_restart_fg) + fg->empty_restart_fg = false; fg->fg_restarting = false; return rc; } @@ -916,10 +921,17 @@ int fg_get_msoc(struct fg_dev *fg, int *msoc) * of the values 1-254 will be scaled to 1-99. DIV_ROUND_UP will not * be suitable here as it rounds up any value higher than 252 to 100. */ - if (*msoc == FULL_SOC_RAW) + if ((*msoc >= FULL_SOC_REPORT_THR - 2) + && (*msoc < FULL_SOC_RAW) && fg->report_full) { + *msoc = DIV_ROUND_CLOSEST(*msoc * FULL_CAPACITY, FULL_SOC_RAW) + 1; + if (*msoc >= FULL_CAPACITY) + *msoc = FULL_CAPACITY; + } else if (*msoc == FULL_SOC_RAW) *msoc = 100; else if (*msoc == 0) *msoc = 0; + else if (*msoc >= FULL_SOC_REPORT_THR - 4 && *msoc <= FULL_SOC_REPORT_THR - 3 && fg->report_full) + *msoc = DIV_ROUND_CLOSEST(*msoc * FULL_CAPACITY, FULL_SOC_RAW); else *msoc = DIV_ROUND_CLOSEST((*msoc - 1) * (FULL_CAPACITY - 2), FULL_SOC_RAW - 2) + 1; diff --git a/drivers/power/supply/qcom/idtp9220.c b/drivers/power/supply/qcom/idtp9220.c new file mode 100644 index 000000000000..53b4dd4656e9 --- /dev/null +++ b/drivers/power/supply/qcom/idtp9220.c @@ -0,0 +1,2201 @@ +/** + * Copyright “Copyright (C) 2018 XiaoMi, Inc + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +/*add for sdm845 request*/ +#include +#include +#include +#include +#include +#include +#include +#include + +/* +#ifdef CONFIG_DRM +#include +#endif +*/ + +static struct idtp9220_device_info *g_di; + +struct idtp9220_access_func { + int (*read)(struct idtp9220_device_info *di, u16 reg, u8 *val); + int (*write)(struct idtp9220_device_info *di, u16 reg, u8 val); + int (*read_buf)(struct idtp9220_device_info *di, + u16 reg, u8 *buf, u32 size); + int (*write_buf)(struct idtp9220_device_info *di, + u16 reg, u8 *buf, u32 size); +}; + +struct idtp9220_dt_props { + unsigned int irq_gpio; + unsigned int enable_gpio; + unsigned int wpc_det_gpio; +}; + +struct idtp9220_device_info { + int chip_enable; + char *name; + struct device *dev; + struct idtp9220_access_func bus; + struct regmap *regmap; + struct idtp9220_dt_props dt_props; + int irq; + int wpc_irq; + struct delayed_work irq_work; + struct delayed_work wpc_det_work; + struct pinctrl *idt_pinctrl; + struct pinctrl_state *idt_gpio_active; + struct pinctrl_state *idt_gpio_suspend; + struct power_supply *usb_psy; + struct power_supply *dc_psy; + struct power_supply *batt_psy; + struct power_supply *idtp_psy; + struct power_supply *wireless_psy; + struct mutex read_lock; + struct mutex write_lock; + struct delayed_work chg_monitor_work; + struct delayed_work chg_detect_work; + struct delayed_work bpp_connect_load_work; + struct delayed_work epp_connect_load_work; + struct delayed_work cmd_check_work; + struct delayed_work vout_regulator_work; + struct delayed_work rx_vout_work; + struct delayed_work dc_check_work; + struct delayed_work fast_operate_work; + struct delayed_work mophie_tx_work; + struct delayed_work bpp_e5_tx_work; + struct delayed_work qc2_f1_tx_work; + struct delayed_work qc3_epp_work; + + bool screen_on; + int tx_charger_type; + int status; + int count_5v; + int count_9v; + int count_12v; + int count_15v; + int exchange; + int epp_exchange; + int dcin_present; + int epp; + int vbuck; + int power_off_mode; + int power_max; + u8 header; + u8 cmd; + int is_compatible_hwid; + int last_vin; + int last_vbuck; + int is_car_tx; + int last_icl; + int power_good_flag; + + /*idt9220 charging info*/ + int vout; + int iout; + int f; + int vrect; + int ss; + int is_vin_limit; + int is_epp_qc3; + /* bpp e5_tx info*/ + int last_bpp_icl; + int last_bpp_vout; + /* qc2+f1_tx info*/ + int is_f1_tx; + int bpp_vout_rise; + int last_qc2_vout; + int last_qc2_icl; + /* qc3_epp+f1_tx info*/ + int last_qc3_vout; + int last_qc3_icl; +}; + +void idtp922x_request_adapter(struct idtp9220_device_info *di); +static void idtp9220_set_charging_param(struct idtp9220_device_info *di); + +/*static int idt_signal_strength = 0; +module_param_named(ss, idt_signal_strength, int, 0600); +*/ + +static int idt_signal_range = 2; +module_param_named(signal_range, idt_signal_range, int, 0644); + +static int idtp9220_get_property_names(struct idtp9220_device_info *di) +{ + di->batt_psy = power_supply_get_by_name("battery"); + if (!di->batt_psy) { + dev_err(di->dev, "[idt] no batt_psy,return\n"); + return -EINVAL; + } + di->dc_psy = power_supply_get_by_name("dc"); + if (!di->dc_psy) { + dev_err(di->dev, "[idt] no dc_psy,return\n"); + return -EINVAL; + } + di->usb_psy = power_supply_get_by_name("usb"); + if (!di->usb_psy) { + dev_err(di->dev, "[idt] no usb_psy,return\n"); + return -EINVAL; + } + di->wireless_psy = power_supply_get_by_name("wireless"); + if (!di->wireless_psy) { + dev_err(di->dev, "[idt] no wireless_psy,return\n"); + return -EINVAL; + } + return 0; +} + + +int idtp9220_read(struct idtp9220_device_info *di, u16 reg, u8 *val) { + unsigned int temp; + int rc; + + mutex_lock(&di->read_lock); + rc = regmap_read(di->regmap, reg, &temp); + if (rc >= 0) + *val = (u8)temp; + + mutex_unlock(&di->read_lock); + return rc; +} + +int idtp9220_write(struct idtp9220_device_info *di, u16 reg, u8 val) { + int rc = 0; + + mutex_lock(&di->write_lock); + rc = regmap_write(di->regmap, reg, val); + if (rc < 0) + dev_err(di->dev, "[idt] idtp9220 write error: %d\n", rc); + + mutex_unlock(&di->write_lock); + return rc; +} + +int idtp9220_read_buffer(struct idtp9220_device_info *di, u16 reg, u8 *buf, u32 size) { + int rc =0; + + while (size--) { + rc = di->bus.read(di, reg++, buf++); + if (rc < 0) { + dev_err(di->dev, "[idt] write error: %d\n", rc); + return rc; + } + } + + return rc; +} + +int idtp9220_write_buffer(struct idtp9220_device_info *di, u16 reg, u8 *buf, u32 size) { + int rc = 0; + + while (size--) { + rc = di->bus.write(di, reg++, *buf++); + if (rc < 0) { + dev_err(di->dev, "[idt] write error: %d\n", rc); + return rc; + } + } + + return rc; +} + +u32 ExtractPacketSize(u8 hdr) { + if (hdr < 0x20) + return 1; + if (hdr < 0x80) + return (2 + ((hdr - 0x20) >> 4)); + if (hdr < 0xe0) + return (8 + ((hdr - 0x80) >> 3)); + return (20 + ((hdr - 0xe0) >> 2)); +} + +void idtp922x_clrInt(struct idtp9220_device_info *di, u8 *buf, u32 size) { + di->bus.write_buf(di, REG_SSINTCLR, buf, size); + di->bus.write(di, REG_SSCMND, CLRINT); +} + +void idtp922x_sendPkt(struct idtp9220_device_info *di, ProPkt_Type *pkt) { + u32 size = ExtractPacketSize(pkt->header)+1; + di->bus.write_buf(di, REG_PROPPKT, (u8 *)pkt, size); // write data into proprietary packet buffer + di->bus.write(di, REG_SSCMND, SENDPROPP); // send proprietary packet + + dev_info(di->dev, "pkt header: 0x%x and cmd: 0x%x\n", + pkt->header, pkt->cmd); + di->header = pkt->header; + di->cmd = pkt->cmd; +} + +void idtp922x_receivePkt(struct idtp9220_device_info *di, u8 *buf) { + u8 header; + int rc; + u32 size; + + di->bus.read(di, REG_BCHEADER, &header); + size = ExtractPacketSize(header)+1; + rc = di->bus.read_buf(di, REG_BCDATA, buf, size); + if (rc < 0) + dev_err(di->dev, "[idt] read Tx data error: %d\n", rc); +} + +void idtp922x_set_adap_vol(struct idtp9220_device_info *di, u16 mv) +{ + dev_info(di->dev, "set adapter vol to %d\n", mv); + di->bus.write(di, REG_FC_VOLTAGE_L, mv&0xff); + di->bus.write(di, REG_FC_VOLTAGE_H, (mv>>8)&0xff); + di->bus.write(di, REG_SSCMND, VSWITCH); +} + + +void idtp922x_set_pmi_icl(struct idtp9220_device_info *di, int mA) +{ + union power_supply_propval val = {0, }; + int rc; + + rc = idtp9220_get_property_names(di); + val.intval = mA; + power_supply_set_property(di->dc_psy, POWER_SUPPLY_PROP_CURRENT_MAX, &val); +} + +/* Adapter Type */ +/* Adapter_list = {0x00:'ADAPTER_UNKNOWN', */ +/* 0x01:'SDP 500mA', */ +/* 0x02:'CDP 1.1A', */ +/* 0x03:'DCP 1.5A', */ +/* 0x05:'QC2.0', */ +/* 0x06:'QC3.0', */ +/* 0x07:'PD',} */ +void idtp922x_request_adapter(struct idtp9220_device_info *di) +{ + ProPkt_Type pkt; + pkt.header = PROPRIETARY18; + pkt.cmd = BC_ADAPTER_TYPE; + + idtp922x_sendPkt(di, &pkt); +} + +void idtp922x_request_uuid(struct idtp9220_device_info *di, int is_epp) +{ + ProPkt_Type pkt; + pkt.header = PROPRIETARY18; + if(is_epp) + pkt.cmd = BC_TX_HWID; + else + pkt.cmd = BC_TX_COMPATIBLE_HWID; + idtp922x_sendPkt(di, &pkt); +} + +void idtp922x_get_tx_vin(struct idtp9220_device_info *di) +{ + ProPkt_Type pkt; + pkt.header = PROPRIETARY18; + pkt.cmd = BC_READ_Vin; + + idtp922x_sendPkt(di, &pkt); +} + +void idtp922x_retry_cmd(struct idtp9220_device_info *di) +{ + ProPkt_Type pkt; + pkt.header = di->header; + pkt.cmd = di->cmd; + + idtp922x_sendPkt(di, &pkt); +} + +static void idtp9220_int_enable(struct idtp9220_device_info *di) +{ + u8 int_val; + di->bus.read(di, REG_INTR_EN_L, &int_val); + di->bus.write(di, REG_INTR_EN_L, (int_val | OVER_EVENT_OCCUR)); + dev_info(di->dev, "[idtp9220]: int enabled status 0x%x\n", (int_val | OVER_EVENT_OCCUR)); +} + + +static int idtp9220_get_vout_regulator(struct idtp9220_device_info *di) +{ + u8 vout_l, vout_h; + u16 vout; + + if(!di) + return 0; + + di->bus.read(di, REG_REGULATOR_L, &vout_l); + di->bus.read(di, REG_REGULATOR_H, &vout_h); + vout = vout_l | ((vout_h & 0xff)<< 8); + dev_info(di->dev, "vout regulator get vol: %d\n", vout); + + return vout; +} + +void idtp9220_set_toggle_mode(struct idtp9220_device_info *di) +{ + ProPkt_Type pkt; + pkt.header = PROPRIETARY18; + pkt.cmd = BC_TX_TOGGLE; + idtp922x_sendPkt(di, &pkt); +} + +void idtp9220_retry_id_auth(struct idtp9220_device_info *di) +{ + ProPkt_Type pkt; + pkt.header = PROPRIETARY38; + pkt.cmd = BC_RX_ID_AUTH; + + pkt.data[0] = 0x02; + pkt.data[1] = 0xbb; + + idtp922x_sendPkt(di, &pkt); +} + +static int idtp9220_set_vout_regulator(struct idtp9220_device_info *di, int mv) +{ + u8 vout_l, vout_h; + u16 vout; + + if(!di) + return 0; + vout_l = mv & 0xff; + vout_h = mv >> 8; + + dev_info(di->dev, "vout regulator vout_l: 0x%x and vout_h: 0x%x\n", vout_l, vout_h); + di->bus.write(di, REG_REGULATOR_L, vout_l); + di->bus.write(di, REG_REGULATOR_H, vout_h); + + vout = vout_l | ((vout_h & 0xff) << 8); + dev_info(di->dev, "vout regulator set vol: %d\n", vout); + + return vout; +} + +static int idtp9220_get_vbuck(struct idtp9220_device_info *di) +{ + u8 vbuck_l, vbuck_h; + u16 vbuck_ret; + + if(!di) + return 0; + + di->bus.read(di, 0x08, &vbuck_l); + di->bus.read(di, 0x09, &vbuck_h); + vbuck_ret = vbuck_l | (vbuck_h << 8); + + return vbuck_ret; +} + + +static int idtp9220_get_vout(struct idtp9220_device_info *di) +{ + u8 vout_l, vout_h; + + if(!di) + return 0; + + di->bus.read(di, REG_ADC_VOUT_L, &vout_l); + di->bus.read(di, REG_ADC_VOUT_H, &vout_h); + di->vout = vout_l | ((vout_h & 0xf)<< 8); + di->vout = di->vout * 10 * 21 * 1000 / 40950 + ADJUST_METE_MV; //vout = val/4095*10*2.1 + + return di->vout; +} + +static void idtp9220_set_vout(struct idtp9220_device_info *di, int mv) +{ + u8 val; + if(!di) + return; + val = (mv -3500)/100; + di->bus.write(di, REG_VOUT_SET, val); + dev_info(di->dev, "[idtp9220]: set vout voltage is 0x%x\n", val); +} + +static void idtp9220_set_reset(struct idtp9220_device_info *di) +{ + if(!di) + return; + di->bus.write(di, REG_RX_RESET, 0x01); + dev_info(di->dev, "[idtp9220]: set RX reset\n"); +} +extern char *saved_command_line; + +static int get_cmdline(struct idtp9220_device_info *di) +{ + if (strnstr(saved_command_line, "androidboot.mode=", + strlen(saved_command_line))) { + + di->power_off_mode = 1; + dev_info(di->dev, "[idtp9220]: enter power off charging app\n"); + } else { + di->power_off_mode = 0; + dev_info(di->dev, "[idtp9220]: enter normal boot mode\n"); + } + return 1; +} + +static int idtp9220_get_iout(struct idtp9220_device_info *di) +{ + u8 cout_l, cout_h; + + if(!di) + return 0; + + di->bus.read(di, REG_RX_LOUT_L, &cout_l); + di->bus.read(di, REG_RX_LOUT_H, &cout_h); + di->iout = cout_l | (cout_h << 8); + + return di->iout; +} + +static int idtp9220_get_power_profile(struct idtp9220_device_info *di) +{ + u8 mode; + int ret; + + di->bus.read(di, REG_WPC_MODE, &mode); + ret = mode & BIT(3); + dev_info(di->dev, "tx is epp ? ret is 0x%x\n", mode); + + return ret; +} + + +static int idtp9220_get_freq(struct idtp9220_device_info *di) +{ + u8 data_list[2]; + + di->bus.read_buf(di, REG_FREQ_ADDR, data_list, 2); + di->f = 64*HSCLK/(data_list[0] | (data_list[1] << 8))/10; + + return di->f; +} + +static int idtp9220_get_vrect(struct idtp9220_device_info *di) +{ + u8 data_list[2]; + + di->bus.read_buf(di, REG_ADC_VRECT, data_list, 2); + di->vrect = data_list[0] | ((data_list[1] & 0xf)<< 8); + di->vrect = di->vrect * 10 * 21 * 1000 / 40950; //vrect = val/4095*10*2.1 + + return di->vrect; +} + +static int idtp9220_get_power_max(struct idtp9220_device_info *di) +{ + int power_max; + u8 val; + + di->bus.read(di, REG_POWER_MAX, &val); + power_max = (val/2)*1000; + dev_info(di->dev, "rx power max is %dmW\n", power_max); + + return power_max; +} + +static void idtp9220_send_device_auth(struct idtp9220_device_info *di) +{ + di->bus.write(di, REG_SSCMND, SEND_DEVICE_AUTH); +} + + +static ssize_t chip_version_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 chip_id_l, chip_id_h, chip_rev, cust_id, status, vset; + u8 fw_otp_ver[4], fw_app_ver[4]; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct idtp9220_device_info *di = i2c_get_clientdata(client); + di->bus.read(di, REG_STATUS_L, &status); + di->bus.read(di, REG_VOUT_SET, &vset); + + di->bus.read(di, REG_CHIP_ID_L, &chip_id_l); + di->bus.read(di, REG_CHIP_ID_H, &chip_id_h); + di->bus.read(di, REG_CHIP_REV, &chip_rev); + chip_rev = chip_rev >> 4; + di->bus.read(di, REG_CTM_ID, &cust_id); + di->bus.read_buf(di, REG_OTPFWVER_ADDR, fw_otp_ver, 4); + di->bus.read_buf(di, REG_EPRFWVER_ADDR, fw_app_ver, 4); + + return sprintf(buf, "chip_id_l:%02x\nchip_id_h:%02x\nchip_rev:%02x\ncust_id:%02x status:%02x vset:%02x\n otp_ver:%x.%x.%x.%x\n app_ver:%x.%x.%x.%x\n", + chip_id_l, chip_id_h, chip_rev, cust_id, status, vset, + fw_otp_ver[0], fw_otp_ver[1], fw_otp_ver[2], fw_otp_ver[3], + fw_app_ver[0], fw_app_ver[1], fw_app_ver[2], fw_app_ver[3]); +} + +/* voltage limit attrs */ +static ssize_t chip_vout_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct idtp9220_device_info *di = i2c_get_clientdata(client); + int vout; + + vout = idtp9220_get_vout(di); + return sprintf(buf, "Vout ADC Value: %dMV\n", vout); + +} + +static ssize_t chip_vout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int index; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct idtp9220_device_info *di = i2c_get_clientdata(client); + + index = (int)simple_strtoul(buf, NULL, 16); +/* + if ((index < VOUT_VAL_3500_MV) || (index > VOUT_VAL_5000_MV)) { + dev_err(di->dev, "Store Val %s is invalid!\n", buf); + return count; + } +*/ + + idtp9220_set_vout(di, index); + + return count; +} + +static ssize_t vout_regulator_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct idtp9220_device_info *di = i2c_get_clientdata(client); + int vout; + + vout = idtp9220_get_vout_regulator(di); + + return sprintf(buf, "Vout ADC Value: %dMV\n", vout); +} + +#define VOUT_MIN_4900_MV 4900 +#define VOUT_MAX_10000_MV 10000 +static ssize_t vout_regulator_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int vout; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct idtp9220_device_info *di = i2c_get_clientdata(client); + + vout = (int)simple_strtoul(buf, NULL, 10); + if ((vout <= VOUT_MIN_4900_MV) || (vout > VOUT_MAX_10000_MV)) { + dev_err(di->dev, "Store Val %s : %ld is invalid!\n", buf, vout ); + return count; + } + idtp9220_set_vout_regulator(di, vout); + + return count; +} + +/* current attrs */ +static ssize_t chip_iout_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct idtp9220_device_info *di = i2c_get_clientdata(client); + int cout; + + cout = idtp9220_get_iout(di); + + return sprintf(buf, "Output Current: %dMA\n", cout); +} + +static ssize_t chip_iout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int index; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct idtp9220_device_info *di = i2c_get_clientdata(client); + + index = (int)simple_strtoul(buf, NULL, 10); + + if ((index < CURR_VAL_100_MA) || (index > CURR_VAL_1300_MA)) { + dev_err(di->dev, "Store Val %s is invalid", buf); + return count; + } + + di->bus.write(di, REG_ILIM_SET, index); + + return count; +} + +static ssize_t chip_freq_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct idtp9220_device_info *di = i2c_get_clientdata(client); + int f; + + f = idtp9220_get_freq(di); + + return sprintf(buf, "Output Current: %dkHz\n", f); +} + +static void idtp9220_charging_info(struct idtp9220_device_info *di) +{ + int vbuck_ret = 0; + if(!di) + return; + + idtp9220_get_vout(di); + idtp9220_get_iout(di); + idtp9220_get_freq(di); + idtp9220_get_vrect(di); + vbuck_ret = idtp9220_get_vbuck(di); + + dev_info(di->dev, "%s:Vout:%dmV,Iout:%dmA,Freq:%dKHz,Vrect:%dmV,SS:%d, Vbuck:%d\n", __func__, + di->vout, di->iout, di->f, di->vrect, di->ss, vbuck_ret); +} + +/* chip enable attrs */ +static ssize_t chip_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct idtp9220_device_info *di = i2c_get_clientdata(client); + + if (gpio_is_valid(di->dt_props.enable_gpio)) + ret = gpio_get_value(di->dt_props.enable_gpio); + else { + dev_err(di->dev, "%s: sleep gpio not provided\n", __func__); + ret = -1; + } + + dev_info(di->dev, "chip enable gpio: %d\n", ret); + + return sprintf(buf, "Chip enable: %d\n", !ret); +} + +static int idtp9220_set_present(struct idtp9220_device_info *di, int enable) +{ + int ret = 0; + + dev_info(di->dev, "[idtp] dc plug %s\n", enable ? "in" : "out"); + if(enable) + { + di->dcin_present = true; + di->ss = 1; + } else { + schedule_delayed_work(&di->fast_operate_work, msecs_to_jiffies(200)); + di->status = NORMAL_MODE; + di->count_9v = 0; + di->count_5v = 0; + di->count_12v = 0; + di->count_15v = 0; + di->exchange = 0; + di->epp_exchange = 0; + di->dcin_present = false; + idt_signal_range = 2; + di->ss = 2; + di->vbuck = 0; + di->epp = 0; + di->last_vin = 0; + di->last_icl = 0; + di->last_vbuck = 0; + di->is_car_tx = 0; + di->power_off_mode = 0; + di->is_epp_qc3 = 0; + di->is_vin_limit = 0; + di->bpp_vout_rise = 0; + di->is_f1_tx = 0; + di->last_bpp_vout = 0; + di->last_bpp_icl = 0; + di->last_qc2_vout = 0; + di->last_qc2_icl = 0; + di->last_qc3_vout = 0; + di->last_qc3_icl = 0; + di->tx_charger_type = ADAPTER_NONE; + cancel_delayed_work(&di->chg_monitor_work); + cancel_delayed_work(&di->cmd_check_work); + } + + return ret; +} + + +static int idtp9220_set_enable_mode(struct idtp9220_device_info *di, int enable) +{ + int ret = 0; + + if (gpio_is_valid(di->dt_props.enable_gpio)) { + ret = gpio_request(di->dt_props.enable_gpio, + "idt-enable-gpio"); + if (ret) { + dev_err(di->dev, + "%s: unable to request idt enable gpio [%d]\n", + __func__, di->dt_props.enable_gpio); + } + + ret = gpio_direction_output(di->dt_props.enable_gpio, !enable); + if (ret) { + dev_err(di->dev, + "%s: cannot set direction for idt enable gpio [%d]\n", + __func__, di->dt_props.enable_gpio); + } + gpio_free(di->dt_props.enable_gpio); + } + + return ret; +} + +static ssize_t chip_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int ret, enable; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct idtp9220_device_info *di = i2c_get_clientdata(client); + + ret = (int)simple_strtoul(buf, NULL, 10); + enable = !!ret; + + idtp9220_set_enable_mode(di, enable); + + return count; +} + +static DEVICE_ATTR(chip_enable, S_IWUSR | S_IRUGO, chip_enable_show, chip_enable_store); +static DEVICE_ATTR(chip_version, S_IRUGO, chip_version_show, NULL); +static DEVICE_ATTR(chip_vout, S_IWUSR | S_IRUGO, chip_vout_show, chip_vout_store); +static DEVICE_ATTR(chip_iout, S_IWUSR | S_IRUGO, chip_iout_show, chip_iout_store); +static DEVICE_ATTR(chip_freq, S_IRUGO, chip_freq_show, NULL); +static DEVICE_ATTR(vout_regulator, S_IWUSR | S_IRUGO, vout_regulator_show, vout_regulator_store); + +static struct attribute *sysfs_attrs[] = { + &dev_attr_chip_version.attr, + &dev_attr_chip_vout.attr, + &dev_attr_chip_iout.attr, + &dev_attr_chip_freq.attr, + &dev_attr_chip_enable.attr, + &dev_attr_vout_regulator.attr, + NULL, +}; + +static const struct attribute_group sysfs_group_attrs = { + .attrs = sysfs_attrs, +}; + +static int idtp9220_parse_dt(struct idtp9220_device_info *di) +{ + struct device_node *node = di->dev->of_node; + + if (!node) { + dev_err(di->dev, "device tree node missing\n"); + return -EINVAL; + } + + di->dt_props.irq_gpio = of_get_named_gpio(node, "idt,irq", 0); + if ((!gpio_is_valid(di->dt_props.irq_gpio))) + return -EINVAL; + + di->dt_props.enable_gpio = of_get_named_gpio(node, "idt,enable", 0); + if ((!gpio_is_valid(di->dt_props.enable_gpio))) + return -EINVAL; + + di->dt_props.wpc_det_gpio = of_get_named_gpio(node, "idt,wpc-det", 0); + if ((!gpio_is_valid(di->dt_props.irq_gpio))) + return -EINVAL; + + return 0; +} + +static int idtp9220_gpio_init(struct idtp9220_device_info *di) +{ + int ret = 0; + int irqn = 0; + int wpc_irq = 0; + + di->idt_pinctrl = devm_pinctrl_get(di->dev); + if (IS_ERR_OR_NULL(di->idt_pinctrl)) { + dev_err(di->dev, "No pinctrl config specified\n"); + ret = PTR_ERR(di->dev); + return ret; + } + di->idt_gpio_active = + pinctrl_lookup_state(di->idt_pinctrl, "idt_active"); + if (IS_ERR_OR_NULL(di->idt_gpio_active)) { + dev_err(di->dev, "No active config specified\n"); + ret = PTR_ERR(di->idt_gpio_active); + return ret; + } + di->idt_gpio_suspend = + pinctrl_lookup_state(di->idt_pinctrl, "idt_suspend"); + if (IS_ERR_OR_NULL(di->idt_gpio_suspend)) { + dev_err(di->dev, "No suspend config specified\n"); + ret = PTR_ERR(di->idt_gpio_suspend); + return ret; + } + + ret = pinctrl_select_state(di->idt_pinctrl, + di->idt_gpio_active); + if (ret < 0) { + dev_err(di->dev, "fail to select pinctrl active rc=%d\n", + ret); + return ret; + } + + if (gpio_is_valid(di->dt_props.irq_gpio)) { + irqn = gpio_to_irq(di->dt_props.irq_gpio); + if (irqn < 0) { + ret = irqn; + goto err_irq_gpio; + } + di->irq = irqn; + } else { + dev_err(di->dev, "%s: irq gpio not provided\n", __func__); + goto err_irq_gpio; + } + + if (gpio_is_valid(di->dt_props.wpc_det_gpio)) { + wpc_irq = gpio_to_irq(di->dt_props.wpc_det_gpio); + if (wpc_irq < 0) { + ret = wpc_irq; + goto err_wpc_irq; + } + di->wpc_irq = wpc_irq; + } else { + dev_err(di->dev, "%s: wpc irq gpio not provided\n", __func__); + goto err_wpc_irq; + } + +err_wpc_irq: + gpio_free(di->dt_props.wpc_det_gpio); +err_irq_gpio: + gpio_free(di->dt_props.irq_gpio); + return ret; +} + +static bool need_irq_cleared(struct idtp9220_device_info *di) +{ + u8 int_buf[2]; + u16 int_val; + int rc = -1; + + rc = di->bus.read_buf(di, REG_INTR_L, int_buf, 2); + if (rc < 0) { + dev_err(di->dev, "%s: read int state error\n", __func__); + return true; + } + int_val = int_buf[0] | (int_buf[1] << 8); + if (int_val != 0) { + dev_info(di->dev, "irq not clear right: 0x%04x\n", int_val); + return true; + } + + if (gpio_is_valid(di->dt_props.irq_gpio)) + rc = gpio_get_value(di->dt_props.irq_gpio); + else { + dev_err(di->dev, "%s: irq gpio not provided\n", __func__); + rc = -1; + } + if (!rc) { + dev_info(di->dev, "irq low, need clear int: %d\n", rc); + return true; + } + return false; +} + +#define VBUCK_QC_VOL 7000 +#define VBUCK_FULL_VOL 7000 +#define VBUCK_DEFAULT_VOL 5500 +#define ADAPTER_BPP_QC_VOL 9000 +#define ADAPTER_BPP_LIMIT_VOL 6500 +#define BPP_VOL_THRESHOLD 8000 +#define ADAPTER_BPP_VOL 5000 +#define ADAPTER_EPP_MI_VOL 15000 +#define EPP_VOL_THRESHOLD 12000 +#define EPP_VOL_LIM_THRESHOLD 13000 +#define BPP_VOL_LIM_THRESHOLD 10000 +#define CHARGING_PERIOD_S 10 +#define TAPER_CURR_Limit 950000 + +static void idtp9220_monitor_work(struct work_struct *work) +{ + struct idtp9220_device_info *di = + container_of(work, struct idtp9220_device_info, + chg_monitor_work.work); + + idtp9220_charging_info(di); + + idtp9220_set_charging_param(di); + + schedule_delayed_work(&di->chg_monitor_work, + CHARGING_PERIOD_S * HZ); +} + +static void idtp9220_rx_vout_work(struct work_struct *work) +{ + struct idtp9220_device_info *di = + container_of(work, struct idtp9220_device_info, + rx_vout_work.work); + + idtp9220_set_charging_param(di); + +} + +static void idtp9220_dc_check_work(struct work_struct *work) +{ + struct idtp9220_device_info *di = + container_of(work, struct idtp9220_device_info, + dc_check_work.work); + + dev_info(di->dev, "[idt] dc present: %d\n", di->dcin_present); + if (di->dcin_present) { + di->ss = 1; + dev_info(di->dev, "dcin present, quit dc check work\n"); + return; + } else { + di->ss = 0; + dev_info(di->dev, "dcin no present, continue dc check work\n"); + schedule_delayed_work(&di->dc_check_work, msecs_to_jiffies(2500)); + } + power_supply_changed(di->idtp_psy); +} + +static void idtp9220_fast_operate_work(struct work_struct *work) +{ + struct idtp9220_device_info *di = + container_of(work, struct idtp9220_device_info, + fast_operate_work.work); + int ret = -1; + int usb_present, typec_mode; + union power_supply_propval val = {0, }; + + ret = idtp9220_get_property_names(di); + if(ret < 0) + return; + + power_supply_get_property(di->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &val); + usb_present = val.intval; + + power_supply_get_property(di->usb_psy, + POWER_SUPPLY_PROP_TYPEC_MODE, &val); + typec_mode = val.intval; + + dev_info(di->dev, "usb present:%d typec mode:%d\n", + usb_present, typec_mode); + + if (gpio_is_valid(di->dt_props.wpc_det_gpio)) { + ret = gpio_get_value(di->dt_props.wpc_det_gpio); + /* power good irq will not trigger after insert typec audio/charger + * connector while wireless charging. WR for this situation. + */ + if ((!usb_present && ret) || (usb_present && ret + && (typec_mode == POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER + || typec_mode == POWER_SUPPLY_TYPEC_NONE))){ + dev_info(di->dev, "dc out but power_good high, reset by sleep\n"); + idtp9220_set_enable_mode(di, false); + msleep(10); + idtp9220_set_enable_mode(di, true); + } + } +} + +static void idtp9220_chg_detect_work(struct work_struct *work) +{ + struct idtp9220_device_info *di = + container_of(work, struct idtp9220_device_info, + chg_detect_work.work); + + union power_supply_propval val = {0, }; + union power_supply_propval wk_val = {0, }; + int rc; + + dev_info(di->dev, "[idt] enter %s\n", __func__); + + rc = idtp9220_get_property_names(di); + if(rc < 0) + return; + + /*set idtp9220 into sleep mode when usbin*/ + + power_supply_get_property(di->usb_psy, + POWER_SUPPLY_PROP_ONLINE, &val); + if(val.intval) { + dev_info(di->dev, "[idt] usb_online:%d set chip disable\n", val.intval); + idtp9220_set_enable_mode(di, false); + return; + } + + if(di->dc_psy) { + power_supply_get_property(di->dc_psy, + POWER_SUPPLY_PROP_ONLINE, &val); + dev_info(di->dev, "[idt] dc_online %d\n", val.intval); + if(val.intval && di->wireless_psy) { + idtp9220_int_enable(di); + wk_val.intval = 1; + power_supply_set_property(di->wireless_psy, + POWER_SUPPLY_PROP_WIRELESS_WAKELOCK, &wk_val); + di->epp = idtp9220_get_power_profile(di); + + get_cmdline(di); + if (!di->power_off_mode) + idtp9220_set_reset(di); + else + schedule_delayed_work(&di->irq_work, + msecs_to_jiffies(30)); + } + } +} + +#define DC_LOAD_CURRENT 2000000 +#define DC_FUL_CURRENT 50000 +#define SCREEN_OFF_FUL_CURRENT 220000 +#define DC_LOW_CURRENT 350000 +#define DC_SDP_CURRENT 500000 +#define DC_DCP_CURRENT 500000 +#define DC_PD_CURRENT 800000 +#define DC_MI_CURRENT 2100000 +#define DC_MI_STEP1_CURRENT 1200000 +#define DC_QC3_CURRENT 1400000 +#define DC_QC2_CURRENT 1100000 +#define DC_BPP_CURRENT 850000 +#define DC_BPP_AUTH_FAIL_CURRENT 800000 +#define ICL_EXCHANGE_CURRENT 600000 +#define ICL_EXCHANGE_COUNT 5 /*5 = 1min*/ +#define EXCHANGE_9V 0x0 +#define EXCHANGE_5V 0x1 +#define EXCHANGE_15V 0x0 +#define EXCHANGE_12V 0x1 +#define LIMIT_EPP_IOUT 500 +#define LIMIT_BPP_IOUT 400 +#define LIMIT_SOC 80 +#define TAPER_SOC 95 +#define FULL_SOC 100 +#define TAPER_VOL 4350000 +#define TAPER_CUR -500000 + +static void idtp9220_bpp_connect_load_work(struct work_struct *work) +{ + struct idtp9220_device_info *di = + container_of(work, struct idtp9220_device_info, + bpp_connect_load_work.work); + + idtp922x_set_pmi_icl(di, DC_DCP_CURRENT); + +} + +static void idtp9220_epp_connect_load_work(struct work_struct *work) +{ + + struct idtp9220_device_info *di = + container_of(work, struct idtp9220_device_info, + epp_connect_load_work.work); + int dc_load_curr = 0; + + if (di->power_max <= 10000) + dc_load_curr = (di->power_max - 1000)/5 * 1000; + else + dc_load_curr = DC_LOAD_CURRENT; + + idtp922x_set_pmi_icl(di, dc_load_curr); + +} + +static void idtp9220_cmd_check_work(struct work_struct *work) +{ + struct idtp9220_device_info *di = + container_of(work, struct idtp9220_device_info, + cmd_check_work.work); + + dev_info(di->dev, "[idt] %s: \n", __func__); + idtp922x_get_tx_vin(di); + if (di->power_off_mode) { + schedule_delayed_work(&di->vout_regulator_work, + msecs_to_jiffies(10)); + } + +} + +static void idtp9220_mophie_tx_work(struct work_struct *work) +{ + struct idtp9220_device_info *di = + container_of(work, struct idtp9220_device_info, + mophie_tx_work.work); + int ret = 0; + u8 buf[2] = {0}; + u16 int_val = 0; + + ret = idtp9220_get_power_profile(di); + if (ret) { + ret = di->bus.read_buf(di, REG_TX_TYPE, buf, 2); + if (ret < 0) + dev_err(di->dev, "read tx type error: %d\n", ret); + else { + int_val = buf[0] | (buf[1] << 8); + dev_info(di->dev, "tx type: 0x%04x\n", int_val); + if (int_val == 0x0059) { + dev_info(di->dev, "write vrect target\n"); + di->bus.write(di, 0x0090, 0x0); + di->bus.write(di, 0x0091, 0x8); + di->bus.write(di, 0x003E, 0x41); + } + } + } + +} + +static void idtp9220_bpp_e5_tx_work(struct work_struct *work) +{ + struct idtp9220_device_info *di = + container_of(work, struct idtp9220_device_info, + bpp_e5_tx_work.work); + int icl_curr = 0, vbuck = 0, adapter_vol = 0; + int soc = 0, batt_sts = 0, health = 0; + int icl_setted = 0; + union power_supply_propval val = {0, }; + union power_supply_propval wk_val = {0, }; + + if (di->batt_psy) { + power_supply_get_property(di->batt_psy, + POWER_SUPPLY_PROP_STATUS, &val); + batt_sts = val.intval; + + power_supply_get_property(di->batt_psy, + POWER_SUPPLY_PROP_CAPACITY, &val); + soc = val.intval; + + power_supply_get_property(di->batt_psy, + POWER_SUPPLY_PROP_HEALTH, &val); + health = val.intval; + } + + if (di->tx_charger_type == ADAPTER_QC3) { + adapter_vol = ADAPTER_BPP_QC_VOL; + vbuck = VBUCK_QC_VOL; + icl_curr = DC_QC3_CURRENT; + } else if (di->tx_charger_type == ADAPTER_QC2) { + adapter_vol = ADAPTER_BPP_QC_VOL; + vbuck = VBUCK_QC_VOL; + icl_curr = DC_QC2_CURRENT; + } + + if (di->iout < LIMIT_BPP_IOUT && di->exchange == EXCHANGE_9V) { + di->count_5v++; + di->count_9v = 0; + } else if (di->iout > LIMIT_BPP_IOUT && di->exchange == EXCHANGE_5V) { + di->count_9v++; + di->count_5v = 0; + } else { + di->count_5v = 0; + di->count_9v = 0; + } + /* + * 9V-->5V check 6 times + * 5V-->9v check 3 times + */ + if(di->count_5v > ICL_EXCHANGE_COUNT || + (di->exchange == EXCHANGE_5V && di->count_9v <= ICL_EXCHANGE_COUNT - 3)) + { + dev_info(di->dev, "iout less than 500mA ,set vout to 6.5v\n"); + adapter_vol = ADAPTER_BPP_LIMIT_VOL; + vbuck = VBUCK_DEFAULT_VOL; + icl_curr = DC_BPP_CURRENT; + di->exchange = EXCHANGE_5V; + } else if (di->count_9v > (ICL_EXCHANGE_COUNT - 3)) + di->exchange = EXCHANGE_9V; + + if (soc > 90) + icl_curr = DC_BPP_CURRENT; + + switch (di->status) { + case NORMAL_MODE: + if (soc >= TAPER_SOC) { + di->status = TAPER_MODE; + adapter_vol = ADAPTER_BPP_LIMIT_VOL; + vbuck = VBUCK_DEFAULT_VOL; + icl_curr = min(DC_SDP_CURRENT, icl_curr); + } + break; + case TAPER_MODE: + dev_info (di->dev, "[bpp] taper mode set vout to 6.5v\n"); + adapter_vol = ADAPTER_BPP_LIMIT_VOL; + vbuck = VBUCK_DEFAULT_VOL; + icl_curr = min(DC_SDP_CURRENT, icl_curr); + + if (soc == FULL_SOC && batt_sts == POWER_SUPPLY_STATUS_FULL) + di->status = FULL_MODE; + else if (soc < TAPER_SOC - 1) + di->status = NORMAL_MODE; + break; + case FULL_MODE: + dev_info (di->dev, "[bpp] charge full set vout to 6.5v\n"); + adapter_vol = ADAPTER_BPP_LIMIT_VOL; + vbuck = VBUCK_DEFAULT_VOL; + icl_curr = SCREEN_OFF_FUL_CURRENT; + + if (batt_sts == POWER_SUPPLY_STATUS_CHARGING) { + di->status = RECHG_MODE; + icl_curr = DC_LOW_CURRENT; + } + break; + case RECHG_MODE: + dev_info (di->dev, "[bpp] recharge mode set icl to 300mA\n"); + adapter_vol = ADAPTER_BPP_LIMIT_VOL; + vbuck = VBUCK_DEFAULT_VOL; + icl_curr = DC_LOW_CURRENT; + + if (soc < TAPER_SOC - 1) + di->status = NORMAL_MODE; + else if (batt_sts == POWER_SUPPLY_STATUS_FULL) + di->status = FULL_MODE; + + if (di->wireless_psy) { + wk_val.intval = 1; + power_supply_set_property(di->wireless_psy, + POWER_SUPPLY_PROP_WIRELESS_WAKELOCK, &wk_val); + } + break; + default: + break; + } + + switch(health) { + case POWER_SUPPLY_HEALTH_GOOD: + break; + case POWER_SUPPLY_HEALTH_COOL: + break; + case POWER_SUPPLY_HEALTH_WARM: + adapter_vol = ADAPTER_BPP_LIMIT_VOL; + vbuck = VBUCK_DEFAULT_VOL; + icl_curr = min(DC_SDP_CURRENT, icl_curr); + break; + case POWER_SUPPLY_HEALTH_OVERVOLTAGE: + adapter_vol = ADAPTER_BPP_LIMIT_VOL; + vbuck = VBUCK_DEFAULT_VOL; + icl_curr = min(SCREEN_OFF_FUL_CURRENT, icl_curr); + break; + case POWER_SUPPLY_HEALTH_COLD: + case POWER_SUPPLY_HEALTH_HOT: + adapter_vol = ADAPTER_BPP_LIMIT_VOL; + vbuck = VBUCK_DEFAULT_VOL; + icl_curr = SCREEN_OFF_FUL_CURRENT; + break; + default: + break; + } + + if (adapter_vol != di->last_bpp_vout) { + dev_info(di->dev, "bpp_10w, set new vout: %d, last_vout: %d\n", + adapter_vol, di->last_bpp_vout); + if (adapter_vol > BPP_VOL_THRESHOLD) { + idtp922x_set_adap_vol(di, adapter_vol); + msleep(200); + idtp9220_set_vout_regulator(di, vbuck); + msleep(200); + } else { + idtp9220_set_vout_regulator(di, 5800); + msleep(100); + idtp922x_set_pmi_icl(di, 100000); + msleep(100); + idtp922x_set_adap_vol(di, adapter_vol); + msleep(2000); + idtp922x_set_pmi_icl(di, DC_SDP_CURRENT); + msleep(100); + icl_setted = 1; + } + di->last_bpp_vout = adapter_vol; + } + + if ((icl_curr != di->last_bpp_icl) || (icl_setted)) { + dev_info(di->dev, "bpp_10w, set new icl: %d, last_icl: %d\n", + icl_curr, di->last_bpp_icl); + di->last_bpp_icl = icl_curr; + if ((adapter_vol == ADAPTER_BPP_QC_VOL) && (!di->bpp_vout_rise)) { + dev_info(di->dev, "bpp_10w, vout lower than 8v, set 500mA\n"); + icl_curr = min(DC_SDP_CURRENT, icl_curr); + } + idtp922x_set_pmi_icl(di, icl_curr); + } +} + +static void idtp9220_qc2_f1_tx_work(struct work_struct *work) +{ + struct idtp9220_device_info *di = + container_of(work, struct idtp9220_device_info, + qc2_f1_tx_work.work); + int soc = 0, batt_sts = 0; + int vout = VOUT_VAL_6500_MV; + int dc_icl = DC_QC2_CURRENT; + union power_supply_propval val = {0, }; + + if (di->batt_psy) { + power_supply_get_property(di->batt_psy, + POWER_SUPPLY_PROP_STATUS, &val); + batt_sts = val.intval; + + power_supply_get_property(di->batt_psy, + POWER_SUPPLY_PROP_CAPACITY, &val); + soc = val.intval; + } + + if (vout != di->last_qc2_vout) { + dev_info(di->dev, "qc2+f1_tx, set new vout: %d, last_vout: %d\n", + vout, di->last_qc2_vout); + di->last_qc2_vout = vout; + di->bus.write(di, 0x003E, vout); + msleep(100); + idtp9220_set_vout_regulator(di, VBUCK_DEFAULT_VOL); + msleep(100); + } + + if (dc_icl != di->last_qc2_icl) { + dev_info(di->dev, "qc2+f1_tx, set new icl: %d, last_icl: %d\n", + dc_icl, di->last_qc2_icl); + di->last_qc2_icl = dc_icl; + idtp922x_set_pmi_icl(di, dc_icl); + } +} + +static void idtp9220_qc3_epp_work(struct work_struct *work) +{ + struct idtp9220_device_info *di = + container_of(work, struct idtp9220_device_info, + qc3_epp_work.work); + int adapter_vol = 0; + int dc_icl = DC_MI_STEP1_CURRENT; + + if (adapter_vol != di->last_qc3_vout) { + dev_info(di->dev, "qc3 epp, set new vout: %d, last_vout: %d\n", + adapter_vol, di->last_qc3_vout); + di->last_qc3_vout = adapter_vol; + idtp922x_set_adap_vol(di, adapter_vol); + msleep(200); + } + + if (dc_icl != di->last_qc3_icl) { + dev_info(di->dev, "qc3_epp, set new icl: %d, last_icl: %d\n", + dc_icl, di->last_qc3_icl); + di->last_qc3_icl = dc_icl; + if (dc_icl == DC_MI_STEP1_CURRENT) { + schedule_delayed_work(&di->vout_regulator_work, + msecs_to_jiffies(0)); + } + idtp922x_set_pmi_icl(di, dc_icl); + } +} + +static void idtp9220_vout_regulator_work(struct work_struct *work) +{ + struct idtp9220_device_info *di = + container_of(work, struct idtp9220_device_info, + vout_regulator_work.work); + + union power_supply_propval val = {0, }; + int ret; + int vinc = 0; + int soc = 0; + + if (di->batt_psy) { + power_supply_get_property(di->batt_psy, + POWER_SUPPLY_PROP_CAPACITY, &val); + soc = val.intval; + } + + if(di) + idtp9220_get_vout(di); + else + return; + + if(di->epp) + vinc = (di->vout > EPP_VOL_THRESHOLD) ? 1 : 0; + else + vinc = (di->vout > BPP_VOL_THRESHOLD) ? 1 : 0; + + if (vinc && !di->epp) { + di->bpp_vout_rise = 1; + goto out; + } else if (!vinc && !di->epp) { + di->bpp_vout_rise = 0; + goto out; + } + + if(di->epp && (vinc || di->is_epp_qc3 || di->is_vin_limit) && soc < 95) { + ret = idtp9220_get_property_names(di); + if (di->wireless_psy) { + val.intval = 1; + power_supply_set_property(di->wireless_psy, POWER_SUPPLY_PROP_WIRELESS_CP_EN, &val); + msleep(200); + if(!di->is_epp_qc3 && !di->is_vin_limit) + idtp922x_set_pmi_icl(di, DC_MI_CURRENT); + } else + dev_err(di->dev, "[idt] no wireless psy\n"); + } +out: + dev_info(di->dev, "[idt] %s: epp=%d vbuck=%d vout= %d\n", __func__, di->epp, di->vbuck, di->vout); + return; +} + +static void idtp9220_set_charging_param(struct idtp9220_device_info *di) +{ + int soc = 0, health = 0, batt_sts = 0; + int adapter_vol = 0, icl_curr = 0; + int cur_now = 0, vol_now = 0, vin_inc = 0; + union power_supply_propval val = {0, }; + + switch (di->tx_charger_type) { + case ADAPTER_QC2: + adapter_vol = ADAPTER_BPP_QC_VOL; + di->vbuck = VBUCK_QC_VOL; + icl_curr = DC_QC2_CURRENT; + break; + case ADAPTER_QC3: + if(!di->epp) { + adapter_vol = ADAPTER_BPP_QC_VOL; + di->vbuck = VBUCK_QC_VOL; + icl_curr = DC_QC3_CURRENT; + } else { + di->is_epp_qc3 = 1; + icl_curr = DC_MI_STEP1_CURRENT; + } + break; + case ADAPTER_XIAOMI_PD: + case ADAPTER_PD: + if(di->epp) { + di->vbuck = VBUCK_QC_VOL; + icl_curr = DC_QC3_CURRENT; + } else { + di->vbuck = VBUCK_DEFAULT_VOL; + icl_curr = DC_PD_CURRENT; + } + break; + case ADAPTER_AUTH_FAILED: + if(di->epp) { + di->vbuck = VBUCK_QC_VOL; + if (di->power_max <= 10000) + icl_curr = ((di->power_max - 1000) / (VBUCK_QC_VOL/1000)) * 1000; + else + icl_curr = DC_QC3_CURRENT; + } else { + di->vbuck = VBUCK_DEFAULT_VOL; + icl_curr = DC_BPP_AUTH_FAIL_CURRENT; + } + break; + case ADAPTER_DCP: + case ADAPTER_CDP: + di->vbuck = VBUCK_DEFAULT_VOL; + if (di->is_compatible_hwid) + icl_curr = DC_BPP_AUTH_FAIL_CURRENT; + else + icl_curr = DC_DCP_CURRENT; + break; + case ADAPTER_SDP: + di->vbuck = VBUCK_DEFAULT_VOL; + icl_curr = DC_SDP_CURRENT; + break; + case ADAPTER_XIAOMI_QC3: + case ADAPTER_ZIMI_CAR_POWER: + case ADAPTER_XIAOMI_PD_40W: + if(di->epp) { + adapter_vol = ADAPTER_EPP_MI_VOL; + icl_curr = DC_MI_STEP1_CURRENT; + } else { + di->vbuck = VBUCK_QC_VOL; + icl_curr = DC_QC3_CURRENT; + } + break; + default: + icl_curr = 0; + break; + } + + power_supply_get_property(di->batt_psy, + POWER_SUPPLY_PROP_STATUS, &val); + batt_sts = val.intval; + + power_supply_get_property(di->batt_psy, + POWER_SUPPLY_PROP_CAPACITY, &val); + soc = val.intval; + + power_supply_get_property(di->batt_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &val); + vol_now = val.intval; + + power_supply_get_property(di->batt_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &val); + cur_now = val.intval; + + power_supply_get_property(di->batt_psy, + POWER_SUPPLY_PROP_HEALTH, &val); + health = val.intval; + + idtp9220_get_iout(di); + + if((batt_sts == POWER_SUPPLY_STATUS_DISCHARGING) + && di->dcin_present + && di->power_good_flag) { + dev_info(di->dev, "discharge when dc and pwr present, reset chip\n"); + schedule_delayed_work(&di->fast_operate_work, msecs_to_jiffies(0)); + return; + } + dev_info(di->dev, "[idtp] soc:%d,vol_now:%d,cur_now:%d,health:%d, bat_status:%d\n", + soc, vol_now, cur_now, health, batt_sts); + + /* adapter:qc2/qc3; tx:e5/d5x_10W; + * vout/vbuck/psns is setted in delayed work + */ + if(adapter_vol == ADAPTER_BPP_QC_VOL && di->is_compatible_hwid) { + schedule_delayed_work(&di->bpp_e5_tx_work, msecs_to_jiffies(0)); + goto out; + } + + /* adapter:qc2; tx:F1_27W; + * vout/vbuck/psns is setted in delayed work + */ + if (di->tx_charger_type == ADAPTER_QC2 && di->is_f1_tx) { + schedule_delayed_work(&di->qc2_f1_tx_work, msecs_to_jiffies(0)); + goto out; + } + + /* adapter:qc3_epp; tx:F1_27W; + * vout/vbuck/psns is setted in delayed work + */ + if (di->is_epp_qc3) { + schedule_delayed_work(&di->qc3_epp_work, msecs_to_jiffies(0)); + goto out; + } + + if (adapter_vol > 0 && adapter_vol != di->last_vin) { + idtp922x_set_adap_vol(di, adapter_vol); + vin_inc = 1; + di->last_vin = adapter_vol; + if (adapter_vol == ADAPTER_EPP_MI_VOL || adapter_vol == EPP_VOL_THRESHOLD) { + msleep(100); + idtp922x_set_pmi_icl(di, icl_curr); + di->last_icl = icl_curr; + if (di->is_vin_limit) + schedule_delayed_work(&di->vout_regulator_work, + msecs_to_jiffies(0)); + } + } else { + if (((di->tx_charger_type == ADAPTER_QC2) || (di->tx_charger_type == ADAPTER_QC3)) + && !di->epp && !di->bpp_vout_rise && icl_curr > DC_DCP_CURRENT) + dev_info(di->dev, "don't set curr with qc2/qc3 of bpp when vout low\n"); + else { + if (icl_curr > 0 && icl_curr != di->last_icl) { + idtp922x_set_pmi_icl(di, icl_curr); + di->last_icl = icl_curr; + } + msleep(150); + if (di->vbuck > 0 && di->vbuck != di->last_vbuck) { + idtp9220_set_vout_regulator(di, di->vbuck); + di->last_vbuck = di->vbuck; + } + } + } + + printk("[idtp] di->status:0x%x,adapter_vol =%d,icl_curr=%d, vbuck=%d, last_vin=%d, last_icl=%d\n", + di->status, adapter_vol, icl_curr, di->vbuck, di->last_vin, di->last_icl); + + if (adapter_vol == ADAPTER_EPP_MI_VOL || + adapter_vol == ADAPTER_BPP_QC_VOL || + adapter_vol == VBUCK_FULL_VOL) { + if (vin_inc && (!di->is_vin_limit && !di->is_epp_qc3)) + schedule_delayed_work(&di->cmd_check_work, + msecs_to_jiffies(8000)); + } + +out: + return; +} + +static void idtp9220_wpc_det_work(struct work_struct *work) +{ + struct idtp9220_device_info *di = + container_of(work, struct idtp9220_device_info, + wpc_det_work.work); + union power_supply_propval val = {0, }; + int ret = 0; + + ret = idtp9220_get_property_names(di); + if (ret < 0) { + dev_err(di->dev, "get property error: %d\n", ret); + return; + } + + if (gpio_is_valid(di->dt_props.wpc_det_gpio)) { + ret = gpio_get_value(di->dt_props.wpc_det_gpio); + if (ret) { + /* check if mophie tx after 100ms */ + schedule_delayed_work(&di->mophie_tx_work, msecs_to_jiffies(100)); + /* check dc present to judge device skewing */ + schedule_delayed_work(&di->dc_check_work, msecs_to_jiffies(2500)); + di->power_good_flag = 1; + val.intval = 1; + idtp9220_int_enable(di); + } + else { + cancel_delayed_work(&di->dc_check_work); + di->power_good_flag = 0; + val.intval = 0; + di->ss = 2; + } + power_supply_set_property(di->wireless_psy, POWER_SUPPLY_PROP_WIRELESS_POWER_GOOD_EN, &val); + } + + return; +} + +static void idtp9220_irq_work(struct work_struct *work) +{ + struct idtp9220_device_info *di = + container_of(work, struct idtp9220_device_info, + irq_work.work); + u8 int_buf[2] = {0}; + u16 int_val = 0; + int rc = 0; + u8 recive_data[5] = {0}; + static int retry = 0; + static int retry_id = 0; + static int retry_count = 0; + int tx_vin = 0; + int irq_level; + + if (gpio_is_valid(di->dt_props.irq_gpio)) + irq_level = gpio_get_value(di->dt_props.irq_gpio); + else { + dev_err(di->dev, "%s: irq gpio not provided\n", __func__); + irq_level = -1; + return; + } + if(irq_level) { + return; + } + + rc = di->bus.read_buf(di, REG_INTR_L, int_buf, 2); + if (rc < 0) { + dev_err(di->dev, "[idt]read int state error: %d\n", rc); + goto out; + } + /* clear int and enable irq immediately when read int register*/ + idtp922x_clrInt(di, int_buf, 2); + + int_val = int_buf[0] | (int_buf[1] << 8); + dev_info(di->dev, "[idt] int: 0x%04x\n", int_val); + + msleep(5); + + if (need_irq_cleared(di)) + { + u8 clr_buf[2] = {0xFF, 0xFF}; + idtp922x_clrInt(di, clr_buf, 2); + msleep(5); + } + if ((int_val & INT_OV_CURR) || (int_val & INT_OV_TEMP)) { + dev_info(di->dev, "[idt] ocp or otp found \n"); + schedule_delayed_work(&di->chg_monitor_work, + msecs_to_jiffies(0)); + goto out; + } + + if(int_val & INT_VOUT_ON) { + di->epp = idtp9220_get_power_profile(di); + if(di->epp) { + di->power_max = idtp9220_get_power_max(di); + schedule_delayed_work(&di->epp_connect_load_work, + msecs_to_jiffies(0)); + } else + schedule_delayed_work(&di->bpp_connect_load_work, + msecs_to_jiffies(0)); + if (int_val & INT_IDAUTH_SUCESS) + idtp9220_send_device_auth(di); + goto out; + } + + if (int_val & INT_VSWITCH_SUCESS) { + schedule_delayed_work(&di->vout_regulator_work, + msecs_to_jiffies(50)); + cancel_delayed_work(&di->cmd_check_work); + goto out; + } + + if (int_val & INT_IDAUTH_SUCESS) { + idtp9220_send_device_auth(di); + //idtp922x_request_adapter(di); + goto out; + } + + + if(int_val & INT_AUTH_SUCESS) { + idtp922x_request_uuid(di, di->epp); + goto out; + } +/* + idtp9220_get_signal_strength(di); + di->tx_charger_type = ADAPTER_QC3; + schedule_delayed_work(&di->chg_monitor_work, + msecs_to_jiffies(0)); + goto out; +*/ + + if ((int_val & INT_IDAUTH_FAIL) || (int_val & INT_AUTH_FAIL) || int_val == 0) { + if(((int_val & INT_AUTH_FAIL) || (int_val == 0)) && (retry < 5)){ + idtp9220_send_device_auth(di); + retry++; + dev_info(di->dev, "[idtp] dev auth failed retry %d\n", retry); + goto out; + } else if ((int_val & INT_IDAUTH_FAIL) && retry_id < 5) { + idtp9220_retry_id_auth(di); + retry_id++; + dev_info(di->dev, "[idtp] id auth failed retry %d\n", retry); + goto out; + } else { + retry = 0; + retry_id = 0; + } + di->tx_charger_type = ADAPTER_AUTH_FAILED; + + schedule_delayed_work(&di->rx_vout_work, + msecs_to_jiffies(0)); + schedule_delayed_work(&di->chg_monitor_work, + msecs_to_jiffies(0)); + goto out; + } else + retry = 0; +/* + if (int_val & INT_IDAUTH_FAIL) { + idtp922x_request_adapter(di); + goto out; + } +*/ + + if (int_val & INT_SEND_TIMEOUT) { + if (retry_count < 3) { + dev_info(di->dev, "timeout retry %d\n", retry_count); + idtp922x_retry_cmd(di); + retry_count++; + goto out; + } else { + dev_err(di->dev, "%s: retry failed\n", __func__); + di->tx_charger_type = ADAPTER_AUTH_FAILED; + schedule_delayed_work(&di->rx_vout_work, + msecs_to_jiffies(0)); + schedule_delayed_work(&di->chg_monitor_work, + msecs_to_jiffies(0)); + retry_count = 0; + goto out; + } + } else { + retry_count = 0; + + } + + if (int_val & INT_TX_DATA_RECV) { + idtp922x_receivePkt(di, recive_data); + dev_info(di->dev, "[idt] cmd: %x\n", recive_data[0]); + + switch (recive_data[0]) { + case BC_TX_HWID: + if(recive_data[4] == 0x01 && + recive_data[2] == 0x2 && + recive_data[3] == 0x8 && + recive_data[1] == 0x6) + { + di->is_car_tx = 1; + } + idtp922x_request_adapter(di); + break; + case BC_TX_COMPATIBLE_HWID: + if(recive_data[1] == 0x12 && recive_data[2]) + di->is_compatible_hwid = 1; + if(recive_data[1] == 0x16 && recive_data[2] == 0x11) + di->is_f1_tx = 1; + idtp922x_request_adapter(di); + break; + case BC_ADAPTER_TYPE: + if (di->is_car_tx && (recive_data[1] == ADAPTER_XIAOMI_QC3)) + + di->tx_charger_type = ADAPTER_ZIMI_CAR_POWER; + else + di->tx_charger_type = recive_data[1]; + + if(!di->epp && (di->tx_charger_type == ADAPTER_QC3 || + di->tx_charger_type == ADAPTER_QC2)) { + idtp922x_set_adap_vol(di, ADAPTER_BPP_VOL); + } + schedule_delayed_work(&di->rx_vout_work, + msecs_to_jiffies(100)); + schedule_delayed_work(&di->chg_monitor_work, + msecs_to_jiffies(1000)); + if (di->wireless_psy) + power_supply_changed(di->wireless_psy); + break; + case BC_READ_Vin: + tx_vin = recive_data[1] | (recive_data[2] << 8); + if(!di->power_off_mode) { + if (di->epp) + idtp922x_set_adap_vol(di, ADAPTER_EPP_MI_VOL); + else + idtp922x_set_adap_vol(di, ADAPTER_BPP_QC_VOL); + } + break; + case BC_RX_ID_AUTH: + idtp9220_send_device_auth(di); + break; + default: + dev_info(di->dev, "[idt] unsupport cmd: %x\n", recive_data[0]); + break; + } + } + +out: + return; +} + +static irqreturn_t idtp9220_wpc_det_irq_handler(int irq, void *dev_id) +{ + struct idtp9220_device_info *di = dev_id; + + schedule_delayed_work(&di->wpc_det_work, msecs_to_jiffies(0)); + + return IRQ_HANDLED; +} + +static irqreturn_t idtp9220_irq_handler(int irq, void *dev_id) +{ + + struct idtp9220_device_info *di = dev_id; + + schedule_delayed_work(&di->irq_work, msecs_to_jiffies(30)); + + return IRQ_HANDLED; +} + +static int idtp9220_irq_request(struct idtp9220_device_info *di) +{ + int ret = 0; + + if (!di->irq) { + dev_err(di->dev, "%s: irq is wrong\n", __func__); + return -EINVAL; + } + + ret = request_irq(di->irq, idtp9220_irq_handler, + IRQF_TRIGGER_FALLING, di->name, di); + if (ret) { + dev_err(di->dev, "%s: request_irq failed\n", __func__); + return ret; + } + + ret = enable_irq_wake(di->irq); + if(ret) { + dev_err(di->dev, "%s: enable_irq_wake failed\n", __func__); + return ret; + } + + if (!di->wpc_irq) { + dev_err(di->dev, "%s: wpc irq is wrong\n", __func__); + return -EINVAL; + } + + ret = request_irq(di->wpc_irq, idtp9220_wpc_det_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "wpc_det", di); + if (ret) { + dev_err(di->dev, "%s: wpc request_irq failed\n", __func__); + return ret; + } + + ret = enable_irq_wake(di->wpc_irq); + if(ret) { + dev_err(di->dev, "%s: enable_irq_wake failed\n", __func__); + return ret; + } + + return 0; +} + + +static struct regmap_config i2c_idtp9220_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = 0xFFFF, +}; + +/* +static int idtp9220_get_version(struct idtp9220_device_info *di) +{ + int id_val = 0; + u8 chip_id[2] = {0}; + + di->bus.read_buf(di, REG_CHIP_ID_L, chip_id, 2); + id_val = (chip_id[1] << 8) | chip_id[0]; + + return id_val; +} +*/ + +static enum power_supply_property idtp9220_props[] = { + POWER_SUPPLY_PROP_PIN_ENABLED, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_WIRELESS_VERSION, + POWER_SUPPLY_PROP_SIGNAL_STRENGTH, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, + POWER_SUPPLY_PROP_TX_ADAPTER, +}; + +static int idtp9220_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct idtp9220_device_info *di = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_PIN_ENABLED: + val->intval = gpio_get_value(di->dt_props.enable_gpio); + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = di->dcin_present; + break; + case POWER_SUPPLY_PROP_WIRELESS_VERSION: + val->intval = di->epp; + break; + case POWER_SUPPLY_PROP_SIGNAL_STRENGTH: + val->intval = di->ss; + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION: + val->intval = idtp9220_get_vout_regulator(di); + break; + case POWER_SUPPLY_PROP_TX_ADAPTER: + val->intval = di->tx_charger_type; + break; + default: + return -EINVAL; + } + return 0; +} + +static int idtp9220_set_prop(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct idtp9220_device_info *di = power_supply_get_drvdata(psy); + int rc = 0; + + int data; + + switch (psp) { + case POWER_SUPPLY_PROP_PIN_ENABLED: + rc = idtp9220_set_enable_mode(di, val->intval); + break; + case POWER_SUPPLY_PROP_PRESENT: + rc = idtp9220_set_present(di, val->intval); + break; + case POWER_SUPPLY_PROP_SIGNAL_STRENGTH: + di->ss = val->intval; + power_supply_changed(di->idtp_psy); + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION: + data = val->intval/1000; + if(data >= 6000) { + idtp9220_set_vout_regulator(di, data); + dev_info(di->dev, "[idt] set buck %s\n", __func__); + } + break; + default: + return -EINVAL; + } + + return rc; +} + +static int idtp9220_prop_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + int rc; + + switch (psp) { + case POWER_SUPPLY_PROP_PIN_ENABLED: + case POWER_SUPPLY_PROP_PRESENT: + case POWER_SUPPLY_PROP_SIGNAL_STRENGTH: + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION: + return 1; + default: + rc = 0; + break; + } + + return rc; +} + +static const struct power_supply_desc idtp_psy_desc = { + .name = "idt", + .type = POWER_SUPPLY_TYPE_WIRELESS, + .properties = idtp9220_props, + .num_properties = ARRAY_SIZE(idtp9220_props), + .get_property = idtp9220_get_prop, + .set_property = idtp9220_set_prop, + .property_is_writeable = idtp9220_prop_is_writeable, +}; + +static int idtp9220_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct idtp9220_device_info *di; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct power_supply_config idtp_cfg = {}; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) { + dev_err(&client->dev, "i2c check functionality failed!\n"); + return -EIO; + } + + di = devm_kzalloc(&client->dev, sizeof(*di), GFP_KERNEL); + if (!di) { + dev_err(&client->dev, "i2c allocated device info data failed!\n"); + return -ENOMEM; + } + + di->name = IDT_DRIVER_NAME; + di->dev = &client->dev; + di->chip_enable = 1; + di->ss = 2; + di->status = NORMAL_MODE; + di->regmap = devm_regmap_init_i2c(client, &i2c_idtp9220_regmap_config); + if (!di->regmap) + return -ENODEV; + di->bus.read = idtp9220_read; + di->bus.write = idtp9220_write; + di->bus.read_buf = idtp9220_read_buffer; + di->bus.write_buf = idtp9220_write_buffer; + INIT_DELAYED_WORK(&di->irq_work, idtp9220_irq_work); + INIT_DELAYED_WORK(&di->wpc_det_work, idtp9220_wpc_det_work); + mutex_init(&di->read_lock); + mutex_init(&di->write_lock); + device_init_wakeup(&client->dev, true); + i2c_set_clientdata(client, di); + g_di = di; + + ret = idtp9220_parse_dt(di); + if (ret < 0) { + dev_err(di->dev, "%s: parse dt error [%d]\n", + __func__, ret); + goto cleanup; + } + + ret = idtp9220_gpio_init(di); + if (ret < 0) { + dev_err(di->dev, "%s: gpio init error [%d]\n", + __func__, ret); + goto cleanup; + } + + ret = idtp9220_irq_request(di); + if (ret < 0) { + dev_err(di->dev, "%s: request irq error [%d]\n", + __func__, ret); + goto cleanup; + } + + if(sysfs_create_group(&client->dev.kobj, &sysfs_group_attrs)) { + dev_err(&client->dev, "create sysfs attrs failed!\n"); + ret = -EIO; + goto cleanup; + } + idtp_cfg.drv_data = di; + di->idtp_psy = power_supply_register(di->dev, + &idtp_psy_desc, + &idtp_cfg); + + INIT_DELAYED_WORK(&di->chg_monitor_work,idtp9220_monitor_work); + INIT_DELAYED_WORK(&di->chg_detect_work,idtp9220_chg_detect_work); + INIT_DELAYED_WORK(&di->bpp_connect_load_work,idtp9220_bpp_connect_load_work); + INIT_DELAYED_WORK(&di->epp_connect_load_work,idtp9220_epp_connect_load_work); + INIT_DELAYED_WORK(&di->cmd_check_work,idtp9220_cmd_check_work); + INIT_DELAYED_WORK(&di->vout_regulator_work,idtp9220_vout_regulator_work); + INIT_DELAYED_WORK(&di->rx_vout_work,idtp9220_rx_vout_work); + INIT_DELAYED_WORK(&di->dc_check_work,idtp9220_dc_check_work); + INIT_DELAYED_WORK(&di->fast_operate_work,idtp9220_fast_operate_work); + INIT_DELAYED_WORK(&di->mophie_tx_work, idtp9220_mophie_tx_work); + INIT_DELAYED_WORK(&di->bpp_e5_tx_work, idtp9220_bpp_e5_tx_work); + INIT_DELAYED_WORK(&di->qc2_f1_tx_work, idtp9220_qc2_f1_tx_work); + INIT_DELAYED_WORK(&di->qc3_epp_work, idtp9220_qc3_epp_work); + + + dev_info(di->dev, "[idt] success probe idtp922x driver\n"); + schedule_delayed_work(&di->chg_detect_work, 3 * HZ); + + return 0; + +cleanup: + free_irq(di->irq, di); + cancel_delayed_work_sync(&di->irq_work); + i2c_set_clientdata(client, NULL); + + return ret; +} + +static int idtp9220_remove(struct i2c_client *client) +{ + struct idtp9220_device_info *di = i2c_get_clientdata(client); + + gpio_free(di->dt_props.enable_gpio); + cancel_delayed_work_sync(&di->irq_work); + i2c_set_clientdata(client, NULL); + + return 0; +} + +static void idtp9220_shutdown(struct i2c_client *client) +{ + struct idtp9220_device_info *di = i2c_get_clientdata(client); + + if (di->power_good_flag) + idtp9220_set_reset(di); +} + +static const struct i2c_device_id idtp9220_id[] = { + {IDT_DRIVER_NAME, 0}, + {}, +}; + +static const struct of_device_id idt_match_table[] = { + {.compatible = "idt,p9220"}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, idtp9220_id); + +static struct i2c_driver idtp9220_driver = { + .driver = { + .name = IDT_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = idt_match_table, + }, + .probe = idtp9220_probe, + .remove = idtp9220_remove, + .shutdown = idtp9220_shutdown, + .id_table = idtp9220_id, +}; + +module_i2c_driver(idtp9220_driver); + +MODULE_AUTHOR("bsp-charging@xiaomi.com"); +MODULE_DESCRIPTION("IDTP9220 Wireless Power Charger Monitor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/supply/qcom/idtp9220.h b/drivers/power/supply/qcom/idtp9220.h new file mode 100644 index 000000000000..60cff62f4de7 --- /dev/null +++ b/drivers/power/supply/qcom/idtp9220.h @@ -0,0 +1,1266 @@ +/** + * @file idtp9220.h + * @author + * @date Sun Nov 22 11:49:55 2015 + * + * @brief + * + * + */ +#ifndef __IDTP9220_H__ +#define __IDTP9220_H__ + +#include +#include +#include + +#define IDT_DRIVER_NAME "idtp9220" +#define IDT_I2C_ADDR 0x61 + +#define HSCLK 60000 + +#define ADJUST_METE_MV 35 +#define IDTP9220_DELAY 2000 +#define CHARGING_FULL 100 +#define CHARGING_NEED 95 + +/* status low regiter bits define */ +#define STATUS_VOUT_ON (1 << 7) +#define STATUS_VOUT_OFF (1 << 6) +#define STATUS_TX_DATA_RECV (1 << 4) +#define STATUS_OV_TEMP (1 << 2) +#define STATUS_OV_VOL (1 << 1) +#define STATUS_OV_CURR (1 << 0) + +#define OVER_EVENT_OCCUR (STATUS_OV_TEMP | STATUS_OV_VOL | STATUS_OV_CURR) +#define TOGGLE_LDO_ON_OFF (1 << 1) + +/* interrupt register bits define */ +#define INT_IDAUTH_SUCESS (1 << 13) +#define INT_IDAUTH_FAIL (1 << 12) +#define INT_SEND_SUCESS (1 << 11) +#define INT_SEND_TIMEOUT (1 << 10) +#define INT_AUTH_SUCESS (1 << 9) +#define INT_AUTH_FAIL (1 << 8) +#define INT_VOUT_OFF (1 << 7) +#define INT_VOUT_ON (1 << 6) +#define INT_MODE_CHANGE (1 << 5) +#define INT_TX_DATA_RECV (1 << 4) +#define INT_VSWITCH_SUCESS (1 << 3) +#define INT_OV_TEMP (1 << 2) +#define INT_OV_VOL (1 << 1) +#define INT_OV_CURR (1 << 0) + +/* used registers define */ +#define REG_CHIP_ID_L 0x0000 +#define REG_CHIP_ID_H 0x0001 +#define REG_CHIP_REV 0x0002 +#define REG_CTM_ID 0x0003 +#define REG_OTPFWVER_ADDR 0x0004 // OTP firmware version +#define REG_EPRFWVER_ADDR 0x001c // EEPROM firmware version +#define REG_STATUS_L 0x0034 +#define REG_STATUS_H 0x0035 +#define REG_INTR_L 0x0036 +#define REG_INTR_H 0x0037 +#define REG_INTR_EN_L 0x0038 +#define REG_INTR_EN_H 0x0039 +#define REG_CHG_STATUS 0x003A +#define REG_ADC_VOUT_L 0x003C +#define REG_ADC_VOUT_H 0x003D +#define REG_VOUT_SET 0x003E +#define REG_VRECT_ADJ 0x003F +#define REG_ADC_VRECT 0x0040 +#define REG_RX_LOUT_L 0x0044 +#define REG_RX_LOUT_H 0x0045 +#define REG_FREQ_ADDR 0x0048 // Operating Frequency, Fop(KHz) = 64 * 6000 /value * 256) +#define REG_ILIM_SET 0x004A +#define REG_SIGNAL_STRENGTH 0x004B +#define REG_WPC_MODE 0x004D +#define REG_SSCMND 0x004e // Command Register, COM (0x4E) +#define REG_RX_RESET 0x004F +#define REG_PROPPKT 0x0050 // Proprietary Packet Header Register, PPP_Header (0x50) +#define REG_PPPDATA 0x0051 // PPP Data Value Register(0X51, 0x52, 0x53, 0x54, 0x55) +#define REG_SSINTCLR 0x0056 // Interrupt Clear Registers, INT_Clear_L (0x56) +#define REG_BCHEADER 0x0058 // Back Channel Packet Register (0x58) +#define REG_BCDATA 0x0059 // Back Channel Packet Register (0x59, 0x5A, 0x5B, 0x5C) +#define REG_FC_VOLTAGE_L 0x0078 // Fast Charging Voltage Register +#define REG_FC_VOLTAGE_H 0x0079 +#define REG_REGULATOR_L 0x000C +#define REG_REGULATOR_H 0x000d +#define REG_POWER_MAX 0x0084 //Get the TX power on EPP mode. +#define REG_TX_TYPE 0x00A2 //Get the TX type. + +// RX -> TX +#define PROPRIETARY18 0x18 +#define PROPRIETARY28 0x28 +#define PROPRIETARY38 0x38 +#define PROPRIETARY48 0x48 +#define PROPRIETARY58 0x58 + +// bitmap for customer command +#define BC_NONE 0x00 +#define BC_SET_FREQ 0x03 +#define BC_GET_FREQ 0x04 +#define BC_READ_FW_VER 0x05 +#define BC_READ_Iin 0x06 +#define BC_READ_Vin 0x07 +#define BC_SET_Vin 0x0a + +#define BC_ADAPTER_TYPE 0x0b +#define BC_RESET 0x0c +#define BC_READ_I2C 0x0d +#define BC_WRITE_I2C 0x0e +#define BC_VI2C_INIT 0x10 +#define BC_RX_ID_AUTH 0x3b +#define BC_TX_COMPATIBLE_HWID 0x3f +#define BC_TX_HWID 0x4c +#define BC_TX_TOGGLE 0xc4 + +//Factory test command +#define BC_READ_IOUT 0x12 +#define BC_READ_VOUT 0x13 +#define BC_START_CHARGE 0x30 +#define BC_SET_AP_OVERLOAD 0x31 +#define BC_ENABLE_FAST_CHARGE 0x32 + +/* Adapter_list = {0x00:'ADAPTER_UNKNOWN', */ +/* 0x01:'SDP 500mA', */ +/* 0x02:'CDP 1.1A', */ +/* 0x03:'DCP 1.5A', */ +/* 0x05:'QC2.0', */ +/* 0x06:'QC3.0', */ +/* 0x07:'PD',} */ +//define adapter type +#define ADAPTER_NONE 0x00 +#define ADAPTER_SDP 0x01 +#define ADAPTER_CDP 0x02 +#define ADAPTER_DCP 0x03 +#define ADAPTER_QC2 0x05 +#define ADAPTER_QC3 0x06 +#define ADAPTER_PD 0x07 +#define ADAPTER_AUTH_FAILED 0x08 +#define ADAPTER_XIAOMI_QC3 0x09 +#define ADAPTER_XIAOMI_PD 0x0a +#define ADAPTER_ZIMI_CAR_POWER 0x0b +#define ADAPTER_XIAOMI_PD_40W 0x0c + +#define NORMAL_MODE 0x1 +#define TAPER_MODE 0x2 +#define FULL_MODE 0x3 +#define RECHG_MODE 0x4 + +// bitmap for status flags +// 1: indicates a pending interrupt for LDO Vout state change – from OFF to ON +#define VOUTCHANGED BIT(7) // Stat_Vout_ON +// 1: indicates a pending interrupt for TX Data Received. (Change from “No Received Data” state to “Data Received” state) +#define TXDATARCVD BIT(4) // TX Data Received + +// bitmap for SSCmnd register 0x4e +#define VSWITCH BIT(7) +// If AP sets this bit to "1" then IDTP9220 M0 clears the interrupt corresponding to the bit(s) which has a value of “1” +#define CLRINT BIT(5) // Clear Interrupt +// If AP sets this bit to "1" then IDTP9220 M0 toggles LDO output once (from on to off, or from off to on), and then sets this bit to “0” +#define LDOTGL BIT(1) // Toggle LDO On/OFF +// If AP sets this bit to “1” then IDTP9220 M0 sends the Proprietary Packet +#define SENDPROPP BIT(0) // SEND RX Data + +#define SEND_DEVICE_AUTH BIT(2) + +enum VOUT_SET_VAL { + VOUT_VAL_3500_MV = 0, + VOUT_VAL_3600_MV, + VOUT_VAL_3700_MV, + VOUT_VAL_3800_MV, + VOUT_VAL_3900_MV, + VOUT_VAL_4000_MV, + VOUT_VAL_4100_MV, + VOUT_VAL_4200_MV, + VOUT_VAL_4300_MV, + VOUT_VAL_4400_MV, + VOUT_VAL_4500_MV, + VOUT_VAL_4600_MV, + VOUT_VAL_4700_MV, + VOUT_VAL_4800_MV, + VOUT_VAL_4900_MV, + VOUT_VAL_5000_MV, + VOUT_VAL_5100_MV, + VOUT_VAL_5200_MV, + VOUT_VAL_5300_MV, + VOUT_VAL_5400_MV, + VOUT_VAL_5500_MV, + VOUT_VAL_5600_MV, + VOUT_VAL_5700_MV, + VOUT_VAL_5800_MV, + VOUT_VAL_5900_MV, + VOUT_VAL_6000_MV, + VOUT_VAL_6100_MV, + VOUT_VAL_6200_MV, + VOUT_VAL_6300_MV, + VOUT_VAL_6400_MV, + VOUT_VAL_6500_MV, + VOUT_VAL_6600_MV, + VOUT_VAL_6700_MV, + VOUT_VAL_6800_MV, + VOUT_VAL_6900_MV, + VOUT_VAL_7000_MV, + VOUT_VAL_7100_MV, + VOUT_VAL_7200_MV, + VOUT_VAL_7300_MV, + VOUT_VAL_7400_MV, + VOUT_VAL_7500_MV, + VOUT_VAL_7600_MV, + VOUT_VAL_7700_MV, + VOUT_VAL_7800_MV, + VOUT_VAL_7900_MV, + VOUT_VAL_8000_MV, +}; + +enum IMIL_SET_VAL { + CURR_VAL_100_MA = 0, + CURR_VAL_200_MA, + CURR_VAL_300_MA, + CURR_VAL_400_MA, + CURR_VAL_500_MA, + CURR_VAL_600_MA, + CURR_VAL_700_MA, + CURR_VAL_800_MA, + CURR_VAL_900_MA, + CURR_VAL_1000_MA, + CURR_VAL_1100_MA, + CURR_VAL_1200_MA, + CURR_VAL_1300_MA, +}; + +struct vol_curr_table { + int index; + char *val; +}; +/* +struct idtp9220_platform_data { + enum VOUT_SET_VAL vout_val_default; + enum IMIL_SET_VAL curr_val_default; + unsigned long gpio_en; +}; +*/ +typedef struct { // write to structure at SRAM address 0x0400 + u16 status; // Read/Write by both 9220 and 9220 host + u16 startAddr; // OTP image address of the current packet + u16 codeLength; // The size of the OTP image data in the current packet + u16 dataChksum; // Checksum of the current packet + u8 dataBuf[128]; // OTP image data of the current packet +}idtp9220_packet_t; + +// proprietary packet type +typedef struct { + u8 header; // The header consists of a single byte that indicates the Packet type. + u8 cmd; // Back channel command + u8 data[4]; // Send data buffer +} ProPkt_Type; + +#if 1 +static char bootloader_data[] = { + 0x00, 0x04, 0x00, 0x20, 0xe3, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, /// 0x10 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /// 0x20 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, /// 0x30 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /// 0x40 + 0xfe, 0xe7, 0x00, 0x00, 0xf0, 0xb5, 0x3e, 0x49, 0x00, 0x20, 0x0a, 0x88, 0x05, 0x46, 0x93, 0x06, /// 0x50 + 0x00, 0xd5, 0x04, 0x20, 0xd2, 0x06, 0x07, 0xd5, 0x8a, 0x78, 0x0b, 0x79, 0x1a, 0x43, 0x92, 0x07, /// 0x60 + 0x02, 0xd1, 0x20, 0x22, 0x10, 0x43, 0x01, 0x25, 0x36, 0x4b, 0x5a, 0x22, 0x1a, 0x74, 0x35, 0x4b, /// 0x70 + 0x20, 0x3b, 0x18, 0x72, 0x02, 0x20, 0x40, 0x1c, 0x20, 0x28, 0xfc, 0xd3, 0x32, 0x4c, 0x00, 0x26, /// 0x80 + 0xa6, 0x81, 0x48, 0x88, 0xe2, 0x13, 0x82, 0x18, 0x00, 0x2d, 0x09, 0xd0, 0x00, 0x20, 0x03, 0xe0, /// 0x90 + 0x45, 0x18, 0xad, 0x68, 0x15, 0x50, 0x00, 0x1d, 0x8d, 0x88, 0x85, 0x42, 0xf8, 0xd8, 0x08, 0xe0, /// 0xa0 + 0x00, 0x20, 0x03, 0xe0, 0x45, 0x18, 0x2d, 0x7a, 0x15, 0x54, 0x40, 0x1c, 0x8d, 0x88, 0x85, 0x42, /// 0xb0 + 0xf8, 0xd8, 0x1e, 0x72, 0x25, 0x48, 0xa0, 0x81, 0x02, 0x20, 0x00, 0x24, 0x23, 0x46, 0x0b, 0xe0, /// 0xc0 + 0x5f, 0x18, 0x3e, 0x7a, 0xd5, 0x5c, 0xae, 0x42, 0x05, 0xd0, 0x3d, 0x72, 0x00, 0x2c, 0x00, 0xd1, /// 0xd0 + 0x4b, 0x80, 0x04, 0x20, 0x64, 0x1c, 0x5b, 0x1c, 0x8d, 0x88, 0x9d, 0x42, 0xf0, 0xd8, 0x8c, 0x80, /// 0xe0 + 0xf0, 0xbd, 0x1c, 0x49, 0x1a, 0x48, 0x08, 0x60, 0x17, 0x48, 0x5a, 0x21, 0x40, 0x38, 0x01, 0x70, /// 0xf0 + 0x01, 0x21, 0x01, 0x71, 0x05, 0x21, 0x01, 0x72, 0x17, 0x49, 0x81, 0x81, 0x08, 0x25, 0x10, 0x4f, /// 0x100 + 0x02, 0x26, 0x38, 0x78, 0x3c, 0x46, 0xc0, 0x07, 0xfb, 0xd0, 0x60, 0x88, 0xa2, 0x88, 0x10, 0x18, /// 0x110 + 0x81, 0xb2, 0x00, 0x20, 0x04, 0xe0, 0x03, 0x19, 0x1b, 0x7a, 0x59, 0x18, 0x89, 0xb2, 0x40, 0x1c, /// 0x120 + 0x82, 0x42, 0xf8, 0xd8, 0xe0, 0x88, 0x88, 0x42, 0x01, 0xd0, 0x3d, 0x80, 0xe9, 0xe7, 0x00, 0x2a, /// 0x130 + 0x03, 0xd0, 0xff, 0xf7, 0x87, 0xff, 0x20, 0x80, 0xe3, 0xe7, 0x3e, 0x80, 0xe1, 0xe7, 0x00, 0x00, /// 0x140 + 0x00, 0x04, 0x00, 0x20, 0x40, 0x5c, 0x00, 0x40, 0x40, 0x30, 0x00, 0x40, 0xff, 0x01, 0x00, 0x00, /// 0x150 + 0xff, 0x0f, 0x00, 0x00, 0x80, 0xe1, 0x00, 0xe0, 0x04, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +unsigned char idt_firmware_otp[] = { /// ver 2.3.1.42 + 0x00, 0x08, 0x00, 0x20, 0xa9, 0x02, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, /// 0x10 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /// 0x20 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, /// 0x30 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /// 0x40 + 0x81, 0x0e, 0x00, 0x00, 0x87, 0x0e, 0x00, 0x00, 0x8d, 0x0e, 0x00, 0x00, 0x93, 0x0e, 0x00, 0x00, /// 0x50 + 0x99, 0x0e, 0x00, 0x00, 0x9f, 0x0e, 0x00, 0x00, 0xa5, 0x0e, 0x00, 0x00, 0xab, 0x0e, 0x00, 0x00, /// 0x60 + 0xb1, 0x0e, 0x00, 0x00, 0xb7, 0x0e, 0x00, 0x00, 0xbd, 0x0e, 0x00, 0x00, 0xc3, 0x0e, 0x00, 0x00, /// 0x70 + 0xfe, 0xe7, 0xfe, 0xe7, 0xfe, 0xe7, 0xfe, 0xe7, 0xfe, 0xe7, 0x00, 0x20, 0x00, 0x68, 0x80, 0xf3, /// 0x80 + 0x08, 0x88, 0x70, 0x47, 0xb6, 0x48, 0x00, 0x21, 0x01, 0x72, 0x41, 0x72, 0x70, 0x47, 0x10, 0xb5, /// 0x90 + 0xb3, 0x4b, 0x1a, 0x7a, 0x5c, 0x7a, 0x51, 0x1c, 0x09, 0x07, 0x09, 0x0f, 0x8c, 0x42, 0x01, 0xd1, /// 0xa0 + 0x01, 0x20, 0x10, 0xbd, 0xaf, 0x4c, 0x92, 0x00, 0x12, 0x19, 0xff, 0x32, 0x01, 0x32, 0x90, 0x60, /// 0xb0 + 0x19, 0x72, 0x00, 0x20, 0x10, 0xbd, 0xab, 0x4e, 0x34, 0x46, 0xff, 0x34, 0x41, 0x34, 0x21, 0x7a, /// 0xc0 + 0x60, 0x7a, 0x81, 0x42, 0x01, 0xd1, 0x30, 0xbf, 0xf9, 0xe7, 0x81, 0x00, 0x89, 0x19, 0xff, 0x31, /// 0xd0 + 0x01, 0x31, 0x8d, 0x68, 0x00, 0x2d, 0x0c, 0xd0, 0x40, 0x1c, 0x00, 0x07, 0x00, 0x0f, 0x60, 0x72, /// 0xe0 + 0xa8, 0x47, 0x00, 0x28, 0xeb, 0xd0, 0x72, 0xb6, 0x28, 0x46, 0xff, 0xf7, 0xd0, 0xff, 0x62, 0xb6, /// 0xf0 + 0xe5, 0xe7, 0xff, 0xf7, 0xc7, 0xff, 0xe2, 0xe7, 0x9c, 0x49, 0x9b, 0x48, 0x08, 0x60, 0xff, 0xf7, /// 0x100 + 0xbc, 0xff, 0x9b, 0x48, 0x5a, 0x21, 0x01, 0x70, 0x00, 0x24, 0x04, 0x71, 0x09, 0x21, 0x01, 0x72, /// 0x110 + 0x98, 0x49, 0x81, 0x81, 0x0a, 0x21, 0x81, 0x82, 0x97, 0x49, 0x01, 0x83, 0x08, 0x21, 0x81, 0x83, /// 0x120 + 0x93, 0x49, 0x02, 0x20, 0x20, 0x31, 0x08, 0x76, 0x94, 0x48, 0x84, 0x80, 0x93, 0x49, 0x94, 0x48, /// 0x130 + 0x08, 0x80, 0x39, 0x20, 0x00, 0x02, 0x08, 0x82, 0x92, 0x48, 0x01, 0x26, 0x06, 0x80, 0x81, 0x27, /// 0x140 + 0x07, 0x80, 0x90, 0x48, 0x90, 0x49, 0x20, 0x30, 0x01, 0x80, 0x90, 0x49, 0x89, 0x78, 0x31, 0x43, /// 0x150 + 0x01, 0x71, 0x8f, 0x49, 0x40, 0x20, 0x08, 0x72, 0x20, 0x20, 0x08, 0x76, 0x0c, 0x77, 0x8c, 0x49, /// 0x160 + 0x20, 0x31, 0x08, 0x70, 0x0c, 0x71, 0x08, 0x73, 0x08, 0x74, 0x8a, 0x48, 0x81, 0xb2, 0x8a, 0x48, /// 0x170 + 0x00, 0xf0, 0xff, 0xfb, 0x30, 0x22, 0x89, 0x49, 0x89, 0x48, 0x00, 0xf0, 0xea, 0xfb, 0x89, 0x4d, /// 0x180 + 0x0e, 0x20, 0xa8, 0x72, 0x07, 0x20, 0xe8, 0x73, 0x4b, 0x20, 0x00, 0x01, 0x00, 0xf0, 0xdc, 0xfb, /// 0x190 + 0x85, 0x48, 0x06, 0x70, 0x07, 0x70, 0x84, 0x49, 0x3b, 0x20, 0x08, 0x71, 0x3f, 0x20, 0x08, 0x81, /// 0x1a0 + 0x21, 0x20, 0x08, 0x70, 0x00, 0xf0, 0xec, 0xfb, 0x00, 0xf0, 0x6f, 0xfb, 0x7f, 0x49, 0x06, 0x20, /// 0x1b0 + 0x08, 0x60, 0x6e, 0x49, 0x80, 0x39, 0x08, 0x60, 0x7d, 0x48, 0x06, 0x80, 0x03, 0x21, 0x01, 0x80, /// 0x1c0 + 0x41, 0x21, 0x01, 0x76, 0x7b, 0x48, 0x00, 0xf0, 0x55, 0xf9, 0x7b, 0x48, 0x04, 0x71, 0x7b, 0x48, /// 0x1d0 + 0x44, 0x70, 0x00, 0xf0, 0xd1, 0xfd, 0x2f, 0x46, 0x60, 0x3f, 0x78, 0x73, 0x00, 0xf0, 0xe7, 0xfa, /// 0x1e0 + 0x75, 0x48, 0x01, 0x7a, 0x89, 0x07, 0xfc, 0xd5, 0x73, 0x49, 0x20, 0x31, 0x0c, 0x70, 0x02, 0x21, /// 0x1f0 + 0x01, 0x72, 0x5b, 0x49, 0x10, 0x22, 0x4c, 0x31, 0x88, 0x18, 0x00, 0xf0, 0xaa, 0xfb, 0x07, 0x22, /// 0x200 + 0x01, 0x21, 0x00, 0x20, 0x00, 0xf0, 0x20, 0xf9, 0x40, 0x03, 0x01, 0x0c, 0x65, 0x48, 0x20, 0x38, /// 0x210 + 0x81, 0x85, 0x78, 0x7b, 0xc0, 0x07, 0x0d, 0xd0, 0x61, 0x48, 0x69, 0x49, 0x81, 0x61, 0x48, 0x20, /// 0x220 + 0xe8, 0x72, 0x00, 0xf0, 0xab, 0xfa, 0x01, 0xf0, 0x07, 0xf9, 0x4d, 0x48, 0x44, 0x30, 0x00, 0xf0, /// 0x230 + 0x21, 0xf9, 0x01, 0xe0, 0x00, 0xf0, 0xe8, 0xfa, 0x62, 0x48, 0x01, 0x7d, 0x59, 0x4e, 0xa0, 0x3e, /// 0x240 + 0x31, 0x70, 0x01, 0x7c, 0x71, 0x70, 0x00, 0x7e, 0xb0, 0x70, 0x02, 0x20, 0xf0, 0x70, 0xff, 0x20, /// 0x250 + 0x02, 0x30, 0x70, 0x60, 0x5c, 0x48, 0xf0, 0x61, 0x0d, 0x20, 0xb8, 0x72, 0x30, 0x46, 0x01, 0x27, /// 0x260 + 0x60, 0x30, 0x87, 0x70, 0x00, 0xf0, 0xa8, 0xfd, 0xa8, 0x73, 0x00, 0xf0, 0x51, 0xf9, 0x57, 0x48, /// 0x270 + 0x07, 0x70, 0x81, 0x21, 0x01, 0x70, 0x0d, 0x21, 0x01, 0x70, 0x54, 0x4a, 0x95, 0x21, 0x20, 0x32, /// 0x280 + 0x11, 0x80, 0xff, 0x21, 0x81, 0x82, 0x03, 0x21, 0x09, 0x02, 0x01, 0x82, 0x04, 0x71, 0x47, 0x49, /// 0x290 + 0x61, 0x20, 0x08, 0x60, 0x35, 0x49, 0x80, 0x39, 0x08, 0x60, 0x4d, 0x48, 0x30, 0x87, 0x40, 0x48, /// 0x2a0 + 0x4c, 0x49, 0x41, 0x60, 0xff, 0xf7, 0x07, 0xff, 0x08, 0x20, 0x00, 0xf0, 0x4d, 0xfb, 0x30, 0x48, /// 0x2b0 + 0x40, 0x30, 0x01, 0x78, 0x49, 0x06, 0xfc, 0xd4, 0xff, 0xf7, 0x1e, 0xff, 0x30, 0xb5, 0x28, 0x4c, /// 0x2c0 + 0x38, 0x4d, 0x40, 0x3c, 0x20, 0x68, 0x40, 0x1c, 0x20, 0x3d, 0x20, 0x60, 0x28, 0x78, 0x80, 0x07, /// 0x2d0 + 0x04, 0xd4, 0x72, 0xb6, 0x60, 0x68, 0xff, 0xf7, 0xda, 0xfe, 0x62, 0xb6, 0x28, 0x78, 0x40, 0x07, /// 0x2e0 + 0x0a, 0xd5, 0x1f, 0x48, 0x20, 0x30, 0x81, 0x7d, 0xc0, 0x7d, 0x81, 0x42, 0x04, 0xd0, 0x72, 0xb6, /// 0x2f0 + 0x39, 0x48, 0xff, 0xf7, 0xcc, 0xfe, 0x62, 0xb6, 0x72, 0xb6, 0xa0, 0x6f, 0xff, 0xf7, 0xc7, 0xfe, /// 0x300 + 0x62, 0xb6, 0x00, 0x20, 0x30, 0xbd, 0x70, 0xb5, 0x00, 0x25, 0x27, 0x4c, 0x08, 0x26, 0x0a, 0xe0, /// 0x310 + 0x20, 0x78, 0x30, 0x43, 0x20, 0x70, 0x6d, 0x1c, 0x0a, 0x2d, 0x01, 0xd3, 0xff, 0xf7, 0xec, 0xfe, /// 0x320 + 0x14, 0x20, 0x00, 0xf0, 0x11, 0xfb, 0x20, 0x78, 0x00, 0x07, 0xf1, 0xd4, 0x15, 0x48, 0x20, 0x30, /// 0x330 + 0x01, 0x88, 0xc9, 0x04, 0x11, 0xd5, 0x0a, 0x49, 0x40, 0x39, 0x0a, 0x68, 0x80, 0x31, 0x09, 0x6e, /// 0x340 + 0x51, 0x1a, 0x02, 0x15, 0x91, 0x42, 0x08, 0xdb, 0x01, 0x88, 0x82, 0x14, 0x91, 0x43, 0x01, 0x80, /// 0x350 + 0x07, 0x48, 0x01, 0x8b, 0x42, 0x14, 0x91, 0x43, 0x01, 0x83, 0x00, 0x20, 0x70, 0xbd, 0x00, 0x00, /// 0x360 + 0x70, 0x02, 0x00, 0x20, 0x30, 0x01, 0x00, 0x20, 0xff, 0x0f, 0x00, 0x00, 0x80, 0xe1, 0x00, 0xe0, /// 0x370 + 0x00, 0x30, 0x00, 0x40, 0x05, 0x1d, 0x00, 0x00, 0x3b, 0x23, 0x00, 0x00, 0x00, 0x34, 0x00, 0x40, /// 0x380 + 0x01, 0x0a, 0x00, 0x00, 0x00, 0x38, 0x00, 0x40, 0x0e, 0x30, 0x00, 0x00, 0x60, 0x47, 0x00, 0x00, /// 0x390 + 0x00, 0x6c, 0x00, 0x40, 0xc8, 0x03, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0xe4, 0x14, 0x00, 0x00, /// 0x3a0 + 0x00, 0x01, 0x00, 0x20, 0xa0, 0x00, 0x00, 0x20, 0x00, 0x3c, 0x00, 0x40, 0x80, 0xe2, 0x00, 0xe0, /// 0x3b0 + 0x00, 0x44, 0x00, 0x40, 0xdc, 0x14, 0x00, 0x00, 0x00, 0x60, 0x00, 0x40, 0x70, 0x03, 0x00, 0x20, /// 0x3c0 + 0xff, 0x0f, 0x00, 0x00, 0x60, 0x58, 0x00, 0x40, 0x46, 0x01, 0x03, 0x02, 0x00, 0x64, 0x00, 0x40, /// 0x3d0 + 0x07, 0x40, 0x00, 0x00, 0x71, 0x10, 0x00, 0x00, 0x0d, 0x0e, 0x00, 0x00, 0x70, 0xb5, 0xf7, 0x49, /// 0x3e0 + 0x8a, 0x88, 0xd2, 0x43, 0x92, 0x07, 0x06, 0xd0, 0xf5, 0x4a, 0x92, 0x7f, 0x32, 0x2a, 0x02, 0xd3, /// 0x3f0 + 0x40, 0x09, 0x00, 0x23, 0x03, 0xe0, 0xc2, 0x00, 0x10, 0x1a, 0xc0, 0x09, 0x01, 0x23, 0x3c, 0x28, /// 0x400 + 0x01, 0xd2, 0x3c, 0x20, 0x02, 0xe0, 0xff, 0x28, 0x00, 0xd9, 0xff, 0x20, 0x0a, 0x8a, 0xed, 0x4c, /// 0x410 + 0x12, 0x0a, 0xc0, 0x1e, 0x82, 0x42, 0x15, 0xd0, 0x00, 0x2b, 0x02, 0xd1, 0x25, 0x79, 0x01, 0x2d, /// 0x420 + 0x03, 0xd0, 0x55, 0x1d, 0xa8, 0x42, 0x00, 0xd9, 0x28, 0x46, 0x0a, 0x8a, 0x40, 0x25, 0x2a, 0x43, /// 0x430 + 0x0a, 0x82, 0x0a, 0x8a, 0x4f, 0x26, 0x32, 0x40, 0x00, 0x02, 0x02, 0x43, 0x0a, 0x82, 0x08, 0x8a, /// 0x440 + 0xa8, 0x43, 0x08, 0x82, 0x23, 0x71, 0x70, 0xbd, 0x70, 0xb5, 0x00, 0x23, 0xde, 0x4d, 0x0b, 0xe0, /// 0x450 + 0x44, 0x00, 0x64, 0x19, 0xff, 0x34, 0x81, 0x34, 0xa4, 0x8b, 0x26, 0x0b, 0x96, 0x42, 0x07, 0xd1, /// 0x460 + 0x24, 0x05, 0x24, 0x0d, 0xe3, 0x18, 0x40, 0x18, 0x08, 0x28, 0xf1, 0xd3, 0x18, 0x46, 0x70, 0xbd, /// 0x470 + 0xd6, 0x48, 0x70, 0xbd, 0x10, 0xb5, 0xd6, 0x4a, 0x00, 0x21, 0x11, 0x70, 0xd1, 0x4b, 0x01, 0x21, /// 0x480 + 0x40, 0x3b, 0x59, 0x70, 0xd3, 0x49, 0x03, 0x88, 0x8b, 0x80, 0x43, 0x88, 0x0b, 0x81, 0x83, 0x88, /// 0x490 + 0x8b, 0x81, 0xc0, 0x88, 0x08, 0x82, 0xb4, 0x20, 0x10, 0x70, 0xca, 0x48, 0xf4, 0x38, 0x10, 0x81, /// 0x4a0 + 0x07, 0x20, 0x90, 0x80, 0xca, 0x48, 0x02, 0x22, 0x20, 0x38, 0x02, 0x72, 0x03, 0x79, 0x72, 0xb6, /// 0x4b0 + 0x13, 0x43, 0x03, 0x71, 0x62, 0xb6, 0x11, 0x20, 0x08, 0x80, 0x10, 0xbd, 0x01, 0x88, 0x42, 0x88, /// 0x4c0 + 0x51, 0x18, 0x82, 0x88, 0xc0, 0x88, 0x51, 0x18, 0x40, 0x18, 0xc3, 0x49, 0x00, 0x22, 0x8a, 0x5e, /// 0x4d0 + 0x80, 0x10, 0x80, 0x1a, 0x00, 0xd5, 0x00, 0x20, 0x70, 0x47, 0xc0, 0x49, 0x4a, 0x14, 0x0b, 0x8b, /// 0x4e0 + 0x00, 0x28, 0x05, 0xd0, 0x93, 0x43, 0x0b, 0x83, 0x03, 0x28, 0x03, 0xd9, 0x00, 0x22, 0x07, 0xe0, /// 0x4f0 + 0x13, 0x43, 0x0b, 0x83, 0x03, 0x21, 0x48, 0x40, 0x01, 0x21, 0x40, 0x03, 0x09, 0x03, 0x42, 0x18, /// 0x500 + 0xb7, 0x48, 0x01, 0x88, 0x07, 0x23, 0x1b, 0x03, 0x99, 0x43, 0x11, 0x43, 0x01, 0x80, 0x70, 0x47, /// 0x510 + 0xf8, 0xb5, 0xb2, 0x48, 0x00, 0x8b, 0x47, 0x26, 0xc0, 0x04, 0x36, 0x02, 0xc1, 0x0e, 0xb5, 0x8a, /// 0x520 + 0x30, 0x8b, 0x4f, 0x1c, 0x2c, 0x1a, 0xaf, 0x48, 0x21, 0x46, 0x78, 0x43, 0x00, 0xf0, 0x9e, 0xff, /// 0x530 + 0xa4, 0x49, 0xc8, 0x22, 0x40, 0x39, 0x00, 0x91, 0x88, 0x87, 0x28, 0x46, 0x21, 0x46, 0x50, 0x43, /// 0x540 + 0x00, 0xf0, 0xaa, 0xff, 0xff, 0x21, 0x91, 0x31, 0x08, 0x1a, 0x00, 0x99, 0xc8, 0x87, 0xf5, 0x8b, /// 0x550 + 0xf0, 0x8a, 0x29, 0x1a, 0xa4, 0x48, 0x78, 0x43, 0x00, 0xf0, 0x88, 0xff, 0x99, 0x4c, 0x80, 0xb2, /// 0x560 + 0x20, 0x80, 0x39, 0x46, 0x15, 0x22, 0x68, 0x43, 0x51, 0x43, 0x00, 0xf0, 0x7f, 0xff, 0x19, 0x21, /// 0x570 + 0x80, 0x09, 0x49, 0x01, 0x08, 0x1a, 0x00, 0xb2, 0x8d, 0x21, 0x48, 0x43, 0xc1, 0x17, 0x09, 0x0e, /// 0x580 + 0x08, 0x18, 0x00, 0x12, 0x60, 0x80, 0xf8, 0xbd, 0x70, 0xb5, 0x05, 0x46, 0x93, 0x48, 0x00, 0x8b, /// 0x590 + 0x8c, 0x4c, 0xc0, 0x04, 0x40, 0x3c, 0x61, 0x7a, 0xc0, 0x0e, 0x81, 0x42, 0x02, 0xd0, 0x60, 0x72, /// 0x5a0 + 0xff, 0xf7, 0xb6, 0xff, 0x91, 0x48, 0x85, 0x42, 0x03, 0xd8, 0x3e, 0x22, 0xa1, 0x8f, 0xa2, 0x5e, /// 0x5b0 + 0x1f, 0xe0, 0x8e, 0x49, 0x83, 0x48, 0x40, 0x31, 0x8d, 0x42, 0x03, 0xd3, 0x02, 0x22, 0x01, 0x88, /// 0x5c0 + 0x82, 0x5e, 0x16, 0xe0, 0x89, 0x49, 0xa3, 0x8f, 0x49, 0x42, 0x6a, 0x18, 0x01, 0x88, 0xc9, 0x1a, /// 0x5d0 + 0x51, 0x43, 0xce, 0x17, 0xb6, 0x0e, 0x71, 0x18, 0x89, 0x11, 0xc9, 0x18, 0x02, 0x26, 0x3e, 0x23, /// 0x5e0 + 0x86, 0x5f, 0xe3, 0x5e, 0xf0, 0x1a, 0x50, 0x43, 0xc2, 0x17, 0x92, 0x0e, 0x10, 0x18, 0x80, 0x11, /// 0x5f0 + 0xc2, 0x18, 0x4d, 0x43, 0x28, 0x0c, 0x80, 0x18, 0x00, 0xd5, 0x00, 0x20, 0x70, 0xbd, 0xf8, 0xb5, /// 0x600 + 0x02, 0x22, 0x04, 0x21, 0x00, 0x20, 0xff, 0xf7, 0x1f, 0xff, 0x6e, 0x4d, 0x6e, 0x4e, 0x20, 0x3d, /// 0x610 + 0xa9, 0x79, 0x09, 0x27, 0x49, 0x00, 0xc0, 0x03, 0x89, 0x19, 0xbf, 0x01, 0x00, 0x0c, 0xc9, 0x19, /// 0x620 + 0x88, 0x81, 0x01, 0x22, 0x04, 0x21, 0x10, 0x46, 0xff, 0xf7, 0x0e, 0xff, 0xa9, 0x79, 0xc0, 0x03, /// 0x630 + 0x49, 0x00, 0x89, 0x19, 0x00, 0x0c, 0xc9, 0x19, 0x88, 0x83, 0x03, 0x22, 0x08, 0x21, 0x10, 0x46, /// 0x640 + 0xff, 0xf7, 0x02, 0xff, 0xa9, 0x79, 0x0f, 0x22, 0x49, 0x00, 0x89, 0x19, 0xc9, 0x19, 0x88, 0x82, /// 0x650 + 0x04, 0x21, 0x02, 0x20, 0xff, 0xf7, 0xf8, 0xfe, 0xc0, 0x03, 0x01, 0x0c, 0xa8, 0x79, 0x40, 0x00, /// 0x660 + 0x82, 0x19, 0x05, 0x20, 0xc0, 0x01, 0x10, 0x18, 0x81, 0x81, 0x04, 0x22, 0x08, 0x21, 0x07, 0x20, /// 0x670 + 0xff, 0xf7, 0xea, 0xfe, 0x52, 0x4c, 0x20, 0x34, 0x23, 0x46, 0xe0, 0x80, 0x40, 0x3b, 0x98, 0x69, /// 0x680 + 0x41, 0x00, 0x58, 0x69, 0xc2, 0x0f, 0x11, 0x43, 0x99, 0x61, 0x41, 0x00, 0xa8, 0x79, 0x42, 0x00, /// 0x690 + 0x92, 0x19, 0xd2, 0x19, 0x12, 0x7b, 0x40, 0x1c, 0xd2, 0x07, 0xd2, 0x0f, 0x11, 0x43, 0x80, 0x07, /// 0x6a0 + 0x80, 0x0f, 0x59, 0x61, 0xa8, 0x71, 0x28, 0x46, 0x14, 0x38, 0xff, 0xf7, 0x07, 0xff, 0x21, 0x46, /// 0x6b0 + 0x60, 0x31, 0x0e, 0x22, 0x8a, 0x56, 0x80, 0x18, 0x20, 0x80, 0x28, 0x1f, 0xff, 0xf7, 0xfe, 0xfe, /// 0x6c0 + 0x60, 0x87, 0x28, 0x46, 0x0c, 0x38, 0xff, 0xf7, 0xf9, 0xfe, 0x98, 0x87, 0x28, 0x46, 0x2c, 0x30, /// 0x6d0 + 0xff, 0xf7, 0xf4, 0xfe, 0x40, 0x08, 0x01, 0x01, 0x40, 0x18, 0x27, 0x46, 0x58, 0x81, 0x40, 0x37, /// 0x6e0 + 0x38, 0x78, 0xc0, 0x06, 0x48, 0xd5, 0x60, 0x8f, 0xff, 0xf7, 0x4e, 0xff, 0xa1, 0x88, 0x08, 0x1a, /// 0x6f0 + 0x00, 0xd5, 0x40, 0x42, 0xc8, 0x28, 0x3f, 0xdd, 0xa8, 0x79, 0x40, 0x1e, 0x80, 0x07, 0x40, 0x0f, /// 0x700 + 0x81, 0x19, 0x09, 0x20, 0x80, 0x01, 0x08, 0x18, 0x2e, 0x49, 0x80, 0x8b, 0x40, 0x39, 0x88, 0x83, /// 0x710 + 0xc8, 0x83, 0x08, 0x84, 0x48, 0x84, 0x60, 0x87, 0xff, 0xf7, 0x36, 0xff, 0xa1, 0x88, 0x05, 0x46, /// 0x720 + 0xa9, 0x42, 0x25, 0xd9, 0x38, 0x78, 0xc0, 0x07, 0x22, 0xd1, 0x40, 0x2d, 0x01, 0xd2, 0xff, 0xf7, /// 0x730 + 0xd4, 0xfe, 0x22, 0x49, 0x80, 0x2d, 0x02, 0xd2, 0x01, 0x20, 0x88, 0x80, 0x03, 0xe0, 0xff, 0x20, /// 0x740 + 0x41, 0x30, 0x85, 0x42, 0x01, 0xd2, 0x00, 0x20, 0x0c, 0xe0, 0xff, 0x20, 0xe1, 0x30, 0x85, 0x42, /// 0x750 + 0x01, 0xd2, 0x01, 0x20, 0x06, 0xe0, 0x05, 0x20, 0xc0, 0x01, 0x85, 0x42, 0x01, 0xd2, 0x02, 0x20, /// 0x760 + 0x00, 0xe0, 0x03, 0x20, 0x0a, 0x88, 0xf0, 0x23, 0x9a, 0x43, 0x80, 0x01, 0x02, 0x43, 0x0a, 0x80, /// 0x770 + 0x1f, 0x48, 0xa5, 0x80, 0x32, 0x21, 0x41, 0x66, 0x00, 0x20, 0xf8, 0xbd, 0x1d, 0x48, 0x01, 0x78, /// 0x780 + 0x42, 0x22, 0x11, 0x43, 0x01, 0x70, 0x1c, 0x49, 0xc0, 0x14, 0x08, 0x60, 0x1b, 0x49, 0x08, 0x60, /// 0x790 + 0x70, 0x47, 0x0b, 0x49, 0x20, 0x39, 0xca, 0x8e, 0x02, 0x43, 0x08, 0x8f, 0x10, 0x40, 0xc8, 0x86, /// 0x7a0 + 0x04, 0xd0, 0x17, 0x48, 0x01, 0x7f, 0xfb, 0x22, 0x11, 0x40, 0x01, 0x77, 0x70, 0x47, 0x15, 0x48, /// 0x7b0 + 0x81, 0x21, 0x01, 0x80, 0x00, 0x21, 0x01, 0x80, 0x70, 0x47, 0x00, 0x00, 0x00, 0x34, 0x00, 0x40, /// 0x7c0 + 0x20, 0x00, 0x00, 0x20, 0xb0, 0x03, 0x00, 0x20, 0x30, 0x01, 0x00, 0x20, 0xff, 0xff, 0x00, 0x00, /// 0x7d0 + 0x20, 0x60, 0x00, 0x40, 0x00, 0x44, 0x00, 0x40, 0x20, 0x47, 0x00, 0x00, 0x00, 0x30, 0x00, 0x40, /// 0x7e0 + 0x20, 0x38, 0x00, 0x40, 0x00, 0x1a, 0x04, 0x00, 0x80, 0x92, 0x08, 0x00, 0xda, 0x02, 0x00, 0x00, /// 0x7f0 + 0xb0, 0x02, 0x00, 0x20, 0x00, 0x3c, 0x00, 0x40, 0x80, 0xe2, 0x00, 0xe0, 0x00, 0xe1, 0x00, 0xe0, /// 0x800 + 0x20, 0x6c, 0x00, 0x40, 0x00, 0x4c, 0x00, 0x40, 0x10, 0xb5, 0xff, 0x49, 0x08, 0x20, 0x88, 0x83, /// 0x810 + 0xfe, 0x48, 0x00, 0x22, 0x82, 0x80, 0xfe, 0x4b, 0x03, 0x80, 0x39, 0x23, 0x1b, 0x02, 0x03, 0x82, /// 0x820 + 0xfc, 0x48, 0x08, 0x83, 0xfc, 0x49, 0x81, 0x20, 0x08, 0x80, 0xfc, 0x48, 0x0e, 0x21, 0x81, 0x72, /// 0x830 + 0x07, 0x21, 0xc1, 0x73, 0xf8, 0x48, 0xfa, 0x49, 0x20, 0x30, 0x01, 0x80, 0xf9, 0x49, 0x01, 0x23, /// 0x840 + 0x89, 0x78, 0x19, 0x43, 0x01, 0x71, 0xf8, 0x49, 0x09, 0x20, 0x08, 0x70, 0xff, 0xf7, 0xaf, 0xff, /// 0x850 + 0xf7, 0x48, 0xf6, 0x49, 0x41, 0x60, 0xf7, 0x49, 0x0b, 0x78, 0xfb, 0x24, 0x23, 0x40, 0x0b, 0x70, /// 0x860 + 0xf5, 0x49, 0x81, 0x67, 0x01, 0x68, 0x80, 0x30, 0x01, 0x66, 0xec, 0x48, 0x60, 0x38, 0x01, 0x46, /// 0x870 + 0x42, 0x73, 0x40, 0x39, 0x88, 0x8e, 0x40, 0x22, 0x90, 0x43, 0x20, 0x22, 0x10, 0x43, 0x88, 0x86, /// 0x880 + 0x10, 0x46, 0xff, 0xf7, 0x86, 0xff, 0x00, 0x20, 0x10, 0xbd, 0xec, 0x48, 0x00, 0x21, 0x01, 0x72, /// 0x890 + 0x41, 0x72, 0xeb, 0x48, 0x01, 0x21, 0x01, 0x80, 0x01, 0x15, 0x81, 0x80, 0x0f, 0x21, 0x01, 0x80, /// 0x8a0 + 0x02, 0x22, 0x02, 0x81, 0x01, 0x83, 0x04, 0x21, 0x81, 0x82, 0x85, 0xe7, 0xe5, 0x48, 0x01, 0x21, /// 0x8b0 + 0x01, 0x80, 0x02, 0x15, 0x82, 0x80, 0x2b, 0x22, 0x02, 0x80, 0x01, 0x81, 0xe1, 0x49, 0x96, 0x22, /// 0x8c0 + 0x20, 0x31, 0x0a, 0x80, 0x7d, 0x22, 0x8a, 0x80, 0x0f, 0x21, 0x01, 0x83, 0x08, 0x21, 0x81, 0x82, /// 0x8d0 + 0x72, 0xe7, 0x00, 0x28, 0x09, 0xd0, 0x00, 0x29, 0x07, 0xd0, 0x81, 0x42, 0x01, 0xd9, 0x41, 0x1e, /// 0x8e0 + 0x89, 0xb2, 0xd8, 0x4a, 0x20, 0x32, 0x91, 0x80, 0x10, 0x80, 0x65, 0xe7, 0x40, 0x1a, 0x10, 0xb5, /// 0x8f0 + 0x00, 0x23, 0x02, 0xb2, 0x00, 0x2a, 0x02, 0xda, 0x01, 0x23, 0x50, 0x42, 0x02, 0xb2, 0xc7, 0x48, /// 0x900 + 0xa0, 0x38, 0xc4, 0x7c, 0x94, 0x42, 0x1e, 0xda, 0x19, 0x24, 0x64, 0x01, 0xa2, 0x42, 0x0b, 0xdc, /// 0x910 + 0x01, 0x46, 0x09, 0x8a, 0x00, 0x2b, 0x01, 0xd0, 0x49, 0x1e, 0x00, 0xe0, 0x49, 0x1c, 0x01, 0x82, /// 0x920 + 0x09, 0x04, 0x09, 0x0c, 0x08, 0xd0, 0x09, 0xe0, 0xff, 0x22, 0x15, 0x32, 0x51, 0x43, 0xc6, 0x4a, /// 0x930 + 0x51, 0x1a, 0x89, 0x00, 0x09, 0x0c, 0xf2, 0xe7, 0x01, 0x21, 0x01, 0x82, 0x8a, 0xb2, 0xc1, 0x89, /// 0x940 + 0x8a, 0x42, 0x00, 0xd9, 0x01, 0x82, 0x10, 0xbd, 0x00, 0xe0, 0x00, 0xbf, 0x40, 0x1e, 0xfc, 0xd2, /// 0x950 + 0x32, 0xe7, 0x82, 0x18, 0x01, 0xe0, 0x08, 0xc9, 0x08, 0xc0, 0x90, 0x42, 0xfb, 0xd3, 0x2b, 0xe7, /// 0x960 + 0x82, 0x18, 0x03, 0xe0, 0x0b, 0x78, 0x03, 0x70, 0x40, 0x1c, 0x49, 0x1c, 0x90, 0x42, 0xf9, 0xd3, /// 0x970 + 0x22, 0xe7, 0x41, 0x18, 0x00, 0x22, 0x00, 0xe0, 0x04, 0xc0, 0x88, 0x42, 0xfc, 0xd3, 0x1b, 0xe7, /// 0x980 + 0xb2, 0x48, 0x01, 0x21, 0x01, 0x70, 0xb1, 0x4a, 0x05, 0x21, 0x40, 0x32, 0x11, 0x80, 0x00, 0x22, /// 0x990 + 0x02, 0x74, 0xae, 0x49, 0x20, 0x31, 0x0a, 0x74, 0xad, 0x4a, 0x8a, 0x81, 0x13, 0x22, 0x0a, 0x70, /// 0x9a0 + 0x07, 0x21, 0x01, 0x72, 0x08, 0xe7, 0x9d, 0x4a, 0xa0, 0x3a, 0x11, 0x46, 0x40, 0x31, 0xd0, 0x8e, /// 0x9b0 + 0xcb, 0x8a, 0x98, 0x43, 0xd0, 0x86, 0x00, 0x22, 0xca, 0x82, 0x00, 0x28, 0x05, 0xd1, 0x9d, 0x48, /// 0x9c0 + 0x20, 0x38, 0x01, 0x7f, 0x04, 0x22, 0x11, 0x43, 0x01, 0x77, 0xf5, 0xe6, 0x8f, 0x48, 0x00, 0x21, /// 0x9d0 + 0x80, 0x88, 0x00, 0x07, 0x00, 0xd5, 0x02, 0x21, 0x8b, 0x48, 0x80, 0x8b, 0xc0, 0x04, 0x01, 0xd5, /// 0x9e0 + 0x01, 0x20, 0x01, 0x43, 0x9b, 0x48, 0x80, 0x89, 0xc0, 0x06, 0x01, 0xd5, 0x04, 0x20, 0x01, 0x43, /// 0x9f0 + 0x8a, 0x4b, 0xa0, 0x3b, 0x9a, 0x8e, 0x50, 0x07, 0x40, 0x0f, 0x48, 0x40, 0x04, 0xd0, 0xd2, 0x08, /// 0xa00 + 0xd2, 0x00, 0x0a, 0x43, 0x9a, 0x86, 0xc4, 0xe6, 0xd6, 0xe6, 0x02, 0x46, 0x00, 0x20, 0x02, 0xe0, /// 0xa10 + 0x13, 0x78, 0x58, 0x40, 0x52, 0x1c, 0x49, 0x1e, 0xfa, 0xd2, 0xcd, 0xe6, 0x08, 0x22, 0x01, 0x21, /// 0xa20 + 0x01, 0xe0, 0x09, 0x18, 0x40, 0x08, 0x52, 0x1e, 0xfb, 0xd2, 0xc8, 0x07, 0xc0, 0x0f, 0xc3, 0xe6, /// 0xa30 + 0xf8, 0xb5, 0x89, 0x4c, 0x00, 0x26, 0x20, 0x46, 0xff, 0x30, 0xa1, 0x30, 0x84, 0x46, 0x0c, 0x30, /// 0xa40 + 0x01, 0x27, 0xff, 0x34, 0x35, 0x46, 0xc1, 0x34, 0x00, 0x90, 0x60, 0x7a, 0x00, 0x28, 0x0e, 0xd1, /// 0xa50 + 0x60, 0x46, 0xa1, 0x7a, 0x00, 0x7b, 0x81, 0x42, 0x06, 0xd2, 0x01, 0x20, 0xb0, 0x40, 0x49, 0x1c, /// 0xa60 + 0x05, 0x43, 0xa1, 0x72, 0xb6, 0x1c, 0x36, 0xe0, 0x00, 0x20, 0xa0, 0x72, 0x67, 0x72, 0x61, 0x7a, /// 0xa70 + 0x20, 0x7a, 0x81, 0x42, 0x1d, 0xd2, 0xa0, 0x7a, 0x00, 0x28, 0x1d, 0xd0, 0x09, 0x28, 0x06, 0xd2, /// 0xa80 + 0x00, 0x9a, 0x40, 0x1e, 0x89, 0x5c, 0xc1, 0x40, 0xc8, 0x07, 0xc0, 0x0f, 0x14, 0xe0, 0x09, 0x28, /// 0xa90 + 0x05, 0xd0, 0xff, 0x22, 0x01, 0x20, 0xa2, 0x72, 0x49, 0x1c, 0x61, 0x72, 0x0c, 0xe0, 0x00, 0x98, /// 0xaa0 + 0x08, 0x5c, 0xff, 0xf7, 0xbb, 0xff, 0x61, 0x46, 0x09, 0x7b, 0x00, 0x29, 0x04, 0xd1, 0x78, 0x40, /// 0xab0 + 0x02, 0xe0, 0x60, 0x7d, 0xa7, 0x75, 0x78, 0x40, 0x61, 0x7d, 0x79, 0x40, 0x48, 0x40, 0x60, 0x75, /// 0xac0 + 0x40, 0x00, 0x08, 0x43, 0xb0, 0x40, 0x05, 0x43, 0xa0, 0x7a, 0xb6, 0x1c, 0x40, 0x1c, 0xa0, 0x72, /// 0xad0 + 0xa0, 0x7d, 0x01, 0x28, 0x01, 0xd0, 0x10, 0x2e, 0xb7, 0xd3, 0x76, 0x1e, 0x26, 0x75, 0x65, 0x82, /// 0xae0 + 0xf8, 0xbd, 0x70, 0xb5, 0x05, 0x46, 0x41, 0x1c, 0x54, 0x48, 0x53, 0x38, 0xff, 0xf7, 0x8d, 0xff, /// 0xaf0 + 0x59, 0x49, 0x52, 0x4c, 0x49, 0x19, 0xff, 0x31, 0xa1, 0x31, 0x88, 0x73, 0x40, 0x3c, 0xed, 0x1c, /// 0xb00 + 0x25, 0x72, 0x20, 0x7b, 0xe0, 0x72, 0x00, 0x20, 0x60, 0x72, 0xa0, 0x72, 0x60, 0x75, 0x02, 0x26, /// 0xb10 + 0xa6, 0x75, 0xff, 0xf7, 0x8d, 0xff, 0x40, 0x48, 0x20, 0x30, 0x01, 0x88, 0xc9, 0x04, 0x61, 0x7b, /// 0xb20 + 0x03, 0xd5, 0x20, 0x22, 0x11, 0x43, 0x61, 0x73, 0x07, 0xe0, 0xdf, 0x22, 0x11, 0x40, 0x61, 0x73, /// 0xb30 + 0x01, 0x88, 0x01, 0x22, 0x12, 0x03, 0x11, 0x43, 0x01, 0x80, 0x34, 0x49, 0x0a, 0x8a, 0x0c, 0x23, /// 0xb40 + 0x1a, 0x43, 0x0a, 0x82, 0x03, 0x88, 0x03, 0x21, 0x09, 0x02, 0x8b, 0x43, 0x33, 0x49, 0x30, 0x25, /// 0xb50 + 0xca, 0x7a, 0x15, 0x40, 0x2d, 0x01, 0x2b, 0x43, 0x03, 0x80, 0x2f, 0x4d, 0x3f, 0x4b, 0xab, 0x80, /// 0xb60 + 0x2e, 0x81, 0x00, 0x23, 0xdb, 0x43, 0xab, 0x81, 0x48, 0x23, 0x1a, 0x40, 0x25, 0x32, 0x2a, 0x80, /// 0xb70 + 0x62, 0x8a, 0x2a, 0x82, 0x22, 0x7d, 0x2a, 0x75, 0xa2, 0x7b, 0x00, 0x2a, 0x14, 0xd1, 0x0a, 0x46, /// 0xb80 + 0x60, 0x3a, 0x33, 0x4b, 0xd2, 0x8f, 0xb4, 0x3b, 0x9a, 0x42, 0x0d, 0xd9, 0xca, 0x7b, 0x1f, 0x2a, /// 0xb90 + 0x01, 0xd9, 0x1f, 0x22, 0xca, 0x73, 0x02, 0x88, 0xc9, 0x7b, 0x52, 0x09, 0x52, 0x01, 0x0a, 0x43, /// 0xba0 + 0x02, 0x80, 0x01, 0x79, 0x31, 0x43, 0x01, 0x71, 0xff, 0xf7, 0x42, 0xff, 0xff, 0x20, 0x00, 0x02, /// 0xbb0 + 0xa8, 0x81, 0x70, 0xbd, 0x70, 0xb5, 0x00, 0x20, 0xff, 0xf7, 0x8f, 0xfc, 0x1d, 0x49, 0x08, 0x78, /// 0xbc0 + 0xdf, 0x22, 0x10, 0x40, 0x08, 0x70, 0x15, 0x48, 0xef, 0x24, 0xa0, 0x38, 0x82, 0x7c, 0x22, 0x40, /// 0xbd0 + 0x82, 0x74, 0x0d, 0x4a, 0x08, 0x23, 0x93, 0x83, 0x1d, 0x4b, 0xac, 0x3b, 0x13, 0x83, 0x39, 0x22, /// 0xbe0 + 0x0a, 0x4b, 0x12, 0x02, 0x1a, 0x82, 0x00, 0x22, 0x9a, 0x80, 0x1d, 0x88, 0xf0, 0x26, 0xb5, 0x43, /// 0xbf0 + 0x1d, 0x80, 0x12, 0x4b, 0xfb, 0x25, 0x40, 0x3b, 0x5e, 0x7b, 0x2e, 0x40, 0x5e, 0x73, 0x0b, 0x78, /// 0xc00 + 0x2b, 0x40, 0x0b, 0x70, 0x82, 0x87, 0x2b, 0xe0, 0x00, 0x30, 0x00, 0x40, 0x00, 0x34, 0x00, 0x40, /// 0xc10 + 0x01, 0x0a, 0x00, 0x00, 0x3b, 0x3b, 0x00, 0x00, 0x00, 0x38, 0x00, 0x40, 0xa0, 0x00, 0x00, 0x20, /// 0xc20 + 0x0e, 0x30, 0x00, 0x00, 0x60, 0x47, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x40, 0x07, 0x03, 0x00, 0x00, /// 0xc30 + 0x30, 0x02, 0x00, 0x20, 0x40, 0x6c, 0x00, 0x40, 0x3d, 0x15, 0x00, 0x00, 0x30, 0x03, 0x00, 0x20, /// 0xc40 + 0x00, 0x4c, 0x00, 0x40, 0x00, 0x50, 0x00, 0x40, 0x55, 0x32, 0x34, 0x00, 0x00, 0x54, 0x00, 0x40, /// 0xc50 + 0xe7, 0x03, 0x00, 0x00, 0x20, 0x48, 0x00, 0x40, 0x30, 0x01, 0x00, 0x20, 0xdb, 0x05, 0x00, 0x00, /// 0xc60 + 0x01, 0x46, 0x40, 0x31, 0x8a, 0x87, 0x8a, 0x80, 0x40, 0x31, 0x0a, 0x78, 0x22, 0x40, 0x0a, 0x70, /// 0xc70 + 0x81, 0x8e, 0x80, 0x22, 0x11, 0x43, 0x81, 0x86, 0x10, 0x46, 0xff, 0xf7, 0x8a, 0xfd, 0x70, 0xbd, /// 0xc80 + 0x51, 0x4a, 0x10, 0x8b, 0xc0, 0x04, 0xc1, 0x0e, 0x50, 0x48, 0x80, 0x7a, 0x40, 0x00, 0x40, 0x1c, /// 0xc90 + 0x1f, 0x28, 0x00, 0xdd, 0x1f, 0x20, 0x81, 0x42, 0x00, 0xd2, 0x48, 0x1c, 0x11, 0x8b, 0x1f, 0x23, /// 0xca0 + 0x1b, 0x02, 0x99, 0x43, 0x00, 0x02, 0x01, 0x43, 0x11, 0x83, 0x49, 0x49, 0x08, 0x88, 0x07, 0x22, /// 0xcb0 + 0x12, 0x02, 0x90, 0x43, 0x45, 0x4a, 0x20, 0x32, 0x92, 0x78, 0x52, 0x07, 0x52, 0x0d, 0x10, 0x43, /// 0xcc0 + 0x08, 0x80, 0x42, 0x48, 0x60, 0x30, 0x81, 0x7a, 0x1f, 0x29, 0x01, 0xd9, 0x1f, 0x21, 0x81, 0x72, /// 0xcd0 + 0x40, 0x49, 0x0a, 0x88, 0x80, 0x7a, 0x52, 0x09, 0x52, 0x01, 0x02, 0x43, 0x0a, 0x80, 0x6b, 0xe5, /// 0xce0 + 0xf8, 0xb5, 0x41, 0x09, 0x3c, 0x4a, 0x00, 0x29, 0x0a, 0xd0, 0x20, 0x29, 0x01, 0xdb, 0x1f, 0x21, /// 0xcf0 + 0x06, 0xe0, 0x93, 0x7b, 0x8b, 0x42, 0x01, 0xda, 0x49, 0x1e, 0x01, 0xe0, 0x8b, 0x42, 0x00, 0xdd, /// 0xd00 + 0x91, 0x73, 0x47, 0x21, 0x09, 0x02, 0x0e, 0x22, 0x8a, 0x56, 0x30, 0x49, 0x20, 0x39, 0x8e, 0x7f, /// 0xd10 + 0xc9, 0x7f, 0xb3, 0x00, 0x8f, 0x07, 0xbf, 0x0f, 0x39, 0x46, 0xd2, 0x18, 0x1c, 0x31, 0x51, 0x18, /// 0xd20 + 0x29, 0x4a, 0x11, 0x82, 0x23, 0x36, 0x7d, 0x21, 0x70, 0x43, 0xc9, 0x00, 0x00, 0xf0, 0x9e, 0xfb, /// 0xd30 + 0x26, 0x4c, 0x40, 0x34, 0xa1, 0x7c, 0x08, 0x1a, 0x04, 0xd4, 0xe1, 0x7c, 0x40, 0x43, 0x41, 0x43, /// 0xd40 + 0x48, 0x11, 0x00, 0xe0, 0x00, 0x20, 0xe1, 0x8a, 0x0d, 0x18, 0xa0, 0x8a, 0xa8, 0x42, 0x00, 0xd2, /// 0xd50 + 0x05, 0x46, 0x96, 0x2e, 0x00, 0xd1, 0x00, 0x25, 0xb0, 0x00, 0xc0, 0x19, 0x80, 0x02, 0xd2, 0x21, /// 0xd60 + 0x00, 0xf0, 0x84, 0xfb, 0x1e, 0x21, 0x61, 0x56, 0x40, 0x19, 0x08, 0x18, 0x1b, 0x49, 0x88, 0x42, /// 0xd70 + 0x00, 0xd9, 0x08, 0x46, 0x20, 0x82, 0xf8, 0xbd, 0x17, 0x48, 0x17, 0x49, 0x10, 0xb5, 0xc0, 0x38, /// 0xd80 + 0x17, 0x4a, 0x48, 0x31, 0x13, 0x78, 0x5b, 0x06, 0x02, 0xd4, 0x03, 0x68, 0x05, 0x2b, 0x01, 0xd3, /// 0xd90 + 0x00, 0x20, 0x10, 0xbd, 0x0b, 0x78, 0x05, 0x2b, 0xf4, 0xd9, 0x0f, 0x48, 0x81, 0x8d, 0xc2, 0x8d, /// 0xda0 + 0x51, 0x18, 0x02, 0x8e, 0x40, 0x8e, 0x51, 0x18, 0x41, 0x18, 0x0e, 0x48, 0x00, 0xf0, 0x5e, 0xfb, /// 0xdb0 + 0x06, 0x49, 0x08, 0x84, 0x01, 0x20, 0x10, 0xbd, 0x0b, 0x48, 0x0c, 0x4a, 0x01, 0x89, 0x51, 0x1a, /// 0xdc0 + 0x00, 0x22, 0x82, 0x5e, 0x88, 0x18, 0xf7, 0xe4, 0x00, 0x30, 0x00, 0x40, 0x40, 0x00, 0x00, 0x20, /// 0xdd0 + 0x00, 0x34, 0x00, 0x40, 0x20, 0x38, 0x00, 0x40, 0xf0, 0x02, 0x00, 0x20, 0xff, 0x0f, 0x00, 0x00, /// 0xde0 + 0x00, 0x3c, 0x00, 0x40, 0x00, 0x70, 0x17, 0x00, 0x20, 0x47, 0x00, 0x00, 0x31, 0x04, 0x00, 0x00, /// 0xdf0 + 0x1e, 0x49, 0x1f, 0x4b, 0x8a, 0x7d, 0x92, 0x00, 0xd2, 0x18, 0xff, 0x32, 0x01, 0x32, 0xd0, 0x64, /// 0xe00 + 0x88, 0x7d, 0x40, 0x1c, 0x40, 0x07, 0x40, 0x0f, 0x88, 0x75, 0x70, 0x47, 0x30, 0xb5, 0x19, 0x48, /// 0xe10 + 0x01, 0x79, 0x89, 0x06, 0x28, 0xd4, 0x18, 0x49, 0x40, 0x22, 0x0a, 0x70, 0x80, 0x22, 0x0a, 0x74, /// 0xe20 + 0x12, 0x4a, 0x13, 0x4d, 0xd4, 0x7d, 0xa3, 0x00, 0x5b, 0x19, 0xff, 0x33, 0x4d, 0x33, 0x0b, 0x83, /// 0xe30 + 0x04, 0x23, 0x8b, 0x82, 0x01, 0x23, 0x0b, 0x72, 0x0d, 0x79, 0x72, 0xb6, 0x1d, 0x43, 0x0d, 0x71, /// 0xe40 + 0x64, 0x1c, 0x61, 0x07, 0x49, 0x0f, 0xd1, 0x75, 0x05, 0x21, 0x51, 0x75, 0x62, 0xb6, 0x58, 0x21, /// 0xe50 + 0x01, 0x77, 0xff, 0x21, 0x81, 0x82, 0x09, 0x49, 0x01, 0x82, 0x30, 0x21, 0x01, 0x71, 0x0f, 0x21, /// 0xe60 + 0x01, 0x70, 0x01, 0x79, 0x19, 0x43, 0x01, 0x71, 0x00, 0x20, 0x30, 0xbd, 0x90, 0x02, 0x00, 0x20, /// 0xe70 + 0x30, 0x01, 0x00, 0x20, 0x00, 0x64, 0x00, 0x40, 0x00, 0x60, 0x00, 0x40, 0xc6, 0x03, 0x00, 0x00, /// 0xe80 + 0x95, 0x48, 0x00, 0x68, 0x00, 0x47, 0x94, 0x48, 0x40, 0x68, 0x00, 0x47, 0x92, 0x48, 0x80, 0x68, /// 0xe90 + 0x00, 0x47, 0x91, 0x48, 0xc0, 0x68, 0x00, 0x47, 0x8f, 0x48, 0x00, 0x69, 0x00, 0x47, 0x8e, 0x48, /// 0xea0 + 0x40, 0x69, 0x00, 0x47, 0x8c, 0x48, 0x80, 0x69, 0x00, 0x47, 0x8b, 0x48, 0xc0, 0x69, 0x00, 0x47, /// 0xeb0 + 0x89, 0x48, 0x00, 0x6a, 0x00, 0x47, 0x88, 0x48, 0x40, 0x6a, 0x00, 0x47, 0x86, 0x48, 0x80, 0x6a, /// 0xec0 + 0x00, 0x47, 0x85, 0x48, 0xc0, 0x6a, 0x00, 0x47, 0xf8, 0xb5, 0x84, 0x48, 0x02, 0x7a, 0x01, 0x79, /// 0xed0 + 0x0a, 0x40, 0x02, 0x72, 0xd1, 0x07, 0x19, 0xd0, 0x01, 0x79, 0x49, 0x08, 0x49, 0x00, 0x01, 0x71, /// 0xee0 + 0x00, 0x21, 0x01, 0x74, 0x01, 0x70, 0x7e, 0x4c, 0x65, 0x7d, 0x7e, 0x49, 0x02, 0x23, 0x05, 0x2d, /// 0xef0 + 0x09, 0xd0, 0x0e, 0x79, 0x04, 0x25, 0x2e, 0x43, 0x0e, 0x71, 0x0e, 0x8a, 0xc1, 0x27, 0xbf, 0x00, /// 0xf00 + 0xbe, 0x43, 0x0e, 0x82, 0x65, 0x75, 0x0c, 0x79, 0x1c, 0x43, 0x0c, 0x71, 0x91, 0x07, 0x10, 0xd5, /// 0xf10 + 0x01, 0x79, 0xfd, 0x22, 0x11, 0x40, 0x01, 0x71, 0x70, 0x48, 0x00, 0x24, 0x20, 0x30, 0x04, 0x70, /// 0xf20 + 0x6f, 0x49, 0x10, 0x22, 0x2c, 0x31, 0x88, 0x18, 0xff, 0xf7, 0x13, 0xfd, 0x6c, 0x48, 0xe0, 0x30, /// 0xf30 + 0x44, 0x70, 0xf8, 0xbd, 0xf8, 0xb5, 0x6b, 0x4c, 0xa0, 0x8a, 0x21, 0x8a, 0x08, 0x40, 0xff, 0x21, /// 0xf40 + 0xa1, 0x82, 0xc6, 0x21, 0x06, 0x46, 0xff, 0x22, 0x0e, 0x40, 0x67, 0x4d, 0x6d, 0x32, 0x11, 0x1d, /// 0xf50 + 0x52, 0x59, 0x49, 0x5b, 0xff, 0x35, 0x02, 0x23, 0x61, 0x35, 0x00, 0x2e, 0x25, 0xd0, 0xc4, 0x26, /// 0xf60 + 0x30, 0x40, 0x00, 0x26, 0x00, 0x28, 0x08, 0xd0, 0x20, 0x79, 0x18, 0x43, 0x20, 0x71, 0x20, 0x20, /// 0xf70 + 0xff, 0xf7, 0xea, 0xfc, 0x06, 0x20, 0x68, 0x75, 0x07, 0xe0, 0x68, 0x7d, 0x04, 0x28, 0x03, 0xd1, /// 0xf80 + 0x20, 0x7b, 0x51, 0x18, 0x20, 0x39, 0xc8, 0x77, 0x6e, 0x75, 0x0d, 0x20, 0x20, 0x70, 0x03, 0x20, /// 0xf90 + 0x00, 0x02, 0x20, 0x82, 0xfc, 0x20, 0x20, 0x77, 0x26, 0x71, 0x50, 0x48, 0x06, 0x70, 0x06, 0x74, /// 0xfa0 + 0x01, 0x79, 0x49, 0x08, 0x49, 0x00, 0x01, 0x71, 0xf8, 0xbd, 0x4d, 0x4e, 0xc7, 0x07, 0x20, 0x3e, /// 0xfb0 + 0x76, 0x8e, 0x00, 0x2f, 0x03, 0xd0, 0x30, 0x0a, 0x20, 0x73, 0x6b, 0x75, 0xf8, 0xbd, 0xc0, 0x06, /// 0xfc0 + 0xfc, 0xd5, 0x68, 0x7d, 0x02, 0x28, 0x12, 0xd0, 0x21, 0x20, 0x20, 0x71, 0xff, 0x20, 0xc3, 0x30, /// 0xfd0 + 0x20, 0x82, 0x42, 0x48, 0x40, 0x23, 0x03, 0x70, 0x90, 0x23, 0x03, 0x74, 0x02, 0x83, 0x89, 0x1e, /// 0xfe0 + 0x81, 0x82, 0x01, 0x21, 0x01, 0x72, 0x02, 0x79, 0x0a, 0x43, 0x02, 0x71, 0xf8, 0xbd, 0x26, 0x73, /// 0xff0 + 0x03, 0x20, 0x68, 0x75, 0xf8, 0xbd, 0x3d, 0x49, 0x81, 0x20, 0x08, 0x80, 0x70, 0x47, 0x70, 0xb5, /// 0x1000 + 0x3a, 0x4b, 0x30, 0x20, 0x18, 0x76, 0x36, 0x48, 0x00, 0x24, 0x60, 0x30, 0x81, 0x7d, 0x00, 0x29, /// 0x1010 + 0x0a, 0xd0, 0x42, 0x8a, 0x1a, 0x82, 0x02, 0x7d, 0x1a, 0x75, 0x01, 0x29, 0x02, 0xd0, 0xff, 0xf7, /// 0x1020 + 0x07, 0xfd, 0x70, 0xbd, 0x84, 0x75, 0x70, 0xbd, 0x30, 0x4a, 0x20, 0x32, 0x11, 0x79, 0xfd, 0x25, /// 0x1030 + 0x29, 0x40, 0x11, 0x71, 0x2e, 0x49, 0x8d, 0x7a, 0x1f, 0x2d, 0x01, 0xd9, 0x1f, 0x25, 0x8d, 0x72, /// 0x1040 + 0x15, 0x88, 0x89, 0x7a, 0x6d, 0x09, 0x6d, 0x01, 0x0d, 0x43, 0x15, 0x80, 0x01, 0x21, 0x19, 0x80, /// 0x1050 + 0x04, 0x72, 0x28, 0x48, 0x01, 0x8a, 0x0c, 0x22, 0x91, 0x43, 0x01, 0x82, 0x70, 0xbd, 0x20, 0x48, /// 0x1060 + 0x60, 0x38, 0x01, 0x68, 0x49, 0x1c, 0x01, 0x60, 0x23, 0x49, 0x07, 0x20, 0x08, 0x72, 0x70, 0x47, /// 0x1070 + 0x10, 0xb5, 0x21, 0x4c, 0x20, 0x7a, 0x80, 0x07, 0x02, 0xd5, 0x20, 0x48, 0xff, 0xf7, 0x07, 0xf8, /// 0x1080 + 0x07, 0x20, 0x20, 0x72, 0x10, 0xbd, 0x10, 0xb5, 0x1d, 0x49, 0x8b, 0x89, 0x14, 0x48, 0x16, 0x4c, /// 0x1090 + 0xa0, 0x30, 0x02, 0x7a, 0x52, 0x00, 0x12, 0x19, 0xff, 0x32, 0xc1, 0x32, 0x13, 0x85, 0x02, 0x7a, /// 0x10a0 + 0x52, 0x1c, 0x12, 0x07, 0x12, 0x0f, 0x02, 0x72, 0x0f, 0x20, 0x08, 0x83, 0x10, 0xbd, 0x15, 0x49, /// 0x10b0 + 0x0f, 0x20, 0x08, 0x83, 0x70, 0x47, 0x10, 0xb5, 0x13, 0x48, 0x01, 0x78, 0x49, 0x06, 0x04, 0xd5, /// 0x10c0 + 0x41, 0x21, 0x01, 0x70, 0x11, 0x48, 0xfe, 0xf7, 0xe2, 0xff, 0x11, 0x48, 0x01, 0x79, 0xc9, 0x07, /// 0x10d0 + 0x01, 0xd0, 0x10, 0x21, 0x01, 0x71, 0x10, 0xbd, 0x00, 0x01, 0x00, 0x20, 0x00, 0x60, 0x00, 0x40, /// 0x10e0 + 0x90, 0x02, 0x00, 0x20, 0x00, 0x64, 0x00, 0x40, 0x30, 0x01, 0x00, 0x20, 0x00, 0x38, 0x00, 0x40, /// 0x10f0 + 0xa0, 0x00, 0x00, 0x20, 0x00, 0x34, 0x00, 0x40, 0x00, 0x54, 0x00, 0x40, 0xbd, 0x02, 0x00, 0x00, /// 0x1100 + 0x00, 0x4c, 0x00, 0x40, 0x00, 0x50, 0x00, 0x40, 0x00, 0x3c, 0x00, 0x40, 0x09, 0x08, 0x00, 0x00, /// 0x1110 + 0x40, 0x30, 0x00, 0x40, 0x00, 0x21, 0x41, 0x61, 0x81, 0x61, 0xbb, 0x4a, 0x02, 0x60, 0xc1, 0x61, /// 0x1120 + 0xba, 0x4a, 0x42, 0x60, 0xb8, 0x4a, 0xd2, 0x43, 0x82, 0x60, 0xb8, 0x4a, 0xd2, 0x43, 0xc2, 0x60, /// 0x1130 + 0xb7, 0x4a, 0x01, 0x66, 0x02, 0x61, 0x41, 0x66, 0x70, 0x47, 0xf0, 0xb5, 0xb5, 0x4b, 0x84, 0x46, /// 0x1140 + 0x0f, 0xcb, 0xd9, 0xb0, 0x6c, 0x46, 0x0f, 0xc4, 0x00, 0x23, 0x09, 0xa8, 0x9a, 0x00, 0x61, 0x46, /// 0x1150 + 0x54, 0x18, 0x20, 0x34, 0x21, 0x78, 0x5b, 0x1c, 0x09, 0x06, 0x81, 0x50, 0x65, 0x78, 0x2d, 0x04, /// 0x1160 + 0x29, 0x43, 0x81, 0x50, 0xa5, 0x78, 0x2d, 0x02, 0x29, 0x43, 0x81, 0x50, 0xe4, 0x78, 0x21, 0x43, /// 0x1170 + 0x81, 0x50, 0x10, 0x2b, 0xea, 0xdb, 0x10, 0x21, 0x8c, 0x00, 0x22, 0x18, 0x80, 0x3a, 0x15, 0x6e, /// 0x1180 + 0x53, 0x6f, 0x6b, 0x40, 0x95, 0x6c, 0x12, 0x6c, 0x55, 0x40, 0x6b, 0x40, 0x1f, 0x22, 0xd3, 0x41, /// 0x1190 + 0x49, 0x1c, 0x03, 0x51, 0x50, 0x29, 0xef, 0xdb, 0x61, 0x46, 0x0a, 0x68, 0x08, 0x92, 0x49, 0x68, /// 0x11a0 + 0x63, 0x46, 0x07, 0x91, 0x9b, 0x68, 0x64, 0x46, 0x06, 0x93, 0xe4, 0x68, 0x65, 0x46, 0x05, 0x94, /// 0x11b0 + 0x2d, 0x69, 0x00, 0x26, 0xb6, 0x46, 0x04, 0x95, 0x70, 0x46, 0x86, 0x00, 0x09, 0xa8, 0x87, 0x59, /// 0x11c0 + 0x1b, 0x20, 0x16, 0x46, 0xc6, 0x41, 0xb8, 0x19, 0x0e, 0x46, 0x27, 0x46, 0x1e, 0x40, 0x8f, 0x43, /// 0x11d0 + 0x3e, 0x43, 0x75, 0x19, 0x45, 0x19, 0x00, 0x98, 0x28, 0x18, 0x25, 0x46, 0x1c, 0x46, 0x0b, 0x46, /// 0x11e0 + 0x02, 0x21, 0xcb, 0x41, 0x11, 0x46, 0x02, 0x46, 0x70, 0x46, 0x40, 0x1c, 0x86, 0x46, 0x14, 0x28, /// 0x11f0 + 0xe2, 0xdb, 0x14, 0x20, 0x86, 0x46, 0x86, 0x00, 0x09, 0xa8, 0x87, 0x59, 0x1b, 0x20, 0x16, 0x46, /// 0x1200 + 0xc6, 0x41, 0xb8, 0x19, 0x0e, 0x46, 0x5e, 0x40, 0x66, 0x40, 0x75, 0x19, 0x45, 0x19, 0x01, 0x98, /// 0x1210 + 0x28, 0x18, 0x25, 0x46, 0x1c, 0x46, 0x0b, 0x46, 0x02, 0x21, 0xcb, 0x41, 0x11, 0x46, 0x02, 0x46, /// 0x1220 + 0x70, 0x46, 0x40, 0x1c, 0x86, 0x46, 0x28, 0x28, 0xe5, 0xdb, 0x28, 0x20, 0x86, 0x46, 0x86, 0x00, /// 0x1230 + 0x09, 0xa8, 0x87, 0x59, 0x1b, 0x20, 0x16, 0x46, 0xc6, 0x41, 0xbf, 0x19, 0x1e, 0x46, 0x26, 0x43, /// 0x1240 + 0x18, 0x46, 0x0e, 0x40, 0x20, 0x40, 0x06, 0x43, 0x70, 0x19, 0x3d, 0x18, 0x02, 0x98, 0x28, 0x18, /// 0x1250 + 0x25, 0x46, 0x1c, 0x46, 0x02, 0x23, 0xd9, 0x41, 0x0b, 0x46, 0x11, 0x46, 0x02, 0x46, 0x70, 0x46, /// 0x1260 + 0x40, 0x1c, 0x86, 0x46, 0x3c, 0x28, 0xe2, 0xdb, 0x3c, 0x20, 0x86, 0x46, 0x86, 0x00, 0x09, 0xa8, /// 0x1270 + 0x87, 0x59, 0x1b, 0x20, 0x16, 0x46, 0xc6, 0x41, 0xb8, 0x19, 0x0e, 0x46, 0x5e, 0x40, 0x66, 0x40, /// 0x1280 + 0x75, 0x19, 0x45, 0x19, 0x03, 0x98, 0x28, 0x18, 0x25, 0x46, 0x1c, 0x46, 0x02, 0x23, 0xd9, 0x41, /// 0x1290 + 0x0b, 0x46, 0x11, 0x46, 0x02, 0x46, 0x70, 0x46, 0x40, 0x1c, 0x86, 0x46, 0x50, 0x28, 0xe5, 0xdb, /// 0x12a0 + 0x08, 0x98, 0x82, 0x18, 0x60, 0x46, 0x02, 0x60, 0x07, 0x98, 0x41, 0x18, 0x60, 0x46, 0x41, 0x60, /// 0x12b0 + 0x06, 0x98, 0xc1, 0x18, 0x60, 0x46, 0x81, 0x60, 0x05, 0x98, 0x01, 0x19, 0x60, 0x46, 0xc1, 0x60, /// 0x12c0 + 0x04, 0x98, 0x41, 0x19, 0x60, 0x46, 0x01, 0x61, 0x00, 0x21, 0xc1, 0x61, 0x59, 0xb0, 0xf0, 0xbd, /// 0x12d0 + 0x70, 0xb5, 0x04, 0x46, 0xc0, 0x69, 0x25, 0x46, 0x41, 0x1c, 0x80, 0x22, 0x20, 0x35, 0x00, 0x26, /// 0x12e0 + 0xe1, 0x61, 0x37, 0x28, 0x42, 0x55, 0x03, 0xdc, 0x13, 0xe0, 0x41, 0x1c, 0xe1, 0x61, 0x46, 0x55, /// 0x12f0 + 0xe0, 0x69, 0x40, 0x28, 0xf9, 0xdb, 0x20, 0x46, 0xff, 0xf7, 0x1f, 0xff, 0x02, 0xe0, 0x41, 0x1c, /// 0x1300 + 0xe1, 0x61, 0x46, 0x55, 0xe0, 0x69, 0x38, 0x28, 0xf9, 0xdb, 0x05, 0xe0, 0x41, 0x1c, 0xe1, 0x61, /// 0x1310 + 0x46, 0x55, 0xe0, 0x69, 0x38, 0x28, 0xf9, 0xdb, 0xa1, 0x69, 0x20, 0x46, 0x0a, 0x0e, 0x40, 0x30, /// 0x1320 + 0x02, 0x76, 0x0a, 0x0c, 0x42, 0x76, 0x0a, 0x0a, 0x82, 0x76, 0xc1, 0x76, 0x61, 0x69, 0x0a, 0x0e, /// 0x1330 + 0x02, 0x77, 0x0a, 0x0c, 0x42, 0x77, 0x0a, 0x0a, 0x82, 0x77, 0xc1, 0x77, 0x20, 0x46, 0xff, 0xf7, /// 0x1340 + 0xfc, 0xfe, 0x70, 0xbd, 0x30, 0xb5, 0x0d, 0x46, 0x04, 0x00, 0x08, 0xd0, 0x00, 0x2d, 0x06, 0xd0, /// 0x1350 + 0x60, 0x6e, 0x00, 0x28, 0x04, 0xd1, 0x20, 0x6e, 0x00, 0x28, 0x02, 0xd0, 0x10, 0xe0, 0x01, 0x20, /// 0x1360 + 0x30, 0xbd, 0x20, 0x46, 0xff, 0xf7, 0xb4, 0xff, 0x00, 0x20, 0x01, 0x46, 0x22, 0x18, 0x20, 0x32, /// 0x1370 + 0x40, 0x1c, 0x11, 0x70, 0x40, 0x28, 0xf9, 0xdb, 0x61, 0x61, 0x01, 0x20, 0xa1, 0x61, 0x20, 0x66, /// 0x1380 + 0x00, 0x20, 0x03, 0x22, 0x81, 0x08, 0x83, 0x07, 0x89, 0x00, 0x9b, 0x0f, 0x61, 0x58, 0xd3, 0x1a, /// 0x1390 + 0xdb, 0x00, 0xd9, 0x40, 0x29, 0x54, 0x40, 0x1c, 0x14, 0x28, 0xf3, 0xdb, 0x00, 0x20, 0x30, 0xbd, /// 0x13a0 + 0xf0, 0xb5, 0x15, 0x00, 0x0e, 0x46, 0x04, 0x46, 0x15, 0xd0, 0x00, 0x2c, 0x07, 0xd0, 0x00, 0x2e, /// 0x13b0 + 0x05, 0xd0, 0x20, 0x6e, 0x00, 0x28, 0x04, 0xd0, 0x03, 0x20, 0x60, 0x66, 0xf0, 0xbd, 0x01, 0x20, /// 0x13c0 + 0xf0, 0xbd, 0x60, 0x6e, 0x00, 0x28, 0xfb, 0xd1, 0x27, 0x46, 0x20, 0x37, 0x6d, 0x1e, 0x02, 0xd3, /// 0x13d0 + 0x60, 0x6e, 0x00, 0x28, 0x01, 0xd0, 0x00, 0x20, 0xf0, 0xbd, 0xe0, 0x69, 0x31, 0x78, 0x42, 0x1c, /// 0x13e0 + 0xe2, 0x61, 0xc1, 0x55, 0x60, 0x69, 0x08, 0x30, 0x60, 0x61, 0x05, 0xd1, 0xa0, 0x69, 0x40, 0x1c, /// 0x13f0 + 0xa0, 0x61, 0x01, 0xd1, 0x01, 0x20, 0x60, 0x66, 0xe0, 0x69, 0x40, 0x28, 0x02, 0xd1, 0x20, 0x46, /// 0x1400 + 0xff, 0xf7, 0x9b, 0xfe, 0x76, 0x1c, 0xe1, 0xe7, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, /// 0x1410 + 0xf0, 0xe1, 0xd2, 0xc3, 0x14, 0x15, 0x00, 0x00, 0x10, 0xb5, 0x06, 0x49, 0x0c, 0x22, 0x0a, 0x73, /// 0x1420 + 0x19, 0x22, 0x4a, 0x73, 0x88, 0x73, 0x10, 0x20, 0x80, 0x31, 0xc8, 0x77, 0x01, 0x20, 0xff, 0xf7, /// 0x1430 + 0x58, 0xfb, 0x10, 0xbd, 0xd0, 0x02, 0x00, 0x20, 0x10, 0xb5, 0x09, 0x48, 0x07, 0x49, 0x41, 0x60, /// 0x1440 + 0x08, 0x49, 0x81, 0x67, 0x08, 0x22, 0x08, 0x49, 0x7c, 0x30, 0xff, 0xf7, 0x82, 0xfa, 0x04, 0x48, /// 0x1450 + 0x08, 0x22, 0x05, 0x49, 0x84, 0x30, 0xff, 0xf7, 0x7c, 0xfa, 0x10, 0xbd, 0xa5, 0x18, 0x00, 0x00, /// 0x1460 + 0x30, 0x02, 0x00, 0x20, 0x3d, 0x15, 0x00, 0x00, 0x64, 0x1b, 0x00, 0x00, 0x30, 0xb5, 0x0b, 0x46, /// 0x1470 + 0x01, 0x46, 0x00, 0x20, 0x20, 0x22, 0x01, 0x24, 0x09, 0xe0, 0x0d, 0x46, 0xd5, 0x40, 0x9d, 0x42, /// 0x1480 + 0x05, 0xd3, 0x1d, 0x46, 0x95, 0x40, 0x49, 0x1b, 0x25, 0x46, 0x95, 0x40, 0x40, 0x19, 0x15, 0x46, /// 0x1490 + 0x52, 0x1e, 0x00, 0x2d, 0xf1, 0xdc, 0x30, 0xbd, 0x70, 0xb5, 0x00, 0x24, 0x25, 0x46, 0x00, 0x28, /// 0x14a0 + 0x01, 0xda, 0x01, 0x24, 0x40, 0x42, 0x00, 0x29, 0x01, 0xda, 0x01, 0x25, 0x49, 0x42, 0xff, 0xf7, /// 0x14b0 + 0xdd, 0xff, 0xac, 0x42, 0x00, 0xd0, 0x40, 0x42, 0x00, 0x2c, 0x00, 0xd0, 0x49, 0x42, 0x70, 0xbd, /// 0x14c0 + 0x30, 0xb4, 0x74, 0x46, 0x64, 0x1e, 0x25, 0x78, 0x64, 0x1c, 0xab, 0x42, 0x00, 0xd2, 0x1d, 0x46, /// 0x14d0 + 0x63, 0x5d, 0x5b, 0x00, 0xe3, 0x18, 0x30, 0xbc, 0x18, 0x47, 0x00, 0x00, 0x07, 0x07, 0x07, 0x07, /// 0x14e0 + 0x07, 0x07, 0x07, 0x07, 0xc9, 0x0e, 0x00, 0x00, 0x5f, 0x10, 0x00, 0x00, 0x87, 0x10, 0x00, 0x00, /// 0x14f0 + 0xaf, 0x10, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x35, 0x0f, 0x00, 0x00, 0xf7, 0x0f, 0x00, 0x00, /// 0x1500 + 0x79, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, /// 0x1510 + 0xb7, 0x10, 0x00, 0x00, 0x99, 0x79, 0x82, 0x5a, 0xa1, 0xeb, 0xd9, 0x6e, 0xdc, 0xbc, 0x1b, 0x8f, /// 0x1520 + 0xd6, 0xc1, 0x62, 0xca, 0xfe, 0x48, 0x32, 0x21, 0x41, 0x66, 0x01, 0x46, 0x08, 0x22, 0x40, 0x39, /// 0x1530 + 0x8a, 0x72, 0x40, 0x39, 0x09, 0x68, 0x32, 0x39, 0x81, 0x65, 0x70, 0x47, 0xf8, 0xb5, 0xff, 0xf7, /// 0x1540 + 0x45, 0xfa, 0xf8, 0x4e, 0xf4, 0x89, 0x00, 0x2c, 0x6b, 0xd0, 0x00, 0x27, 0x35, 0x46, 0xe0, 0x05, /// 0x1550 + 0x40, 0x35, 0xf7, 0x81, 0x00, 0x28, 0x0f, 0xda, 0xff, 0xf7, 0x2c, 0xfb, 0x02, 0x20, 0xf2, 0x4a, /// 0x1560 + 0x28, 0x70, 0x5a, 0x21, 0x11, 0x70, 0xf0, 0x48, 0x10, 0x23, 0x40, 0x30, 0x03, 0x70, 0x11, 0x70, /// 0x1570 + 0x07, 0x72, 0x80, 0x21, 0x01, 0x70, 0x54, 0xe0, 0xec, 0x48, 0x21, 0x04, 0x04, 0xd5, 0x01, 0x8d, /// 0x1580 + 0x01, 0x22, 0x52, 0x02, 0x11, 0x43, 0x01, 0x85, 0xe1, 0x06, 0x04, 0xd5, 0x01, 0x8d, 0xff, 0x22, /// 0x1590 + 0x01, 0x32, 0x11, 0x43, 0x01, 0x85, 0xe1, 0x07, 0x08, 0x22, 0x00, 0x29, 0x02, 0xd0, 0x69, 0x78, /// 0x15a0 + 0x11, 0x43, 0x69, 0x70, 0xa1, 0x05, 0x15, 0xd5, 0xde, 0x49, 0x20, 0x39, 0x8b, 0x79, 0x1b, 0x06, /// 0x15b0 + 0x06, 0xd5, 0x8f, 0x71, 0xcf, 0x71, 0x01, 0x8d, 0x01, 0x22, 0x11, 0x43, 0x01, 0x85, 0x09, 0xe0, /// 0x15c0 + 0xda, 0x48, 0x0b, 0x7b, 0x00, 0x8d, 0x00, 0x2b, 0x0e, 0xd0, 0x20, 0x23, 0x18, 0x43, 0xd7, 0x4b, /// 0x15d0 + 0x18, 0x85, 0xca, 0x71, 0xa0, 0x06, 0x01, 0xd5, 0xff, 0xf7, 0xe5, 0xf9, 0x60, 0x06, 0x0b, 0xd5, /// 0x15e0 + 0x30, 0x7b, 0x5a, 0x28, 0x02, 0xd0, 0x07, 0xe0, 0x10, 0x23, 0xef, 0xe7, 0x70, 0x7b, 0x40, 0x21, /// 0x15f0 + 0x08, 0x43, 0x70, 0x73, 0xce, 0x48, 0x80, 0x47, 0x20, 0x07, 0x01, 0xd5, 0xff, 0xf7, 0x92, 0xff, /// 0x1600 + 0xa0, 0x07, 0x01, 0xd5, 0x00, 0xf0, 0xb0, 0xfd, 0x20, 0x06, 0x03, 0xd5, 0xa8, 0x78, 0x01, 0x21, /// 0x1610 + 0x08, 0x43, 0xa8, 0x70, 0x60, 0x07, 0x03, 0xd5, 0xa8, 0x78, 0x20, 0x21, 0x08, 0x43, 0xa8, 0x70, /// 0x1620 + 0x37, 0x73, 0x00, 0x20, 0xf8, 0xbd, 0xbe, 0x48, 0x10, 0xb5, 0x80, 0x38, 0x01, 0x68, 0x80, 0x30, /// 0x1630 + 0x82, 0x6d, 0x43, 0x6e, 0x8a, 0x1a, 0x9a, 0x42, 0x0c, 0xdb, 0x81, 0x65, 0x83, 0x21, 0x89, 0x00, /// 0x1640 + 0x81, 0x85, 0xb8, 0x48, 0x20, 0x38, 0xc1, 0x7e, 0xb5, 0x48, 0x20, 0x30, 0x81, 0x73, 0x01, 0x20, /// 0x1650 + 0xff, 0xf7, 0x47, 0xfa, 0x10, 0xbd, 0xb3, 0x48, 0x10, 0xb5, 0x10, 0x22, 0xb5, 0x49, 0x50, 0x30, /// 0x1660 + 0xff, 0xf7, 0x77, 0xf9, 0xaf, 0x48, 0x10, 0x22, 0xb3, 0x49, 0x28, 0x30, 0xff, 0xf7, 0x71, 0xf9, /// 0x1670 + 0xac, 0x48, 0x19, 0x21, 0x20, 0x38, 0x02, 0x46, 0x81, 0x77, 0x40, 0x32, 0xd1, 0x70, 0x00, 0x21, /// 0x1680 + 0xc1, 0x77, 0xa9, 0x49, 0x80, 0x20, 0x08, 0x82, 0x10, 0xbd, 0x10, 0xb5, 0xff, 0xf7, 0xe3, 0xff, /// 0x1690 + 0xa4, 0x49, 0xf7, 0x22, 0x48, 0x7b, 0x10, 0x40, 0x48, 0x73, 0x08, 0x46, 0xa7, 0x4a, 0x40, 0x30, /// 0x16a0 + 0xc2, 0x60, 0x82, 0x60, 0x9e, 0x48, 0xa6, 0x4a, 0x80, 0x30, 0x42, 0x81, 0xff, 0x38, 0x01, 0x38, /// 0x16b0 + 0x02, 0x68, 0x80, 0x30, 0x02, 0x66, 0xc2, 0x65, 0x43, 0x6e, 0xd2, 0x1a, 0x82, 0x65, 0x0f, 0x20, /// 0x16c0 + 0x88, 0x72, 0x98, 0x48, 0x03, 0x21, 0x20, 0x30, 0x81, 0x70, 0x95, 0x48, 0x05, 0x21, 0x40, 0x38, /// 0x16d0 + 0x81, 0x72, 0x10, 0xbd, 0x70, 0xb5, 0x9b, 0x48, 0x01, 0x88, 0x07, 0x22, 0x12, 0x02, 0x91, 0x43, /// 0x16e0 + 0x01, 0x80, 0x90, 0x48, 0x08, 0x22, 0x41, 0x7b, 0x04, 0x46, 0x11, 0x43, 0x41, 0x73, 0x0f, 0x21, /// 0x16f0 + 0x81, 0x72, 0x90, 0x49, 0x00, 0x25, 0x20, 0x34, 0xa5, 0x70, 0x10, 0x22, 0x10, 0x31, 0x50, 0x30, /// 0x1700 + 0xff, 0xf7, 0x27, 0xf9, 0x8c, 0x49, 0x20, 0x46, 0x10, 0x22, 0x10, 0x31, 0x08, 0x30, 0xff, 0xf7, /// 0x1710 + 0x20, 0xf9, 0x20, 0x46, 0x8c, 0x49, 0x20, 0x30, 0x01, 0x82, 0xff, 0x21, 0x82, 0x4a, 0x71, 0x31, /// 0x1720 + 0x11, 0x82, 0x01, 0x46, 0x55, 0x22, 0x60, 0x39, 0x8a, 0x77, 0xe2, 0x70, 0xcd, 0x77, 0x87, 0x49, /// 0x1730 + 0xc1, 0x60, 0x82, 0x49, 0x81, 0x60, 0x7a, 0x48, 0x80, 0x30, 0x81, 0x66, 0x04, 0x21, 0xc0, 0x38, /// 0x1740 + 0x81, 0x72, 0x70, 0xbd, 0x10, 0xb5, 0x76, 0x48, 0x81, 0x49, 0xc1, 0x62, 0x41, 0x21, 0x49, 0x02, /// 0x1750 + 0x01, 0x86, 0x80, 0x21, 0x20, 0x30, 0x81, 0x74, 0x05, 0x20, 0xff, 0xf7, 0xc2, 0xf9, 0x10, 0xbd, /// 0x1760 + 0x10, 0xb5, 0x6f, 0x48, 0x7b, 0x49, 0xc1, 0x62, 0x6e, 0x21, 0x01, 0x86, 0x7a, 0x49, 0x20, 0x30, /// 0x1770 + 0xca, 0x7b, 0x82, 0x74, 0x0a, 0x7c, 0xc2, 0x74, 0x49, 0x7c, 0x01, 0x75, 0x07, 0x20, 0xff, 0xf7, /// 0x1780 + 0xb0, 0xf9, 0x10, 0xbd, 0x10, 0xb5, 0x67, 0x49, 0x08, 0x88, 0xc0, 0x08, 0xff, 0x28, 0x00, 0xd9, /// 0x1790 + 0xff, 0x20, 0xc8, 0x72, 0xff, 0x22, 0x62, 0x49, 0x0d, 0x32, 0x8a, 0x85, 0x20, 0x31, 0x88, 0x73, /// 0x17a0 + 0x01, 0x20, 0xff, 0xf7, 0x9e, 0xf9, 0x10, 0xbd, 0xf8, 0xb5, 0x5e, 0x4d, 0x5f, 0x4c, 0x40, 0x35, /// 0x17b0 + 0x68, 0x78, 0x26, 0x46, 0x27, 0x46, 0xc1, 0x06, 0x40, 0x36, 0xc0, 0x37, 0x00, 0x29, 0x0a, 0xda, /// 0x17c0 + 0xef, 0x21, 0x08, 0x40, 0x68, 0x70, 0xa1, 0x8e, 0x01, 0x20, 0xc0, 0x02, 0x01, 0x43, 0xa1, 0x86, /// 0x17d0 + 0xfe, 0xf7, 0xdf, 0xff, 0x10, 0xe0, 0x39, 0x79, 0x0a, 0x07, 0x06, 0xd5, 0x32, 0x7e, 0x1e, 0x2a, /// 0x17e0 + 0x03, 0xd0, 0x20, 0x21, 0x08, 0x43, 0x68, 0x70, 0x06, 0xe0, 0x48, 0x06, 0x04, 0xd5, 0xbf, 0x20, /// 0x17f0 + 0x01, 0x40, 0x80, 0x20, 0x01, 0x43, 0x39, 0x71, 0x38, 0x79, 0x81, 0x07, 0x02, 0xd5, 0x31, 0x7e, /// 0x1800 + 0x3f, 0x29, 0x16, 0xd0, 0x69, 0x78, 0x49, 0x06, 0x02, 0xd5, 0x31, 0x7e, 0x4f, 0x29, 0x13, 0xd0, /// 0x1810 + 0x00, 0x07, 0x09, 0xd5, 0x30, 0x7e, 0x1e, 0x28, 0x06, 0xd0, 0xa0, 0x8e, 0x10, 0x21, 0x08, 0x43, /// 0x1820 + 0xa0, 0x86, 0x08, 0x46, 0xfe, 0xf7, 0xb5, 0xff, 0xa8, 0x78, 0xbf, 0x21, 0x08, 0x40, 0xa8, 0x70, /// 0x1830 + 0xf8, 0xbd, 0x01, 0xf0, 0x52, 0xfc, 0xf7, 0xe7, 0x3a, 0x48, 0x19, 0x30, 0x01, 0xf0, 0x28, 0xfc, /// 0x1840 + 0xf2, 0xe7, 0x10, 0xb5, 0x45, 0x49, 0x0d, 0x20, 0x08, 0x72, 0xff, 0xf7, 0x2f, 0xf8, 0x37, 0x48, /// 0x1850 + 0x00, 0x21, 0x01, 0x81, 0x33, 0x49, 0x42, 0x4a, 0x49, 0x7b, 0x82, 0x81, 0x0b, 0x07, 0x7d, 0x21, /// 0x1860 + 0x01, 0x82, 0x96, 0x21, 0xc1, 0x81, 0x2a, 0x21, 0xc1, 0x74, 0x81, 0x7c, 0x20, 0x22, 0x11, 0x43, /// 0x1870 + 0x81, 0x74, 0x10, 0xbd, 0x10, 0xb5, 0x39, 0x48, 0x00, 0x7e, 0x80, 0x07, 0x11, 0xd5, 0x01, 0x20, /// 0x1880 + 0x80, 0x03, 0xfe, 0xf7, 0x86, 0xff, 0xff, 0xf7, 0x95, 0xf9, 0x26, 0x48, 0x02, 0x21, 0x40, 0x30, /// 0x1890 + 0x01, 0x70, 0x25, 0x49, 0x5a, 0x20, 0x08, 0x70, 0x23, 0x49, 0x80, 0x20, 0x40, 0x31, 0x08, 0x70, /// 0x18a0 + 0xfe, 0xe7, 0x10, 0xbd, 0xfe, 0xb5, 0xff, 0xf7, 0xe5, 0xff, 0x2e, 0x48, 0x01, 0x90, 0x00, 0x7d, /// 0x18b0 + 0x00, 0x28, 0x12, 0xd0, 0x40, 0x1e, 0x00, 0x06, 0x01, 0x99, 0x00, 0x0e, 0x08, 0x75, 0x0c, 0xd1, /// 0x18c0 + 0x26, 0x48, 0x20, 0x30, 0x01, 0x78, 0x20, 0x22, 0x11, 0x43, 0x01, 0x70, 0x17, 0x48, 0x10, 0x22, /// 0x18d0 + 0x81, 0x7c, 0x11, 0x43, 0x81, 0x74, 0xff, 0xf7, 0xb4, 0xff, 0x12, 0x4e, 0x40, 0x36, 0xb0, 0x78, /// 0x18e0 + 0x40, 0x06, 0x01, 0xd5, 0xff, 0xf7, 0x60, 0xff, 0x0e, 0x48, 0x20, 0x38, 0x81, 0x7f, 0x40, 0x30, /// 0x18f0 + 0x4b, 0x29, 0x01, 0xd9, 0x01, 0x21, 0x04, 0xe0, 0x23, 0x29, 0x01, 0xd9, 0x00, 0x21, 0x00, 0xe0, /// 0x1900 + 0x03, 0x21, 0x81, 0x70, 0x17, 0x48, 0x06, 0x4d, 0x40, 0x38, 0x00, 0x90, 0x40, 0x78, 0x40, 0x35, /// 0x1910 + 0x00, 0x28, 0x2d, 0xd1, 0x28, 0x7a, 0x00, 0x28, 0x2a, 0xd1, 0x28, 0x46, 0x3c, 0x38, 0x23, 0xe0, /// 0x1920 + 0xb0, 0x02, 0x00, 0x20, 0x40, 0x00, 0x00, 0x20, 0x00, 0x30, 0x00, 0x40, 0x00, 0x00, 0x00, 0x20, /// 0x1930 + 0x11, 0x06, 0x00, 0x20, 0x6c, 0x1b, 0x00, 0x00, 0x44, 0x31, 0x00, 0x00, 0x04, 0x0a, 0x0a, 0x00, /// 0x1940 + 0xdc, 0x05, 0x00, 0x00, 0x00, 0x34, 0x00, 0x40, 0x85, 0x09, 0x00, 0x00, 0x31, 0x14, 0x14, 0x00, /// 0x1950 + 0x0c, 0x51, 0x0a, 0x00, 0x0c, 0x71, 0x12, 0x00, 0x20, 0x47, 0x00, 0x00, 0x20, 0x6c, 0x00, 0x40, /// 0x1960 + 0x88, 0x13, 0x00, 0x00, 0xb0, 0x03, 0x00, 0x20, 0xfe, 0xf7, 0x84, 0xfd, 0xfe, 0xf7, 0x47, 0xfe, /// 0x1970 + 0x72, 0x78, 0x73, 0x4f, 0xd0, 0x06, 0x00, 0x28, 0x11, 0xda, 0xbb, 0x89, 0x39, 0x46, 0x58, 0x1c, /// 0x1980 + 0xb8, 0x81, 0xc0, 0x39, 0x48, 0x8c, 0x83, 0x42, 0x09, 0xd9, 0xef, 0x20, 0x02, 0x40, 0x72, 0x70, /// 0x1990 + 0x8a, 0x8e, 0x01, 0x20, 0x80, 0x02, 0x02, 0x43, 0x8a, 0x86, 0xfe, 0xf7, 0xfa, 0xfe, 0x30, 0x78, /// 0x19a0 + 0xc0, 0x06, 0x23, 0xd5, 0x66, 0x48, 0xc0, 0x38, 0x81, 0x7c, 0x02, 0x91, 0x89, 0x06, 0x1b, 0xd5, /// 0x19b0 + 0x40, 0x89, 0x64, 0x49, 0x48, 0x43, 0x64, 0x49, 0xff, 0xf7, 0x58, 0xfd, 0x60, 0x4c, 0x12, 0x30, /// 0x19c0 + 0x80, 0x3c, 0xe1, 0x78, 0x06, 0x29, 0x15, 0xd0, 0x22, 0x46, 0x40, 0x3a, 0x13, 0x6b, 0x18, 0x18, /// 0x19d0 + 0x49, 0x1c, 0x10, 0x63, 0xe1, 0x70, 0x5a, 0x48, 0xc0, 0x38, 0x01, 0x8a, 0xc0, 0x89, 0xfe, 0xf7, /// 0x19e0 + 0x78, 0xff, 0x30, 0x78, 0xc0, 0x06, 0x01, 0xd5, 0x01, 0xf0, 0xe9, 0xfa, 0x28, 0x7a, 0x00, 0x28, /// 0x19f0 + 0x42, 0xd1, 0x1c, 0xe0, 0x52, 0x49, 0xc0, 0x39, 0x09, 0x6b, 0x08, 0x18, 0x07, 0x21, 0xff, 0xf7, /// 0x1a00 + 0x35, 0xfd, 0x4f, 0x49, 0x80, 0xb2, 0xc0, 0x39, 0x08, 0x81, 0x4d, 0x4a, 0x00, 0x21, 0xe1, 0x70, /// 0x1a10 + 0xc0, 0x3a, 0x11, 0x63, 0x02, 0x99, 0xc9, 0x07, 0xdd, 0xd1, 0x91, 0x89, 0x43, 0x1a, 0x00, 0xd5, /// 0x1a20 + 0x5b, 0x42, 0xd2, 0x7c, 0x93, 0x42, 0xd6, 0xdd, 0xfe, 0xf7, 0x60, 0xff, 0xd3, 0xe7, 0x47, 0x48, /// 0x1a30 + 0xe9, 0x7a, 0x04, 0x46, 0xff, 0x34, 0x41, 0x34, 0x20, 0x46, 0xc0, 0x30, 0x02, 0x90, 0x00, 0x29, /// 0x1a40 + 0x1b, 0xd0, 0x49, 0x1e, 0xe9, 0x72, 0xa0, 0x7a, 0x03, 0x28, 0x76, 0xd3, 0x72, 0xb6, 0x40, 0x48, /// 0x1a50 + 0xfe, 0xf7, 0x1d, 0xfb, 0x62, 0xb6, 0x28, 0x7b, 0xe9, 0x7a, 0x40, 0x1e, 0x81, 0x42, 0x6c, 0xd1, /// 0x1a60 + 0x02, 0x99, 0x00, 0x20, 0x08, 0x82, 0x88, 0x81, 0x68, 0x7b, 0x80, 0x06, 0x65, 0xd4, 0x39, 0x48, /// 0x1a70 + 0x01, 0x88, 0x82, 0x14, 0x91, 0x43, 0x01, 0x80, 0x5f, 0xe0, 0x00, 0x20, 0x68, 0x74, 0xa0, 0x7a, /// 0x1a80 + 0x03, 0x00, 0xff, 0xf7, 0x1d, 0xfd, 0x09, 0x06, 0x15, 0x1a, 0x26, 0x3f, 0x42, 0x45, 0x55, 0x58, /// 0x1a90 + 0x5a, 0x00, 0x31, 0x48, 0x00, 0x68, 0x1d, 0x28, 0x4f, 0xd9, 0x08, 0x20, 0x28, 0x73, 0x2e, 0x48, /// 0x1aa0 + 0x32, 0x21, 0x80, 0x30, 0x41, 0x66, 0x01, 0x20, 0xa0, 0x72, 0xff, 0xf7, 0x6b, 0xfe, 0x44, 0xe0, /// 0x1ab0 + 0xff, 0xf7, 0x56, 0xfe, 0x02, 0x20, 0xa0, 0x72, 0x3f, 0xe0, 0x34, 0x20, 0xf0, 0x70, 0x1e, 0x20, /// 0x1ac0 + 0x28, 0x73, 0xff, 0xf7, 0x3f, 0xfe, 0x03, 0x20, 0xa0, 0x72, 0x24, 0x49, 0x01, 0x20, 0xc8, 0x77, /// 0x1ad0 + 0x33, 0xe0, 0x00, 0x98, 0x00, 0x78, 0xc0, 0x07, 0x02, 0xd0, 0xff, 0xf7, 0xfb, 0xfd, 0x01, 0xe0, /// 0x1ae0 + 0xff, 0xf7, 0xd3, 0xfd, 0x16, 0x49, 0xff, 0x20, 0xf5, 0x30, 0xc0, 0x39, 0x48, 0x84, 0x00, 0x20, /// 0x1af0 + 0xb0, 0x64, 0xff, 0x22, 0x78, 0x71, 0x60, 0x32, 0xfa, 0x81, 0x01, 0x9a, 0x10, 0x75, 0x18, 0x48, /// 0x1b00 + 0x08, 0x87, 0x1a, 0xe0, 0x01, 0xf0, 0x62, 0xf9, 0x17, 0xe0, 0x00, 0xf0, 0xcb, 0xff, 0x14, 0xe0, /// 0x1b10 + 0x00, 0x98, 0x00, 0x78, 0xc0, 0x07, 0x01, 0xd0, 0x07, 0x20, 0x00, 0xe0, 0x05, 0x20, 0xa0, 0x72, /// 0x1b20 + 0x0e, 0x49, 0x00, 0x20, 0x40, 0x31, 0xc8, 0x71, 0x02, 0x98, 0xb1, 0x68, 0x81, 0x66, 0x04, 0xe0, /// 0x1b30 + 0x00, 0xf0, 0x59, 0xfb, 0x01, 0xe0, 0xff, 0xf7, 0x76, 0xfd, 0x00, 0x20, 0xfe, 0xbd, 0x00, 0x00, /// 0x1b40 + 0xc0, 0x00, 0x00, 0x20, 0x34, 0x08, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00, 0x30, 0x01, 0x00, 0x20, /// 0x1b50 + 0x5f, 0x1f, 0x00, 0x00, 0x20, 0x38, 0x00, 0x40, 0x30, 0x02, 0x00, 0x20, 0x50, 0x03, 0x00, 0x20, /// 0x1b60 + 0xd8, 0xbf, 0x00, 0x00, 0x32, 0x31, 0x3f, 0x03, 0x32, 0x31, 0x3f, 0x14, 0xf3, 0x04, 0x19, 0x0c, /// 0x1b70 + 0xea, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x08, 0x32, 0x04, /// 0x1b80 + 0x75, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x28, 0x01, 0xd2, /// 0x1b90 + 0x01, 0x20, 0x70, 0x47, 0x80, 0x28, 0x03, 0xd2, 0x20, 0x38, 0x00, 0x11, 0x80, 0x1c, 0x70, 0x47, /// 0x1ba0 + 0xe0, 0x28, 0x03, 0xd2, 0x80, 0x38, 0xc0, 0x10, 0x08, 0x30, 0x70, 0x47, 0xe0, 0x38, 0x80, 0x10, /// 0x1bb0 + 0x14, 0x30, 0x70, 0x47, 0x41, 0x07, 0x89, 0x0f, 0x03, 0x29, 0x0b, 0xd1, 0x01, 0x07, 0x03, 0xd5, /// 0x1bc0 + 0x80, 0x09, 0x02, 0x28, 0x04, 0xd0, 0x05, 0xe0, 0x00, 0x09, 0x03, 0xd0, 0x0c, 0x28, 0x01, 0xd8, /// 0x1bd0 + 0x01, 0x20, 0x70, 0x47, 0x00, 0x20, 0x70, 0x47, 0x1e, 0x28, 0x08, 0xd0, 0xc1, 0x43, 0x09, 0x07, /// 0x1be0 + 0x07, 0xd1, 0x01, 0x09, 0x06, 0x29, 0x04, 0xd2, 0x00, 0x06, 0x00, 0x0f, 0x01, 0xd0, 0x01, 0x20, /// 0x1bf0 + 0x70, 0x47, 0x00, 0x20, 0x70, 0x47, 0x10, 0xb5, 0xf9, 0x48, 0xf7, 0x22, 0xc1, 0x79, 0x11, 0x40, /// 0x1c00 + 0xc1, 0x71, 0xf7, 0x49, 0x00, 0x20, 0x20, 0x39, 0x08, 0x85, 0x80, 0x20, 0xfe, 0xf7, 0xc1, 0xfd, /// 0x1c10 + 0x10, 0xbd, 0xf8, 0xb5, 0xf3, 0x4a, 0x10, 0x7d, 0xff, 0xf7, 0xde, 0xff, 0xf2, 0x49, 0x11, 0x24, /// 0x1c20 + 0x64, 0x01, 0x0f, 0x19, 0x85, 0x23, 0x09, 0x24, 0x9b, 0x00, 0xa4, 0x01, 0xcb, 0x18, 0x0e, 0x19, /// 0x1c30 + 0x00, 0x28, 0x18, 0xd0, 0xf0, 0x79, 0x40, 0x1c, 0xc0, 0xb2, 0xf0, 0x71, 0x06, 0x28, 0x01, 0xd9, /// 0x1c40 + 0x06, 0x20, 0xf0, 0x71, 0xc2, 0xb2, 0xe6, 0x48, 0x19, 0x46, 0x38, 0x30, 0xfe, 0xf7, 0x88, 0xfe, /// 0x1c50 + 0xe3, 0x48, 0x40, 0x22, 0x60, 0x30, 0x81, 0x78, 0x11, 0x43, 0x81, 0x70, 0xf8, 0x7f, 0xef, 0x21, /// 0x1c60 + 0x08, 0x40, 0xf8, 0x77, 0xf8, 0xbd, 0xde, 0x4c, 0x10, 0x7d, 0x20, 0x3c, 0x25, 0x46, 0x20, 0x35, /// 0x1c70 + 0x15, 0x28, 0x17, 0xd0, 0xff, 0xf7, 0x9e, 0xff, 0x01, 0x1e, 0xdc, 0x48, 0x57, 0xd0, 0x11, 0x7d, /// 0x1c80 + 0xea, 0x79, 0xc9, 0x07, 0xd2, 0x07, 0xc9, 0x0f, 0xd2, 0x0f, 0x91, 0x42, 0x4a, 0xd1, 0xf1, 0x79, /// 0x1c90 + 0xeb, 0x7b, 0x8c, 0x46, 0xaa, 0x7b, 0x59, 0x18, 0x91, 0x42, 0x22, 0xd9, 0xe9, 0x73, 0x20, 0x8d, /// 0x1ca0 + 0x02, 0x21, 0x41, 0xe0, 0x50, 0x7d, 0x01, 0x06, 0x14, 0xd5, 0xe9, 0x79, 0x8a, 0x06, 0x92, 0x0f, /// 0x1cb0 + 0x02, 0xd0, 0x20, 0x8d, 0x04, 0x21, 0x37, 0xe0, 0x22, 0x8d, 0x08, 0x23, 0x40, 0x06, 0x1a, 0x43, /// 0x1cc0 + 0x40, 0x0e, 0x22, 0x85, 0x40, 0x1c, 0xa8, 0x73, 0x00, 0x20, 0xe8, 0x73, 0x48, 0x08, 0x40, 0x00, /// 0x1cd0 + 0xe8, 0x71, 0xf8, 0xbd, 0x03, 0x28, 0x01, 0xd1, 0xff, 0xf7, 0x8d, 0xff, 0x20, 0x8d, 0x08, 0x21, /// 0x1ce0 + 0x22, 0xe0, 0xc0, 0x49, 0x18, 0x18, 0x62, 0x46, 0x15, 0x31, 0xfe, 0xf7, 0x39, 0xfe, 0xe8, 0x7b, /// 0x1cf0 + 0xf1, 0x79, 0x01, 0x22, 0x40, 0x18, 0xc1, 0xb2, 0xe9, 0x73, 0xe8, 0x79, 0x40, 0x23, 0x50, 0x40, /// 0x1d00 + 0xe8, 0x71, 0x22, 0x8d, 0x1a, 0x43, 0x22, 0x85, 0xb6, 0x4a, 0x32, 0x23, 0x80, 0x3a, 0x53, 0x66, /// 0x1d10 + 0x05, 0x22, 0x3a, 0x77, 0xaa, 0x7b, 0x91, 0x42, 0xdb, 0xd1, 0x10, 0x21, 0x08, 0x43, 0xe8, 0x71, /// 0x1d20 + 0x08, 0x46, 0x18, 0xe0, 0x20, 0x8d, 0x40, 0x21, 0x08, 0x43, 0x20, 0x85, 0xf8, 0xbd, 0xe9, 0x79, /// 0x1d30 + 0x89, 0x06, 0x89, 0x0f, 0xbd, 0xd1, 0x21, 0x8d, 0x08, 0x22, 0x11, 0x43, 0x21, 0x85, 0xf1, 0x79, /// 0x1d40 + 0x49, 0x1c, 0xca, 0xb2, 0xaa, 0x73, 0x19, 0x46, 0xfe, 0xf7, 0x0a, 0xfe, 0xe8, 0x79, 0x20, 0x21, /// 0x1d50 + 0x08, 0x43, 0xe8, 0x71, 0x08, 0x46, 0xfe, 0xf7, 0x1c, 0xfd, 0xf8, 0xbd, 0xf8, 0xb5, 0xa0, 0x4c, /// 0x1d60 + 0x61, 0x7b, 0x22, 0x7b, 0x26, 0x46, 0x20, 0x3e, 0xe0, 0x79, 0x91, 0x42, 0x07, 0xd3, 0x32, 0x8d, /// 0x1d70 + 0x80, 0x21, 0x8a, 0x43, 0xf7, 0x21, 0x32, 0x85, 0x08, 0x40, 0xe0, 0x71, 0xf8, 0xbd, 0x52, 0x1a, /// 0x1d80 + 0xd5, 0xb2, 0x08, 0x2d, 0x00, 0xd9, 0x08, 0x25, 0x2a, 0x07, 0x13, 0x0e, 0x82, 0x07, 0x01, 0xd5, /// 0x1d90 + 0x07, 0x22, 0x00, 0xe0, 0x06, 0x22, 0x1a, 0x43, 0x02, 0x23, 0x58, 0x40, 0xe0, 0x71, 0x91, 0x4f, /// 0x1da0 + 0x91, 0x48, 0x20, 0x37, 0x09, 0x18, 0x3a, 0x72, 0x38, 0x46, 0x2a, 0x46, 0x09, 0x30, 0xfe, 0xf7, /// 0x1db0 + 0xd7, 0xfd, 0x60, 0x7b, 0x80, 0x21, 0x40, 0x19, 0x60, 0x73, 0x30, 0x8d, 0x08, 0x43, 0x30, 0x85, /// 0x1dc0 + 0x38, 0x46, 0x32, 0x21, 0xa0, 0x38, 0x41, 0x66, 0x05, 0x20, 0x38, 0x77, 0xf8, 0xbd, 0xfe, 0xb5, /// 0x1dd0 + 0x07, 0x46, 0x84, 0x48, 0x20, 0x30, 0x00, 0x90, 0xc0, 0x7f, 0x00, 0x28, 0x06, 0xd1, 0x00, 0x2f, /// 0x1de0 + 0x01, 0xd0, 0x02, 0x20, 0x00, 0xe0, 0x08, 0x20, 0x00, 0x99, 0xc8, 0x77, 0x00, 0x98, 0x7e, 0x4b, /// 0x1df0 + 0xc1, 0x7f, 0xf8, 0x01, 0x86, 0x46, 0x7a, 0x48, 0xde, 0x1d, 0xff, 0x36, 0x20, 0x30, 0xfa, 0x36, /// 0x1e00 + 0x00, 0x89, 0x34, 0x8a, 0x01, 0x94, 0x09, 0x24, 0xa4, 0x01, 0x1c, 0x19, 0x33, 0x7d, 0x8a, 0x07, /// 0x1e10 + 0xa5, 0x79, 0x9c, 0x46, 0x00, 0x2a, 0x4b, 0xd0, 0x62, 0x46, 0x52, 0x08, 0x73, 0x46, 0x1a, 0x43, /// 0x1e20 + 0xd2, 0xb2, 0x32, 0x75, 0x00, 0x2d, 0x0a, 0xd0, 0x07, 0x2d, 0x10, 0xd3, 0xff, 0x2a, 0x11, 0xd0, /// 0x1e30 + 0x00, 0x2a, 0x38, 0xd0, 0x55, 0x2a, 0x38, 0xd0, 0xaa, 0x2a, 0x36, 0xd0, 0x83, 0xe0, 0x48, 0x21, /// 0x1e40 + 0x48, 0x43, 0x6b, 0x49, 0xff, 0xf7, 0x12, 0xfb, 0x01, 0x99, 0x40, 0x18, 0x70, 0x82, 0x6d, 0x1c, /// 0x1e50 + 0xa5, 0x71, 0x82, 0xe0, 0x88, 0x07, 0x02, 0xd5, 0xff, 0xf7, 0x80, 0xff, 0x73, 0xe0, 0x60, 0x4d, /// 0x1e60 + 0x20, 0x3d, 0x6a, 0x8d, 0x00, 0x2a, 0x1c, 0xd0, 0xd0, 0x07, 0x02, 0xd0, 0xff, 0xf7, 0xc3, 0xfe, /// 0x1e70 + 0x11, 0xe0, 0x5b, 0x48, 0x93, 0x06, 0xc1, 0x79, 0x00, 0x2b, 0x07, 0xda, 0x00, 0x22, 0x42, 0x73, /// 0x1e80 + 0xfd, 0x22, 0x11, 0x40, 0xc1, 0x71, 0xff, 0xf7, 0x69, 0xff, 0x04, 0xe0, 0xd2, 0x06, 0x02, 0xd5, /// 0x1e90 + 0xf7, 0x22, 0x11, 0x40, 0xc1, 0x71, 0x28, 0x8d, 0x69, 0x8d, 0x88, 0x43, 0x28, 0x85, 0x00, 0x20, /// 0x1ea0 + 0x68, 0x85, 0x01, 0x20, 0x02, 0xe0, 0x02, 0x20, 0x00, 0xe0, 0x04, 0x20, 0x20, 0x70, 0x4a, 0xe0, /// 0x1eb0 + 0xc9, 0x06, 0x49, 0x0f, 0x47, 0xd0, 0x4b, 0x4a, 0x40, 0x32, 0xd1, 0x79, 0x52, 0x79, 0x00, 0x2d, /// 0x1ec0 + 0x13, 0xd0, 0x48, 0x48, 0x14, 0x30, 0x09, 0x2d, 0x25, 0xd0, 0x0a, 0x2d, 0x3d, 0xd3, 0x00, 0x2f, /// 0x1ed0 + 0x39, 0xd0, 0x00, 0x23, 0xa3, 0x71, 0x00, 0x2a, 0x25, 0xd0, 0x8a, 0x42, 0x27, 0xd9, 0x89, 0x1c, /// 0x1ee0 + 0xfe, 0xf7, 0x93, 0xfd, 0x00, 0x28, 0x26, 0xd0, 0x2d, 0xe0, 0x00, 0x2f, 0x2b, 0xd1, 0x01, 0x23, /// 0x1ef0 + 0xa3, 0x71, 0x00, 0x2a, 0x0c, 0xd0, 0x8a, 0x1a, 0x58, 0x21, 0x92, 0x1c, 0x4a, 0x43, 0x08, 0x32, /// 0x1f00 + 0x50, 0x43, 0x3b, 0x49, 0xff, 0xf7, 0xb2, 0xfa, 0x01, 0x99, 0x40, 0x18, 0x70, 0x82, 0x24, 0xe0, /// 0x1f10 + 0x60, 0x21, 0x48, 0x43, 0xf5, 0xe7, 0x0a, 0x21, 0xa1, 0x71, 0x10, 0x5c, 0xfe, 0xf7, 0x7e, 0xfd, /// 0x1f20 + 0xb8, 0x42, 0x10, 0xd0, 0x19, 0xe0, 0x60, 0x46, 0xff, 0xf7, 0x30, 0xfe, 0xe0, 0x71, 0x60, 0x79, /// 0x1f30 + 0x40, 0x1c, 0x60, 0x71, 0x11, 0xe0, 0x00, 0x98, 0xc0, 0x7f, 0x40, 0x07, 0x01, 0xd5, 0x08, 0x20, /// 0x1f40 + 0xb4, 0xe7, 0xff, 0xf7, 0x66, 0xfe, 0x01, 0x20, 0xfe, 0xbd, 0x11, 0x5c, 0x73, 0x46, 0x49, 0x08, /// 0x1f50 + 0x19, 0x43, 0x11, 0x54, 0xa0, 0x79, 0x40, 0x1c, 0xa0, 0x71, 0x00, 0x20, 0xfe, 0xbd, 0xfe, 0xb5, /// 0x1f60 + 0x20, 0x4c, 0x24, 0x48, 0xa1, 0x89, 0x26, 0x46, 0x00, 0x27, 0x40, 0x3e, 0x81, 0x42, 0x09, 0xd1, /// 0x1f70 + 0x60, 0x8a, 0x41, 0x1e, 0x61, 0x82, 0x03, 0xd3, 0x30, 0x7b, 0x40, 0x1e, 0xf0, 0x72, 0xed, 0xe0, /// 0x1f80 + 0xf7, 0x72, 0xeb, 0xe0, 0x16, 0x48, 0x22, 0x8a, 0x20, 0x38, 0x01, 0x46, 0x15, 0x4d, 0x40, 0x31, /// 0x1f90 + 0x80, 0x30, 0x40, 0x35, 0x01, 0x91, 0x00, 0x90, 0x00, 0x2a, 0x06, 0xd1, 0x2f, 0x70, 0x6f, 0x71, /// 0x1fa0 + 0xaf, 0x71, 0x0f, 0x20, 0x60, 0x82, 0xfe, 0xf7, 0x70, 0xfc, 0x9b, 0xe0, 0x41, 0x00, 0x0e, 0x4a, /// 0x1fb0 + 0x40, 0x1c, 0x89, 0x18, 0xff, 0x31, 0xc1, 0x31, 0x00, 0x07, 0x09, 0x8d, 0x00, 0x0f, 0x60, 0x72, /// 0x1fc0 + 0xa0, 0x89, 0x40, 0x1c, 0x80, 0xb2, 0xa0, 0x81, 0x03, 0x28, 0x2f, 0xd8, 0x01, 0x28, 0x13, 0xd0, /// 0x1fd0 + 0xe2, 0x89, 0x51, 0x18, 0x89, 0xb2, 0xe1, 0x81, 0x03, 0x28, 0x12, 0xd1, 0x12, 0xe0, 0x00, 0x00, /// 0x1fe0 + 0x20, 0x00, 0x00, 0x20, 0x30, 0x03, 0x00, 0x20, 0x30, 0x01, 0x00, 0x20, 0xb0, 0x01, 0x00, 0x20, /// 0x1ff0 + 0x70, 0x17, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xe7, 0x81, 0xef, 0x70, 0xaf, 0x70, 0xaf, 0x72, /// 0x2000 + 0xef, 0x72, 0x6f, 0xe0, 0x49, 0x1c, 0x48, 0x08, 0xe0, 0x81, 0x01, 0x99, 0x08, 0x81, 0x00, 0x99, /// 0x2010 + 0x09, 0x78, 0xc9, 0x07, 0x01, 0xd1, 0xfe, 0xf7, 0xe1, 0xf9, 0xe1, 0x89, 0x50, 0x4a, 0x48, 0x08, /// 0x2020 + 0x80, 0x18, 0xff, 0xf7, 0x23, 0xfa, 0x01, 0x99, 0xc8, 0x83, 0x5b, 0xe0, 0x00, 0x98, 0xc2, 0x7a, /// 0x2030 + 0x06, 0x20, 0x93, 0x07, 0x9b, 0x0f, 0x98, 0x40, 0x80, 0x1e, 0x52, 0x07, 0xe2, 0x89, 0x03, 0xd5, /// 0x2040 + 0x10, 0x18, 0x88, 0x42, 0x03, 0xd3, 0x20, 0xe0, 0x10, 0x1a, 0x88, 0x42, 0x1d, 0xd9, 0xe8, 0x78, /// 0x2050 + 0x40, 0x1c, 0xc0, 0xb2, 0xe8, 0x70, 0x0a, 0x28, 0x53, 0xd8, 0xa8, 0x7a, 0x00, 0x28, 0x12, 0xd0, /// 0x2060 + 0xa8, 0x78, 0x05, 0x28, 0x01, 0xd9, 0x00, 0x20, 0x05, 0xe0, 0x02, 0x28, 0x0b, 0xd9, 0xe8, 0x7a, /// 0x2070 + 0x00, 0x28, 0x06, 0xd0, 0x01, 0x20, 0xff, 0xf7, 0xaa, 0xfe, 0x00, 0x28, 0x41, 0xd1, 0xef, 0x72, /// 0x2080 + 0x01, 0xe0, 0x01, 0x20, 0xe8, 0x72, 0xaf, 0x70, 0x2c, 0xe0, 0xa8, 0x78, 0x40, 0x1c, 0xc0, 0xb2, /// 0x2090 + 0xa8, 0x70, 0xa9, 0x7a, 0x00, 0x29, 0x0c, 0xd0, 0xe9, 0x7a, 0x00, 0x29, 0x03, 0xd0, 0x05, 0x28, /// 0x20a0 + 0x0c, 0xd9, 0x01, 0x20, 0x02, 0xe0, 0x0a, 0x28, 0x08, 0xd9, 0x00, 0x20, 0xff, 0xf7, 0x8f, 0xfe, /// 0x20b0 + 0x27, 0xe0, 0xe8, 0x78, 0x02, 0x28, 0x01, 0xd9, 0x01, 0x20, 0xa8, 0x72, 0xe8, 0x78, 0x05, 0x28, /// 0x20c0 + 0x01, 0xd9, 0x00, 0x20, 0x05, 0xe0, 0x02, 0x28, 0x0b, 0xd9, 0xe8, 0x7a, 0x00, 0x28, 0x06, 0xd0, /// 0x20d0 + 0x01, 0x20, 0xff, 0xf7, 0x7c, 0xfe, 0x00, 0x28, 0x13, 0xd1, 0xef, 0x72, 0x01, 0xe0, 0x01, 0x20, /// 0x20e0 + 0xe8, 0x72, 0xef, 0x70, 0x21, 0x7a, 0x60, 0x7a, 0x81, 0x42, 0x00, 0xd0, 0x5e, 0xe7, 0x30, 0x7b, /// 0x20f0 + 0x40, 0x1e, 0xf0, 0x72, 0x20, 0x8a, 0x40, 0x1c, 0x80, 0xb2, 0x20, 0x82, 0x61, 0x8a, 0x88, 0x42, /// 0x2100 + 0x2c, 0xd9, 0xfe, 0xf7, 0x54, 0xfb, 0x00, 0x20, 0xc0, 0x43, 0xa0, 0x81, 0x15, 0x48, 0xc7, 0x77, /// 0x2110 + 0x70, 0x7c, 0x00, 0x28, 0x01, 0xd0, 0x16, 0x20, 0x00, 0xe0, 0x05, 0x20, 0x70, 0x74, 0x28, 0x78, /// 0x2120 + 0x00, 0x28, 0x01, 0xd0, 0x70, 0x7c, 0x10, 0xe0, 0x60, 0x8a, 0x0f, 0x28, 0x04, 0xd9, 0x21, 0x8a, /// 0x2130 + 0x40, 0x1a, 0x71, 0x7c, 0x40, 0x18, 0x08, 0xe0, 0x20, 0x8a, 0x31, 0x7b, 0x02, 0x1d, 0x8a, 0x42, /// 0x2140 + 0x02, 0xd2, 0xc0, 0xb2, 0x08, 0x1a, 0x00, 0xe0, 0x04, 0x20, 0x60, 0x82, 0x20, 0x8a, 0x61, 0x8a, /// 0x2150 + 0x42, 0x18, 0x31, 0x7b, 0x8a, 0x42, 0x01, 0xd2, 0x08, 0x1a, 0x60, 0x82, 0x00, 0x20, 0xfe, 0xbd, /// 0x2160 + 0x00, 0xdc, 0x05, 0x00, 0x50, 0x03, 0x00, 0x20, 0x10, 0xb5, 0xfe, 0x48, 0x41, 0x7b, 0x0a, 0x07, /// 0x2170 + 0x03, 0xd5, 0xf7, 0x22, 0x11, 0x40, 0x41, 0x73, 0x10, 0xbd, 0x08, 0x22, 0x11, 0x43, 0x41, 0x73, /// 0x2180 + 0xfe, 0xf7, 0x18, 0xfd, 0x10, 0xbd, 0xf7, 0x49, 0x00, 0x20, 0xf7, 0x4a, 0x40, 0x31, 0x89, 0x6e, /// 0x2190 + 0x92, 0x68, 0x06, 0xe0, 0x0b, 0x46, 0x53, 0x40, 0x1b, 0x06, 0x00, 0xd0, 0x40, 0x1c, 0x09, 0x0a, /// 0x21a0 + 0x12, 0x0a, 0x0b, 0x46, 0x13, 0x43, 0xf5, 0xd1, 0x70, 0x47, 0xee, 0x4a, 0x10, 0xb5, 0xef, 0x4b, /// 0x21b0 + 0x40, 0x3a, 0x93, 0x85, 0x20, 0x32, 0x90, 0x73, 0xd1, 0x73, 0x11, 0x46, 0x01, 0x20, 0x80, 0x31, /// 0x21c0 + 0xc8, 0x77, 0x02, 0x20, 0xfe, 0xf7, 0x8d, 0xfc, 0x10, 0xbd, 0xe6, 0x49, 0x10, 0xb5, 0xe8, 0x4a, /// 0x21d0 + 0x40, 0x39, 0x8a, 0x85, 0x20, 0x31, 0x88, 0x73, 0x04, 0x20, 0x80, 0x31, 0xc8, 0x77, 0x01, 0x20, /// 0x21e0 + 0xfe, 0xf7, 0x7f, 0xfc, 0x10, 0xbd, 0xf8, 0xb5, 0x09, 0x21, 0xe2, 0x4b, 0xdd, 0x4c, 0x89, 0x01, /// 0x21f0 + 0x5d, 0x18, 0xa0, 0x34, 0xff, 0x33, 0xe0, 0x79, 0xff, 0x33, 0x02, 0x26, 0xda, 0x4a, 0x02, 0x33, /// 0x2200 + 0x00, 0x28, 0x02, 0xd0, 0x01, 0x28, 0x0b, 0xd0, 0x3e, 0xe0, 0xd0, 0x79, 0xc0, 0x07, 0x05, 0xd0, /// 0x2210 + 0x31, 0x20, 0xff, 0xf7, 0xda, 0xff, 0x01, 0x20, 0xe0, 0x71, 0xf8, 0xbd, 0xe6, 0x71, 0x0c, 0xe0, /// 0x2220 + 0x28, 0x78, 0x00, 0x07, 0xfa, 0xd5, 0x18, 0x7d, 0x31, 0x28, 0xf7, 0xd1, 0x58, 0x7d, 0x10, 0x71, /// 0x2230 + 0x98, 0x7d, 0x50, 0x71, 0xd8, 0x7d, 0x90, 0x71, 0xf0, 0xe7, 0xe7, 0x79, 0x78, 0x08, 0xf9, 0x07, /// 0x2240 + 0x7f, 0x1c, 0xff, 0xb2, 0xe7, 0x71, 0xbc, 0x46, 0xc7, 0x4f, 0x80, 0x3f, 0x00, 0x29, 0x0c, 0xd0, /// 0x2250 + 0x29, 0x78, 0xc9, 0x07, 0x18, 0xd0, 0x39, 0x18, 0xc6, 0x4f, 0x80, 0x31, 0x38, 0x18, 0x13, 0x27, /// 0x2260 + 0x7f, 0x01, 0x09, 0x7b, 0xc0, 0x19, 0x01, 0x72, 0x0e, 0xe0, 0x3f, 0x18, 0x80, 0x37, 0x39, 0x7b, /// 0x2270 + 0x3f, 0x7a, 0xb9, 0x42, 0x05, 0xd0, 0xc2, 0x07, 0x00, 0xd1, 0x04, 0x20, 0xff, 0xf7, 0x95, 0xff, /// 0x2280 + 0xf8, 0xbd, 0x60, 0x46, 0x40, 0x1c, 0xe0, 0x71, 0xe0, 0x79, 0x08, 0x28, 0xd5, 0xd3, 0x41, 0x1c, /// 0x2290 + 0xe1, 0x71, 0x08, 0x28, 0x06, 0xd0, 0x28, 0x78, 0xc0, 0x07, 0x08, 0xd0, 0x98, 0x6e, 0x90, 0x60, /// 0x22a0 + 0xd6, 0x71, 0x06, 0xe0, 0xff, 0xf7, 0x6f, 0xff, 0xc1, 0xb2, 0x00, 0x20, 0xe6, 0xe7, 0x04, 0x20, /// 0x22b0 + 0xd0, 0x71, 0xac, 0x48, 0x05, 0x21, 0x80, 0x38, 0x81, 0x72, 0xf8, 0xbd, 0xa9, 0x48, 0x10, 0xb5, /// 0x22c0 + 0xad, 0x49, 0x40, 0x38, 0xc1, 0x62, 0xa7, 0x49, 0x01, 0x20, 0x60, 0x31, 0xc8, 0x77, 0xfe, 0xf7, /// 0x22d0 + 0x08, 0xfc, 0x10, 0xbd, 0xa3, 0x48, 0x10, 0xb5, 0xa8, 0x49, 0x40, 0x38, 0x81, 0x85, 0xa2, 0x48, /// 0x22e0 + 0x60, 0x38, 0x81, 0x7e, 0x9f, 0x48, 0x20, 0x38, 0x81, 0x73, 0x01, 0x20, 0xfe, 0xf7, 0xf9, 0xfb, /// 0x22f0 + 0x10, 0xbd, 0x9c, 0x48, 0x10, 0xb5, 0x60, 0x30, 0x00, 0x7a, 0xff, 0xf7, 0x47, 0xfc, 0x99, 0x49, /// 0x2300 + 0xc4, 0xb2, 0x68, 0x31, 0x08, 0x46, 0x62, 0x1c, 0x7b, 0x38, 0xfe, 0xf7, 0x29, 0xfb, 0x95, 0x48, /// 0x2310 + 0x0c, 0x21, 0x20, 0x38, 0x01, 0x73, 0x20, 0x46, 0xfe, 0xf7, 0xe3, 0xfb, 0x10, 0xbd, 0x91, 0x49, /// 0x2320 + 0x10, 0xb5, 0x0c, 0x22, 0x20, 0x39, 0x0a, 0x73, 0x15, 0x22, 0x4a, 0x73, 0x88, 0x73, 0x01, 0x20, /// 0x2330 + 0xfe, 0xf7, 0xd7, 0xfb, 0x10, 0xbd, 0x10, 0xb5, 0x8e, 0x48, 0x00, 0x78, 0xff, 0xf7, 0x26, 0xfc, /// 0x2340 + 0x04, 0x46, 0x42, 0x1c, 0x87, 0x48, 0x8b, 0x49, 0x13, 0x38, 0xfe, 0xf7, 0x09, 0xfb, 0x85, 0x48, /// 0x2350 + 0x0c, 0x21, 0x20, 0x38, 0x01, 0x73, 0x20, 0x46, 0xfe, 0xf7, 0xc3, 0xfb, 0x10, 0xbd, 0xf0, 0xb5, /// 0x2360 + 0x81, 0x48, 0x01, 0x78, 0x49, 0x06, 0x01, 0xd5, 0x00, 0x20, 0xf0, 0xbd, 0x01, 0x8a, 0x7e, 0x48, /// 0x2370 + 0x40, 0x38, 0x00, 0x88, 0x0b, 0x1a, 0x02, 0xd5, 0x01, 0x25, 0x59, 0x42, 0x01, 0xe0, 0x00, 0x25, /// 0x2380 + 0x19, 0x46, 0x78, 0x48, 0x04, 0x26, 0xa0, 0x30, 0x42, 0x7c, 0x94, 0x08, 0x34, 0x1b, 0x4c, 0x43, /// 0x2390 + 0x61, 0x09, 0x05, 0xd0, 0x00, 0x2d, 0x01, 0xd0, 0x00, 0x24, 0x02, 0xe0, 0x02, 0x24, 0x00, 0xe0, /// 0x23a0 + 0x01, 0x24, 0x10, 0x26, 0x86, 0x57, 0x02, 0x27, 0x7e, 0x40, 0x20, 0x27, 0xa6, 0x42, 0x05, 0xd1, /// 0x23b0 + 0x0f, 0x2a, 0x01, 0xd2, 0x52, 0x1c, 0x0e, 0xe0, 0x87, 0x74, 0x0e, 0xe0, 0x86, 0x7c, 0x00, 0x2e, /// 0x23c0 + 0x0b, 0xd0, 0x76, 0x1e, 0x36, 0x06, 0x36, 0x0e, 0x86, 0x74, 0x06, 0xd1, 0x00, 0x2a, 0x04, 0xd0, /// 0x23d0 + 0xfc, 0x26, 0x52, 0x1e, 0x32, 0x40, 0x42, 0x74, 0xee, 0xe7, 0x04, 0x74, 0x00, 0x24, 0x00, 0x29, /// 0x23e0 + 0x0b, 0xd0, 0xc4, 0x73, 0x84, 0x73, 0x08, 0x22, 0x02, 0x73, 0x7f, 0x29, 0x00, 0xd9, 0x7f, 0x21, /// 0x23f0 + 0x00, 0x2d, 0x00, 0xd0, 0x49, 0x42, 0x48, 0xb2, 0xf0, 0xbd, 0x82, 0x7b, 0xd2, 0x18, 0x56, 0xb2, /// 0x2400 + 0x86, 0x73, 0xc2, 0x7b, 0x52, 0x1c, 0xd3, 0xb2, 0xc3, 0x73, 0x9a, 0x07, 0xf0, 0xd1, 0x02, 0x7b, /// 0x2410 + 0x96, 0x42, 0x02, 0xdd, 0x01, 0x21, 0x00, 0x25, 0x04, 0xe0, 0x57, 0x42, 0xbe, 0x42, 0x07, 0xda, /// 0x2420 + 0x01, 0x21, 0x0d, 0x46, 0x0c, 0x2b, 0x01, 0xd3, 0x52, 0x1c, 0x02, 0x73, 0x84, 0x73, 0xdc, 0xe7, /// 0x2430 + 0xc4, 0x73, 0x43, 0x7b, 0x5b, 0x1c, 0xdb, 0xb2, 0x43, 0x73, 0x20, 0x2b, 0xf6, 0xd9, 0x44, 0x73, /// 0x2440 + 0x04, 0x2a, 0xf3, 0xd9, 0x52, 0x1e, 0xf0, 0xe7, 0xfe, 0xb5, 0x47, 0x48, 0x4c, 0x49, 0x80, 0x38, /// 0x2450 + 0x80, 0x8f, 0x45, 0x4e, 0x48, 0x43, 0x00, 0x0c, 0x40, 0x3e, 0xb0, 0x87, 0x30, 0x88, 0x41, 0x43, /// 0x2460 + 0x09, 0x0c, 0xf1, 0x87, 0x47, 0x49, 0x88, 0x42, 0x05, 0xd2, 0x47, 0x48, 0x01, 0x8b, 0x1f, 0x22, /// 0x2470 + 0x12, 0x02, 0x91, 0x43, 0x01, 0x83, 0x3b, 0x4f, 0x3e, 0x4d, 0x78, 0x7b, 0x3a, 0x4c, 0x41, 0x07, /// 0x2480 + 0xe8, 0x1d, 0xf9, 0x30, 0x80, 0x3c, 0x01, 0x90, 0x20, 0x46, 0x80, 0x30, 0xff, 0x35, 0xa0, 0x34, /// 0x2490 + 0x81, 0x35, 0x00, 0x90, 0x00, 0x29, 0x4e, 0xda, 0x70, 0x8f, 0xfe, 0xf7, 0x75, 0xf8, 0xb0, 0x80, /// 0x24a0 + 0x00, 0x99, 0x09, 0x78, 0xc9, 0x07, 0x2b, 0xd1, 0xfe, 0xf7, 0x1a, 0xfc, 0xb8, 0x7b, 0xfe, 0xf7, /// 0x24b0 + 0x14, 0xf8, 0xb9, 0x7b, 0x35, 0x4a, 0x04, 0x29, 0x02, 0xd2, 0x01, 0x20, 0x90, 0x80, 0x03, 0xe0, /// 0x24c0 + 0x03, 0x20, 0x90, 0x80, 0x0a, 0x29, 0x01, 0xd2, 0x00, 0x20, 0x08, 0xe0, 0x0f, 0x29, 0x01, 0xd2, /// 0x24d0 + 0x01, 0x20, 0x04, 0xe0, 0x14, 0x29, 0x01, 0xd2, 0x02, 0x20, 0x00, 0xe0, 0x03, 0x20, 0x2b, 0x4a, /// 0x24e0 + 0x12, 0x88, 0xf0, 0x23, 0x9a, 0x43, 0x80, 0x01, 0x02, 0x43, 0x28, 0x48, 0x02, 0x80, 0x07, 0x29, /// 0x24f0 + 0x01, 0xd9, 0x16, 0x21, 0x00, 0xe0, 0x06, 0x21, 0x24, 0x48, 0x81, 0x81, 0xfe, 0xf7, 0xc0, 0xfb, /// 0x2500 + 0xff, 0xf7, 0x2d, 0xff, 0x18, 0x49, 0x40, 0x31, 0x0a, 0x79, 0x94, 0x46, 0x92, 0x07, 0x6b, 0xd1, /// 0x2510 + 0x8b, 0x79, 0x5a, 0x1c, 0x8a, 0x71, 0x03, 0x2b, 0x66, 0xd9, 0x72, 0x7b, 0x12, 0x07, 0x63, 0xd4, /// 0x2520 + 0x62, 0x46, 0x06, 0x23, 0x1a, 0x43, 0x0a, 0x71, 0x01, 0x9a, 0x13, 0x68, 0x00, 0x9a, 0x93, 0x64, /// 0x2530 + 0x00, 0x22, 0x4a, 0x71, 0x58, 0xe0, 0xff, 0xf7, 0x12, 0xff, 0x79, 0x7b, 0x0a, 0x07, 0x53, 0xd4, /// 0x2540 + 0x00, 0x9a, 0x13, 0x8a, 0x0d, 0x4a, 0xaa, 0x32, 0x93, 0x42, 0x28, 0xd8, 0x32, 0x88, 0x9a, 0x42, /// 0x2550 + 0x06, 0xd8, 0x01, 0x9a, 0x13, 0x68, 0x2a, 0x6e, 0x9a, 0x1a, 0x0d, 0x4b, 0x9a, 0x42, 0x1c, 0xdb, /// 0x2560 + 0x22, 0x7a, 0x17, 0xe0, 0xf0, 0x02, 0x00, 0x20, 0x80, 0x00, 0x00, 0x20, 0x0c, 0x20, 0x00, 0x00, /// 0x2570 + 0x0c, 0x07, 0x00, 0x00, 0x30, 0x01, 0x00, 0x20, 0x0c, 0x09, 0x00, 0x00, 0x0c, 0x05, 0x00, 0x00, /// 0x2580 + 0x80, 0x20, 0x05, 0x00, 0x83, 0x02, 0x00, 0x00, 0x00, 0x30, 0x00, 0x40, 0x00, 0x34, 0x00, 0x40, /// 0x2590 + 0x88, 0x13, 0x00, 0x00, 0x10, 0x23, 0x1a, 0x43, 0x11, 0xe0, 0x37, 0x20, 0x10, 0xe0, 0x22, 0x7a, /// 0x25a0 + 0x94, 0x46, 0x93, 0x07, 0x06, 0xd5, 0x01, 0x9b, 0x2a, 0x6e, 0x1b, 0x68, 0x9b, 0x1a, 0xfe, 0x4a, /// 0x25b0 + 0x93, 0x42, 0x19, 0xdb, 0x62, 0x46, 0x10, 0x23, 0x1a, 0x43, 0x52, 0x08, 0x52, 0x00, 0x22, 0x72, /// 0x25c0 + 0x22, 0x7a, 0x93, 0x07, 0x10, 0xd4, 0xd3, 0x06, 0x0e, 0xd5, 0x10, 0x07, 0x02, 0xd5, 0xf8, 0x48, /// 0x25d0 + 0xf6, 0x4a, 0x42, 0x81, 0x07, 0x20, 0x01, 0x43, 0x79, 0x73, 0x32, 0x20, 0x68, 0x66, 0x01, 0x98, /// 0x25e0 + 0x00, 0x68, 0x28, 0x38, 0xa8, 0x65, 0xfe, 0xbd, 0xf1, 0x49, 0x42, 0x1d, 0x20, 0x31, 0x0a, 0x2a, /// 0x25f0 + 0x02, 0xd9, 0x7a, 0x7b, 0x12, 0x07, 0x0a, 0xd5, 0x00, 0x22, 0x8a, 0x77, 0x0a, 0x7f, 0x00, 0x2a, /// 0x2600 + 0x02, 0xd0, 0x52, 0x1e, 0x0a, 0x77, 0x09, 0xe0, 0x96, 0x21, 0x69, 0x66, 0x06, 0xe0, 0x32, 0x22, /// 0x2610 + 0x6a, 0x66, 0x8a, 0x7f, 0xff, 0x2a, 0x01, 0xd0, 0x52, 0x1c, 0x8a, 0x77, 0x21, 0x7a, 0x0a, 0x07, /// 0x2620 + 0x11, 0xd5, 0x82, 0x1d, 0x0c, 0x2a, 0x04, 0xd8, 0x8a, 0x07, 0x09, 0xd5, 0x49, 0x08, 0x49, 0x00, /// 0x2630 + 0x08, 0xe0, 0xdf, 0x4a, 0x80, 0x32, 0x52, 0x7d, 0x03, 0x2a, 0x04, 0xd9, 0x8a, 0x07, 0x02, 0xd4, /// 0x2640 + 0xfb, 0x22, 0x11, 0x40, 0x21, 0x72, 0xb1, 0x88, 0x32, 0x29, 0x15, 0xd2, 0x1e, 0x28, 0x03, 0xdd, /// 0x2650 + 0x00, 0x99, 0x49, 0x8c, 0x27, 0x29, 0x04, 0xd0, 0x00, 0x99, 0x49, 0x8c, 0x28, 0x29, 0x02, 0xd0, /// 0x2660 + 0x0a, 0xe0, 0x1e, 0x20, 0x08, 0xe0, 0x09, 0x21, 0xc9, 0x43, 0x0a, 0x28, 0x01, 0xdd, 0x0a, 0x20, /// 0x2670 + 0x02, 0xe0, 0x88, 0x42, 0x00, 0xda, 0x08, 0x46, 0xc3, 0x21, 0x89, 0x00, 0xa9, 0x85, 0xcc, 0x49, /// 0x2680 + 0x60, 0x39, 0x88, 0x73, 0x00, 0x28, 0x01, 0xd0, 0x01, 0x20, 0x78, 0x74, 0x01, 0x20, 0xfe, 0xf7, /// 0x2690 + 0x28, 0xfa, 0xc8, 0x48, 0x01, 0x8c, 0x49, 0x1c, 0x01, 0x84, 0xfe, 0xbd, 0xc4, 0x48, 0x10, 0xb5, /// 0x26a0 + 0x0c, 0x21, 0x60, 0x38, 0x01, 0x73, 0x38, 0x21, 0x41, 0x73, 0x0a, 0x21, 0x81, 0x73, 0xc1, 0x49, /// 0x26b0 + 0x40, 0x31, 0x09, 0x8f, 0xc1, 0x73, 0x09, 0x0a, 0x01, 0x74, 0xbd, 0x49, 0x10, 0x20, 0x20, 0x31, /// 0x26c0 + 0xc8, 0x77, 0x03, 0x20, 0xfe, 0xf7, 0x0d, 0xfa, 0x10, 0xbd, 0xb9, 0x48, 0x10, 0xb5, 0x0c, 0x21, /// 0x26d0 + 0x60, 0x38, 0x01, 0x73, 0x38, 0x21, 0x41, 0x73, 0x3b, 0x21, 0x81, 0x73, 0x02, 0x21, 0xc1, 0x73, /// 0x26e0 + 0xbb, 0x21, 0x01, 0x74, 0xb2, 0x49, 0x10, 0x20, 0x20, 0x31, 0xc8, 0x77, 0x03, 0x20, 0xfe, 0xf7, /// 0x26f0 + 0xf8, 0xf9, 0x10, 0xbd, 0xae, 0x4a, 0x10, 0xb5, 0x0c, 0x23, 0x60, 0x3a, 0x13, 0x73, 0xa4, 0x23, /// 0x2700 + 0x53, 0x73, 0x90, 0x73, 0xaa, 0x48, 0x08, 0x22, 0x51, 0x38, 0xfe, 0xf7, 0x29, 0xf9, 0xa8, 0x49, /// 0x2710 + 0x10, 0x20, 0x20, 0x31, 0xc8, 0x77, 0x0c, 0x20, 0xfe, 0xf7, 0xe3, 0xf9, 0x10, 0xbd, 0x10, 0xb5, /// 0x2720 + 0xa5, 0xa1, 0x03, 0xc9, 0xa2, 0xb0, 0x01, 0x91, 0xa2, 0x49, 0x00, 0x90, 0x14, 0x31, 0x08, 0x22, /// 0x2730 + 0x0c, 0x46, 0x1c, 0xa8, 0xfe, 0xf7, 0x14, 0xf9, 0x08, 0x22, 0x69, 0x46, 0x1e, 0xa8, 0xfe, 0xf7, /// 0x2740 + 0x0f, 0xf9, 0x02, 0xa8, 0xfe, 0xf7, 0xe6, 0xfc, 0x10, 0x22, 0x1c, 0xa9, 0x02, 0xa8, 0xfe, 0xf7, /// 0x2750 + 0x27, 0xfe, 0x98, 0x49, 0x02, 0xa8, 0xb0, 0x31, 0xfe, 0xf7, 0xf4, 0xfd, 0x21, 0x46, 0x08, 0x20, /// 0x2760 + 0xff, 0xf7, 0xc8, 0xff, 0x22, 0xb0, 0x10, 0xbd, 0x92, 0x48, 0x10, 0xb5, 0x40, 0x30, 0x00, 0x7c, /// 0x2770 + 0xff, 0xf7, 0x0c, 0xfa, 0x04, 0x46, 0x42, 0x1c, 0x8e, 0x49, 0x8d, 0x48, 0x50, 0x31, 0x53, 0x38, /// 0x2780 + 0xfe, 0xf7, 0xee, 0xf8, 0x8a, 0x48, 0x0c, 0x21, 0x60, 0x38, 0x01, 0x73, 0x88, 0x49, 0x10, 0x20, /// 0x2790 + 0x20, 0x31, 0xc8, 0x77, 0x20, 0x46, 0xfe, 0xf7, 0xa4, 0xf9, 0x10, 0xbd, 0xfe, 0xb5, 0x85, 0x4d, /// 0x27a0 + 0x80, 0x35, 0x28, 0x7e, 0x2e, 0x46, 0x40, 0x1e, 0xc2, 0xb2, 0x20, 0x36, 0x2a, 0x76, 0x00, 0x2a, /// 0x27b0 + 0x1a, 0xd0, 0x80, 0x49, 0x9c, 0x20, 0x40, 0x5a, 0x40, 0x31, 0x00, 0x91, 0x08, 0x2a, 0x42, 0xd3, /// 0x27c0 + 0x91, 0x07, 0x10, 0xd1, 0x00, 0x99, 0x6b, 0x8b, 0x09, 0x88, 0x59, 0x18, 0x89, 0xb2, 0x69, 0x83, /// 0x27d0 + 0x00, 0x9b, 0x5b, 0x8f, 0xc0, 0x18, 0x80, 0xb2, 0xa8, 0x83, 0x08, 0x2a, 0x03, 0xd1, 0x09, 0x09, /// 0x27e0 + 0x69, 0x83, 0x00, 0x09, 0xa8, 0x83, 0xfe, 0xbd, 0x71, 0x48, 0xfd, 0x21, 0x40, 0x30, 0x02, 0x78, /// 0x27f0 + 0x70, 0x48, 0xd3, 0x07, 0xa0, 0x30, 0x00, 0x7a, 0x01, 0x40, 0x00, 0x2b, 0x16, 0xd0, 0x02, 0x07, /// 0x2800 + 0xf1, 0xd5, 0xc2, 0x07, 0x01, 0xd0, 0x40, 0x21, 0x0d, 0xe0, 0x82, 0x07, 0x01, 0xd5, 0x31, 0x72, /// 0x2810 + 0xfe, 0xbd, 0x41, 0x07, 0x06, 0xd4, 0xf7, 0x21, 0x08, 0x40, 0x30, 0x72, 0x64, 0x48, 0x68, 0x49, /// 0x2820 + 0x41, 0x81, 0xfe, 0xbd, 0x80, 0x21, 0x08, 0x43, 0x30, 0x72, 0xfe, 0xbd, 0x92, 0x07, 0xfc, 0xd5, /// 0x2830 + 0x02, 0x07, 0xfa, 0xd5, 0xc2, 0x07, 0xf8, 0xd1, 0x82, 0x07, 0xf6, 0xd5, 0x40, 0x06, 0xf4, 0xd5, /// 0x2840 + 0xbf, 0x20, 0x01, 0x40, 0xe3, 0xe7, 0x06, 0x2a, 0xef, 0xd8, 0xfd, 0xf7, 0x9d, 0xfe, 0x04, 0x46, /// 0x2850 + 0x68, 0x8b, 0x0b, 0x21, 0x09, 0x07, 0x40, 0x18, 0xfe, 0xf7, 0xca, 0xfa, 0x05, 0x20, 0x40, 0x07, /// 0x2860 + 0x20, 0x43, 0xfe, 0xf7, 0xc5, 0xfa, 0x53, 0x48, 0x81, 0x8f, 0x0d, 0x20, 0x00, 0x07, 0x08, 0x18, /// 0x2870 + 0xfe, 0xf7, 0xbe, 0xfa, 0x00, 0x98, 0x0f, 0x21, 0x00, 0x89, 0x09, 0x07, 0x40, 0x18, 0xfe, 0xf7, /// 0x2880 + 0xb7, 0xfa, 0x50, 0x48, 0x00, 0x88, 0x00, 0x0b, 0xc1, 0x07, 0x05, 0xd0, 0x40, 0x07, 0x80, 0x0f, /// 0x2890 + 0x0a, 0x21, 0x40, 0x1c, 0x48, 0x43, 0x04, 0x19, 0x68, 0x8b, 0x15, 0x21, 0x61, 0x43, 0x48, 0x43, /// 0x28a0 + 0x07, 0x0b, 0x00, 0x20, 0x68, 0x83, 0xa8, 0x83, 0x07, 0x21, 0x38, 0x46, 0x49, 0x07, 0x08, 0x43, /// 0x28b0 + 0xfe, 0xf7, 0x9e, 0xfa, 0x44, 0x48, 0x80, 0x8b, 0xc0, 0x07, 0x0a, 0xd0, 0x43, 0x48, 0x81, 0x88, /// 0x28c0 + 0x89, 0x07, 0x89, 0x0f, 0x01, 0x29, 0x06, 0xd0, 0x41, 0x49, 0x8c, 0x42, 0x05, 0xd9, 0x05, 0x20, /// 0x28d0 + 0x07, 0xe0, 0x07, 0x20, 0x05, 0xe0, 0x00, 0x20, 0x03, 0xe0, 0x00, 0x88, 0x00, 0x06, 0x80, 0x0f, /// 0x28e0 + 0x40, 0x1c, 0x68, 0x76, 0x00, 0x06, 0xc1, 0x0d, 0x32, 0x48, 0x09, 0x18, 0x60, 0x31, 0x0a, 0x7a, /// 0x28f0 + 0x7a, 0x43, 0xd3, 0x09, 0x09, 0x22, 0x8a, 0x56, 0x0a, 0x21, 0x4a, 0x43, 0xd4, 0x18, 0x00, 0xd5, /// 0x2900 + 0x00, 0x24, 0x34, 0x4f, 0x84, 0x84, 0x3b, 0x46, 0xff, 0x33, 0x2a, 0x7a, 0x03, 0x21, 0x81, 0x33, /// 0x2910 + 0xff, 0x37, 0x89, 0x07, 0xa1, 0x37, 0x01, 0x93, 0x31, 0x2a, 0x0b, 0xd0, 0x00, 0x22, 0x2a, 0x76, /// 0x2920 + 0x27, 0x4a, 0x80, 0x8f, 0x87, 0x3a, 0x90, 0x42, 0x66, 0xd8, 0x2b, 0x48, 0x84, 0x42, 0x68, 0xd9, /// 0x2930 + 0x04, 0x46, 0x66, 0xe0, 0x1e, 0x4a, 0x01, 0x20, 0x28, 0x76, 0x20, 0x32, 0xd0, 0x77, 0x30, 0x7a, /// 0x2940 + 0x02, 0x07, 0x0a, 0xd5, 0x80, 0x07, 0x01, 0xd5, 0x01, 0x26, 0x18, 0xe0, 0x18, 0x4a, 0x02, 0x26, /// 0x2950 + 0x80, 0x32, 0x50, 0x7d, 0x40, 0x1c, 0x50, 0x75, 0x11, 0xe0, 0x16, 0x4a, 0x00, 0x26, 0xc0, 0x32, /// 0x2960 + 0x10, 0x79, 0x83, 0x07, 0x0b, 0xd1, 0x00, 0x9b, 0x5b, 0x7b, 0x1b, 0x07, 0x07, 0xd5, 0x06, 0x23, /// 0x2970 + 0x18, 0x43, 0x10, 0x71, 0x19, 0x48, 0x00, 0x68, 0xa8, 0x64, 0x00, 0x20, 0x50, 0x71, 0x28, 0x78, /// 0x2980 + 0x00, 0x06, 0x00, 0xd5, 0x64, 0x08, 0x20, 0x46, 0x08, 0x43, 0xfe, 0xf7, 0x31, 0xfa, 0xa9, 0x7a, /// 0x2990 + 0x7d, 0x20, 0xc0, 0x00, 0x41, 0x43, 0x20, 0x04, 0xfe, 0xf7, 0x68, 0xfd, 0x10, 0x49, 0x88, 0x42, /// 0x29a0 + 0x00, 0xd9, 0x08, 0x46, 0x0f, 0x4a, 0x1f, 0xe0, 0x88, 0x13, 0x00, 0x00, 0x6c, 0x07, 0x00, 0x00, /// 0x29b0 + 0x30, 0x03, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0xf9, 0x6e, 0xd4, 0xc1, 0xe3, 0x2a, 0xb7, 0x58, /// 0x29c0 + 0xdc, 0x05, 0x00, 0x00, 0x20, 0x38, 0x00, 0x40, 0x00, 0x30, 0x00, 0x40, 0x00, 0x34, 0x00, 0x40, /// 0x29d0 + 0x52, 0x03, 0x00, 0x00, 0x30, 0x01, 0x00, 0x20, 0xe8, 0x26, 0x00, 0x00, 0x30, 0x02, 0x00, 0x20, /// 0x29e0 + 0xff, 0xff, 0x00, 0x00, 0x0c, 0x31, 0x00, 0x00, 0x01, 0x99, 0x8a, 0x85, 0xbe, 0x73, 0x01, 0x0a, /// 0x29f0 + 0xf9, 0x73, 0x38, 0x74, 0x03, 0x20, 0x11, 0xe0, 0xfe, 0x48, 0x84, 0x42, 0x00, 0xd9, 0x04, 0x46, /// 0x2a00 + 0x64, 0x08, 0x20, 0x46, 0x08, 0x43, 0xfe, 0xf7, 0xf3, 0xf9, 0xe0, 0x01, 0xfa, 0x49, 0xfe, 0xf7, /// 0x2a10 + 0x2d, 0xfd, 0x01, 0x99, 0xf9, 0x4a, 0x8a, 0x85, 0xb8, 0x73, 0x01, 0x20, 0xfe, 0xf7, 0x61, 0xf8, /// 0x2a20 + 0xfe, 0xbd, 0x10, 0xb5, 0xf6, 0x48, 0x01, 0x22, 0x41, 0x7b, 0x49, 0x1e, 0xc9, 0xb2, 0x41, 0x73, /// 0x2a30 + 0x8b, 0x07, 0xf4, 0x48, 0x00, 0x2b, 0x03, 0xda, 0x81, 0x8b, 0x11, 0x43, 0x81, 0x83, 0x10, 0xbd, /// 0x2a40 + 0xcb, 0x07, 0xf1, 0x49, 0x0c, 0xd0, 0x03, 0x8b, 0x8b, 0x43, 0x09, 0x21, 0x09, 0x02, 0x59, 0x18, /// 0x2a50 + 0x01, 0x83, 0xee, 0x48, 0x82, 0x80, 0x01, 0x88, 0x30, 0x22, 0x91, 0x43, 0x01, 0x80, 0x10, 0xbd, /// 0x2a60 + 0x02, 0x8b, 0x8a, 0x43, 0x1b, 0x21, 0x09, 0x02, 0x51, 0x18, 0x01, 0x83, 0xe8, 0x48, 0x40, 0x7b, /// 0x2a70 + 0x00, 0x07, 0x04, 0xd4, 0xe7, 0x48, 0x01, 0x78, 0x04, 0x22, 0x11, 0x43, 0x01, 0x70, 0xe0, 0x49, /// 0x2a80 + 0x0a, 0x20, 0xc0, 0x31, 0x08, 0x75, 0xe2, 0x48, 0x10, 0x22, 0x40, 0x30, 0x01, 0x78, 0x11, 0x43, /// 0x2a90 + 0x01, 0x70, 0x80, 0x38, 0x81, 0x8e, 0x40, 0x22, 0x11, 0x43, 0x81, 0x86, 0x10, 0x46, 0xfd, 0xf7, /// 0x2aa0 + 0x78, 0xfe, 0x10, 0xbd, 0xf0, 0xb5, 0xd6, 0x48, 0x8b, 0xb0, 0x40, 0x7b, 0x80, 0x07, 0x03, 0xd0, /// 0x2ab0 + 0xff, 0xf7, 0xb7, 0xff, 0x0b, 0xb0, 0xf0, 0xbd, 0xd5, 0x4f, 0xd1, 0x49, 0x80, 0x37, 0x38, 0x79, /// 0x2ac0 + 0xc0, 0x39, 0x3c, 0x46, 0x82, 0x07, 0x40, 0x3c, 0x06, 0x91, 0x00, 0x2a, 0x1a, 0xda, 0x09, 0x68, /// 0x2ad0 + 0xa2, 0x6c, 0x89, 0x1a, 0xff, 0x22, 0x2d, 0x32, 0x91, 0x42, 0x13, 0xd9, 0x79, 0x79, 0x03, 0x29, /// 0x2ae0 + 0x01, 0xd2, 0x04, 0x21, 0x0c, 0xe0, 0xca, 0x49, 0x01, 0x20, 0x40, 0x39, 0x8a, 0x8e, 0x00, 0x03, /// 0x2af0 + 0x02, 0x43, 0x8a, 0x86, 0xfd, 0xf7, 0x4d, 0xfe, 0x38, 0x79, 0xfd, 0x21, 0x08, 0x40, 0x01, 0x21, /// 0x2b00 + 0x08, 0x43, 0x38, 0x71, 0x60, 0x78, 0x41, 0x06, 0x13, 0xd5, 0x06, 0x99, 0xa2, 0x6c, 0x09, 0x68, /// 0x2b10 + 0x89, 0x1a, 0xff, 0x22, 0xf5, 0x32, 0x91, 0x42, 0x0b, 0xd9, 0xbf, 0x21, 0x08, 0x40, 0xbc, 0x49, /// 0x2b20 + 0x60, 0x70, 0x40, 0x39, 0x8a, 0x8e, 0xff, 0x20, 0x01, 0x30, 0x02, 0x43, 0x8a, 0x86, 0xfd, 0xf7, /// 0x2b30 + 0x30, 0xfe, 0x20, 0x7e, 0x00, 0x28, 0x02, 0xd0, 0xff, 0xf7, 0x30, 0xfe, 0xba, 0xe7, 0x06, 0x98, /// 0x2b40 + 0xaf, 0x4d, 0x00, 0x68, 0x40, 0x3d, 0x00, 0x90, 0xe9, 0x6d, 0x0a, 0x22, 0x41, 0x1a, 0x28, 0x46, /// 0x2b50 + 0x80, 0x30, 0x08, 0x90, 0x82, 0x5e, 0x00, 0x98, 0x91, 0x42, 0x03, 0xdb, 0xe8, 0x65, 0x48, 0x20, /// 0x2b60 + 0x20, 0x76, 0xe9, 0xe7, 0xa9, 0x6d, 0xa6, 0x4e, 0x41, 0x1a, 0xa9, 0x48, 0x6a, 0x6e, 0x20, 0x38, /// 0x2b70 + 0x60, 0x36, 0x05, 0x90, 0x91, 0x42, 0x7d, 0xdb, 0x00, 0x98, 0xa8, 0x65, 0x60, 0x78, 0x01, 0x07, /// 0x2b80 + 0x0b, 0xd5, 0xf7, 0x21, 0x08, 0x40, 0x32, 0x21, 0x69, 0x66, 0x10, 0x21, 0x08, 0x43, 0x60, 0x70, /// 0x2b90 + 0x00, 0x20, 0xb8, 0x81, 0xff, 0xf7, 0xe8, 0xfd, 0x8c, 0xe7, 0x39, 0x79, 0x0a, 0x07, 0x0f, 0xd5, /// 0x2ba0 + 0xa2, 0x78, 0x93, 0x06, 0x0c, 0xd5, 0xdf, 0x21, 0x0a, 0x40, 0xa2, 0x70, 0x32, 0x21, 0x69, 0x66, /// 0x2bb0 + 0x40, 0x21, 0x08, 0x43, 0x60, 0x70, 0x00, 0x98, 0xa0, 0x64, 0xff, 0xf7, 0xb0, 0xfd, 0x79, 0xe7, /// 0x2bc0 + 0x82, 0x06, 0x08, 0xd5, 0xdf, 0x21, 0x08, 0x40, 0x60, 0x70, 0x32, 0x20, 0x68, 0x66, 0xff, 0x20, /// 0x2bd0 + 0xfe, 0xf7, 0x22, 0xfc, 0x6e, 0xe7, 0x48, 0x07, 0x0d, 0xd5, 0xfb, 0x20, 0x01, 0x40, 0x39, 0x71, /// 0x2be0 + 0x32, 0x20, 0x68, 0x66, 0xff, 0xf7, 0x71, 0xfd, 0x06, 0x98, 0x00, 0x68, 0xa0, 0x64, 0x78, 0x79, /// 0x2bf0 + 0x40, 0x1c, 0x78, 0x71, 0x5e, 0xe7, 0x08, 0x07, 0x23, 0xd5, 0xa7, 0x78, 0xf8, 0x07, 0x20, 0xd0, /// 0x2c00 + 0x83, 0x48, 0x09, 0x90, 0x00, 0x8f, 0x64, 0x21, 0xfe, 0xf7, 0x30, 0xfc, 0x05, 0x99, 0x89, 0x7f, /// 0x2c10 + 0x08, 0x1a, 0x79, 0x08, 0x49, 0x00, 0x23, 0x30, 0xa1, 0x70, 0x00, 0x28, 0x00, 0xdc, 0x40, 0x42, /// 0x2c20 + 0x14, 0x28, 0x2d, 0xdb, 0x32, 0x20, 0x68, 0x66, 0x09, 0x98, 0x02, 0x88, 0x74, 0x48, 0xc0, 0x30, /// 0x2c30 + 0xc2, 0x80, 0x02, 0x22, 0x11, 0x43, 0xa1, 0x70, 0x00, 0x99, 0x81, 0x60, 0xff, 0xf7, 0x2e, 0xfd, /// 0x2c40 + 0x38, 0xe7, 0x73, 0x48, 0x40, 0x38, 0x00, 0x8d, 0x00, 0x28, 0x0a, 0xd0, 0x70, 0x7f, 0x07, 0x28, /// 0x2c50 + 0x07, 0xd2, 0xb0, 0x7f, 0x0a, 0x28, 0x18, 0xd2, 0xff, 0xf7, 0x81, 0xfb, 0xc0, 0x1d, 0x0e, 0x28, /// 0x2c60 + 0x0e, 0xd9, 0x00, 0x20, 0x70, 0x77, 0x30, 0x7f, 0x00, 0x28, 0x01, 0xd0, 0x40, 0x1e, 0x30, 0x77, /// 0x2c70 + 0x06, 0x98, 0x00, 0xe0, 0x04, 0xe0, 0x00, 0x68, 0xa8, 0x65, 0xff, 0xf7, 0xe5, 0xfb, 0x19, 0xe7, /// 0x2c80 + 0x63, 0x48, 0x40, 0x38, 0x00, 0x8d, 0x00, 0x28, 0x86, 0xd0, 0x70, 0x7f, 0x60, 0x49, 0x40, 0x1c, /// 0x2c90 + 0x70, 0x77, 0x40, 0x39, 0x08, 0x8d, 0x80, 0x22, 0x83, 0x06, 0x00, 0x2b, 0x39, 0xd0, 0xc3, 0x07, /// 0x2ca0 + 0x04, 0xd0, 0x01, 0x20, 0x48, 0x85, 0xf0, 0x77, 0x03, 0x20, 0x4a, 0xe0, 0x83, 0x07, 0x05, 0xd5, /// 0x2cb0 + 0x02, 0x20, 0x48, 0x85, 0x01, 0x20, 0xf0, 0x77, 0x05, 0x20, 0x42, 0xe0, 0x43, 0x07, 0x04, 0xd5, /// 0x2cc0 + 0x04, 0x22, 0x90, 0x43, 0x08, 0x85, 0x02, 0x20, 0x3b, 0xe0, 0x03, 0x07, 0x04, 0xd5, 0x08, 0x22, /// 0x2cd0 + 0x90, 0x43, 0x08, 0x85, 0x01, 0x20, 0x34, 0xe0, 0xc3, 0x06, 0x0e, 0xd5, 0x05, 0x9a, 0x92, 0x79, /// 0x2ce0 + 0x52, 0x06, 0x04, 0xd5, 0x10, 0x20, 0x48, 0x85, 0x01, 0x20, 0xf0, 0x77, 0x02, 0xe0, 0x10, 0x22, /// 0x2cf0 + 0x90, 0x43, 0x08, 0x85, 0xff, 0xf7, 0x1f, 0xfb, 0xdc, 0xe6, 0x83, 0x06, 0x09, 0xd5, 0x20, 0x20, /// 0x2d00 + 0x48, 0x85, 0x01, 0x20, 0xf0, 0x77, 0x05, 0x98, 0x00, 0x7b, 0x40, 0x1e, 0x10, 0x43, 0xc0, 0xb2, /// 0x2d10 + 0x17, 0xe0, 0xc0, 0x22, 0x02, 0x40, 0x20, 0xd0, 0xc0, 0x2a, 0x06, 0xd1, 0x05, 0x9a, 0xd2, 0x79, /// 0x2d20 + 0x52, 0x07, 0x01, 0xd5, 0x40, 0x22, 0x00, 0xe0, 0x80, 0x22, 0x52, 0x06, 0x0c, 0xd5, 0x40, 0x22, /// 0x2d30 + 0x90, 0x43, 0x08, 0x85, 0x05, 0x98, 0xfb, 0x21, 0xc0, 0x79, 0x08, 0x40, 0x05, 0x99, 0xc8, 0x71, /// 0x2d40 + 0x06, 0x20, 0xff, 0xf7, 0xec, 0xfa, 0xb5, 0xe6, 0x05, 0x98, 0x04, 0x21, 0xc0, 0x79, 0x08, 0x43, /// 0x2d50 + 0x05, 0x99, 0xc8, 0x71, 0xff, 0xf7, 0xcd, 0xfa, 0xac, 0xe6, 0xc2, 0x05, 0x06, 0xd5, 0xff, 0x22, /// 0x2d60 + 0x01, 0x32, 0x90, 0x43, 0x08, 0x85, 0xff, 0xf7, 0xb5, 0xfa, 0xa3, 0xe6, 0x82, 0x05, 0x86, 0xd5, /// 0x2d70 + 0x06, 0x9a, 0xab, 0x6d, 0x12, 0x68, 0xd3, 0x1a, 0x64, 0x2b, 0xbd, 0xda, 0xeb, 0x6d, 0x0a, 0x24, /// 0x2d80 + 0x9b, 0x1a, 0x08, 0x9a, 0x14, 0x5f, 0x1a, 0x19, 0xff, 0x23, 0x5f, 0x33, 0x9a, 0x42, 0xb3, 0xdd, /// 0x2d90 + 0x01, 0x22, 0x52, 0x02, 0x90, 0x43, 0x08, 0x85, 0x19, 0x48, 0x06, 0x21, 0x80, 0x38, 0x81, 0x72, /// 0x2da0 + 0xff, 0xf7, 0x8c, 0xfa, 0x86, 0xe6, 0x16, 0x48, 0x10, 0xb5, 0x1b, 0x49, 0x40, 0x38, 0xc1, 0x62, /// 0x2db0 + 0x17, 0x48, 0x40, 0x30, 0xc1, 0x78, 0x12, 0x48, 0x20, 0x38, 0xc1, 0x73, 0x10, 0x49, 0x01, 0x20, /// 0x2dc0 + 0x60, 0x31, 0xc8, 0x77, 0x02, 0x20, 0xfd, 0xf7, 0x8c, 0xfe, 0x10, 0xbd, 0xf0, 0xb5, 0x09, 0x22, /// 0x2dd0 + 0x12, 0x49, 0x92, 0x01, 0x0a, 0x4c, 0x8a, 0x18, 0x15, 0x78, 0x20, 0x7c, 0x13, 0x22, 0x52, 0x01, /// 0x2de0 + 0xc3, 0x07, 0x8a, 0x18, 0x87, 0xb0, 0x00, 0x2b, 0x25, 0xd0, 0x00, 0x2d, 0x23, 0xd1, 0xd1, 0x79, /// 0x2df0 + 0x49, 0x1c, 0x15, 0xe0, 0xd0, 0x4d, 0x00, 0x00, 0x88, 0x13, 0x00, 0x00, 0x0c, 0x04, 0x00, 0x00, /// 0x2e00 + 0xf0, 0x02, 0x00, 0x20, 0x00, 0x30, 0x00, 0x40, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x34, 0x00, 0x40, /// 0x2e10 + 0x40, 0x00, 0x00, 0x20, 0x40, 0x6c, 0x00, 0x40, 0x0c, 0x22, 0x00, 0x00, 0x30, 0x01, 0x00, 0x20, /// 0x2e20 + 0xc9, 0xb2, 0xd1, 0x71, 0x03, 0x29, 0x02, 0xd2, 0x40, 0x1e, 0x20, 0x74, 0x03, 0xe0, 0x40, 0x1c, /// 0x2e30 + 0x20, 0x74, 0x00, 0x20, 0xd0, 0x71, 0xbd, 0x4e, 0x27, 0x7c, 0x30, 0x7d, 0x05, 0x90, 0x70, 0x7d, /// 0x2e40 + 0x01, 0x90, 0xbb, 0x48, 0x01, 0x46, 0xa0, 0x31, 0x80, 0x30, 0x00, 0x91, 0xc1, 0x7b, 0x04, 0x91, /// 0x2e50 + 0x81, 0x7b, 0x02, 0x91, 0x41, 0x7b, 0x03, 0x7b, 0x03, 0x93, 0x3b, 0x00, 0xfe, 0xf7, 0x30, 0xfb, /// 0x2e60 + 0x11, 0x0a, 0x0d, 0x13, 0x15, 0x1f, 0x23, 0x36, 0x3e, 0x43, 0x4a, 0x60, 0x68, 0x6d, 0x78, 0x7d, /// 0x2e70 + 0x82, 0xa8, 0xaa, 0x00, 0xff, 0xf7, 0x97, 0xff, 0x9c, 0xe0, 0xa8, 0x07, 0x00, 0xd5, 0xfe, 0xe7, /// 0x2e80 + 0x00, 0x20, 0xd0, 0x71, 0x96, 0xe0, 0x30, 0x20, 0x0a, 0xe0, 0x05, 0x99, 0x30, 0x29, 0xf7, 0xd1, /// 0x2e90 + 0x00, 0x99, 0x01, 0x9b, 0x4b, 0x72, 0xf1, 0x8a, 0x49, 0xba, 0x41, 0x84, 0xf0, 0xe7, 0x31, 0x20, /// 0x2ea0 + 0xff, 0xf7, 0x93, 0xf9, 0x86, 0xe0, 0x05, 0x9b, 0x31, 0x2b, 0xe9, 0xd1, 0x01, 0x9d, 0x05, 0x71, /// 0x2eb0 + 0xb3, 0x7d, 0x43, 0x71, 0xf6, 0x7d, 0x86, 0x71, 0x8d, 0x42, 0x02, 0xd2, 0x8b, 0x42, 0x00, 0xd2, /// 0x2ec0 + 0x43, 0x73, 0x02, 0x99, 0x8b, 0x42, 0xdb, 0xd2, 0x83, 0x73, 0xd9, 0xe7, 0x00, 0x7a, 0x03, 0x99, /// 0x2ed0 + 0x81, 0x42, 0x01, 0xd0, 0x02, 0x20, 0x35, 0xe0, 0x07, 0x20, 0x37, 0xe0, 0xe8, 0x07, 0xcf, 0xd0, /// 0x2ee0 + 0x03, 0x98, 0x10, 0x72, 0xcc, 0xe7, 0x40, 0x7a, 0x81, 0x42, 0x01, 0xd0, 0x01, 0x20, 0x29, 0xe0, /// 0x2ef0 + 0x09, 0x20, 0x2b, 0xe0, 0x8e, 0x4b, 0xed, 0x07, 0x80, 0x33, 0x1b, 0x79, 0x00, 0x2d, 0x09, 0xd0, /// 0x2f00 + 0x51, 0x72, 0x8b, 0x42, 0x00, 0xd2, 0x01, 0x71, 0x41, 0x8c, 0x27, 0x29, 0xb8, 0xd1, 0x04, 0x21, /// 0x2f10 + 0x01, 0x71, 0xb5, 0xe7, 0x99, 0x42, 0xb3, 0xd9, 0x43, 0x73, 0x08, 0x20, 0x20, 0x74, 0xaf, 0xe7, /// 0x2f20 + 0x80, 0x7a, 0x02, 0x99, 0x81, 0x42, 0x01, 0xd0, 0x04, 0x20, 0x0b, 0xe0, 0x0b, 0x20, 0x0d, 0xe0, /// 0x2f30 + 0xe8, 0x07, 0xa5, 0xd0, 0x02, 0x98, 0x90, 0x72, 0xa2, 0xe7, 0xc0, 0x7a, 0x04, 0x99, 0x81, 0x42, /// 0x2f40 + 0x03, 0xd0, 0x03, 0x20, 0xff, 0xf7, 0x31, 0xf9, 0x34, 0xe0, 0x0d, 0x20, 0x20, 0x74, 0x31, 0xe0, /// 0x2f50 + 0xe8, 0x07, 0x95, 0xd0, 0x04, 0x98, 0xd0, 0x72, 0x92, 0xe7, 0xff, 0xf7, 0x14, 0xf9, 0xc1, 0xb2, /// 0x2f60 + 0x00, 0x20, 0xef, 0xe7, 0xe9, 0x07, 0x1c, 0xd0, 0xb1, 0x6e, 0x81, 0x60, 0xff, 0x20, 0xc3, 0x30, /// 0x2f70 + 0x70, 0x81, 0x70, 0x48, 0x01, 0x68, 0x80, 0x30, 0x0a, 0x46, 0xff, 0x3a, 0x2d, 0x3a, 0xc2, 0x65, /// 0x2f80 + 0x01, 0x66, 0x42, 0x6e, 0x89, 0x1a, 0x81, 0x65, 0x6a, 0x49, 0x05, 0x20, 0x40, 0x31, 0x88, 0x72, /// 0x2f90 + 0x00, 0x98, 0x0f, 0x21, 0x01, 0x72, 0x68, 0x48, 0x01, 0x78, 0x04, 0x22, 0x11, 0x43, 0x01, 0x70, /// 0x2fa0 + 0x08, 0xe0, 0xa9, 0x07, 0x06, 0xd5, 0x80, 0x68, 0xb0, 0x66, 0x00, 0x20, 0xd0, 0x71, 0x03, 0xe0, /// 0x2fb0 + 0xfe, 0xf7, 0x6b, 0xfb, 0x20, 0x7c, 0x40, 0x1c, 0x20, 0x74, 0x07, 0xb0, 0xf0, 0xbd, 0xf8, 0xb5, /// 0x2fc0 + 0x5b, 0x4c, 0x80, 0x34, 0xa2, 0x78, 0x90, 0x07, 0x60, 0xd5, 0x58, 0x4d, 0x20, 0x46, 0x40, 0x38, /// 0x2fd0 + 0x80, 0x35, 0x01, 0x88, 0xeb, 0x88, 0xc9, 0x1a, 0x00, 0x29, 0x00, 0xdc, 0x49, 0x42, 0x54, 0x4b, /// 0x2fe0 + 0x53, 0x4f, 0xc0, 0x33, 0xdb, 0x89, 0xbe, 0x8e, 0x99, 0x42, 0x31, 0xdd, 0xfd, 0x21, 0x0a, 0x40, /// 0x2ff0 + 0xa2, 0x70, 0x00, 0x8f, 0x64, 0x21, 0x00, 0x90, 0xfe, 0xf7, 0x38, 0xfa, 0x3d, 0x46, 0x20, 0x35, /// 0x3000 + 0x23, 0x38, 0x29, 0x46, 0xa8, 0x77, 0x40, 0x31, 0xc8, 0x70, 0xaf, 0x21, 0x00, 0x98, 0x09, 0x01, /// 0x3010 + 0x40, 0x1a, 0x19, 0x21, 0xfe, 0xf7, 0x40, 0xfa, 0x48, 0x49, 0x08, 0x82, 0x08, 0x20, 0x06, 0x43, /// 0x3020 + 0xbe, 0x86, 0xfd, 0xf7, 0xb6, 0xfb, 0xa9, 0x7f, 0x28, 0x46, 0x80, 0x30, 0x73, 0x29, 0x02, 0xd1, /// 0x3030 + 0xe9, 0x7f, 0x00, 0x29, 0x06, 0xd0, 0x21, 0x78, 0x49, 0x06, 0x49, 0x0e, 0x21, 0x70, 0x48, 0x21, /// 0x3040 + 0xc1, 0x72, 0x23, 0xe0, 0x21, 0x78, 0x80, 0x22, 0x11, 0x43, 0x21, 0x70, 0x30, 0x21, 0xf7, 0xe7, /// 0x3050 + 0x38, 0x49, 0x3b, 0x4b, 0x0a, 0x68, 0xa9, 0x68, 0x51, 0x1a, 0x99, 0x42, 0x16, 0xdd, 0x69, 0x79, /// 0x3060 + 0x02, 0x29, 0x07, 0xd2, 0xc3, 0x89, 0x80, 0x24, 0x23, 0x43, 0xc3, 0x81, 0x49, 0x1c, 0xaa, 0x60, /// 0x3070 + 0x69, 0x71, 0x0b, 0xe0, 0x01, 0x20, 0xc0, 0x03, 0x06, 0x43, 0xbe, 0x86, 0xfd, 0xf7, 0x89, 0xfb, /// 0x3080 + 0xa0, 0x78, 0xfd, 0x21, 0x08, 0x40, 0xa0, 0x70, 0x00, 0x20, 0x68, 0x71, 0x00, 0x20, 0xf8, 0xbd, /// 0x3090 + 0x70, 0xb5, 0x27, 0x49, 0x9f, 0x23, 0x80, 0x31, 0x4a, 0x78, 0x1a, 0x40, 0x4a, 0x70, 0x0b, 0x46, /// 0x30a0 + 0x20, 0x33, 0x1c, 0x7c, 0x05, 0x78, 0x80, 0x39, 0x8a, 0x8e, 0xac, 0x42, 0x0e, 0xd1, 0x5c, 0x7c, /// 0x30b0 + 0x45, 0x78, 0xac, 0x42, 0x0a, 0xd1, 0x9c, 0x7c, 0x85, 0x78, 0xac, 0x42, 0x06, 0xd1, 0xdb, 0x7c, /// 0x30c0 + 0xc0, 0x78, 0x83, 0x42, 0x02, 0xd1, 0x01, 0x20, 0x40, 0x02, 0x01, 0xe0, 0xff, 0x20, 0x01, 0x30, /// 0x30d0 + 0x02, 0x43, 0x8a, 0x86, 0xfd, 0xf7, 0x5d, 0xfb, 0x70, 0xbd, 0x70, 0xb5, 0x14, 0x48, 0xdf, 0x22, /// 0x30e0 + 0x80, 0x30, 0x41, 0x78, 0x04, 0x46, 0x11, 0x40, 0x41, 0x70, 0x40, 0x3c, 0x60, 0x7e, 0x3b, 0x28, /// 0x30f0 + 0x15, 0xd1, 0x23, 0x46, 0x80, 0x33, 0x18, 0x79, 0xfd, 0x21, 0x08, 0x40, 0x01, 0x21, 0x08, 0x43, /// 0x3100 + 0x18, 0x71, 0x0b, 0x49, 0xa5, 0x7e, 0x8a, 0x8e, 0x02, 0x2d, 0x02, 0xd1, 0xe4, 0x7e, 0xbb, 0x2c, /// 0x3110 + 0x06, 0xd0, 0x01, 0x20, 0x00, 0x03, 0x02, 0x43, 0x8a, 0x86, 0xfd, 0xf7, 0x3a, 0xfb, 0x70, 0xbd, /// 0x3120 + 0x08, 0x24, 0x20, 0x43, 0x18, 0x71, 0xa0, 0x02, 0xf5, 0xe7, 0x00, 0x00, 0x30, 0x03, 0x00, 0x20, /// 0x3130 + 0x00, 0x00, 0x00, 0x20, 0x30, 0x02, 0x00, 0x20, 0x40, 0x6c, 0x00, 0x40, 0x00, 0x30, 0x00, 0x40, /// 0x3140 + 0xdc, 0x05, 0x00, 0x00, 0x94, 0x28, 0x94, 0x1f, 0x94, 0x15, 0x94, 0x15, 0x94, 0x0f, 0x94, 0x14, /// 0x3150 + 0x14, 0x14, 0x00, 0x00, 0x93, 0x3e, 0x85, 0x4d, 0x89, 0x43, 0x86, 0x55, 0x86, 0x5c, 0x86, 0x60, /// 0x3160 + 0x14, 0x00, 0x01, 0x50, 0x01, 0x00, 0x01, 0x00, 0x4e, 0x6f, 0x76, 0x20, 0x32, 0x31, 0x20, 0x32, /// 0x3170 + 0x30, 0x31, 0x38, 0x00, 0x31, 0x34, 0x3a, 0x33, 0x39, 0x3a, 0x35, 0x36, +}; + +unsigned char idt_firmware_sram[] = { + 0x0c, 0x0b, 0x00, 0x20, 0x30, 0x11, 0x00, 0x20, 0x08, 0x1d, 0x00, 0x00, 0xac, 0x29, 0x00, 0x00, /// 0x4010 + 0x09, 0x09, 0x00, 0x20, 0x97, 0x1a, 0x00, 0x00, 0xa3, 0x1e, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, /// 0x4020 + 0x20, 0x92, 0x02, 0x00, 0x87, 0x01, 0x01, 0x02, 0xf3, 0x04, 0x1e, 0x12, 0xd4, 0x01, 0x09, 0x00, /// 0x4030 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x13, 0x78, 0x05, 0x02, 0x80, 0x00, 0x00, 0x00, 0x00, /// 0x4040 + 0x23, 0x01, 0x9a, 0x02, 0x90, 0x01, 0xe8, 0x03, 0x4c, 0x1d, 0x08, 0xff, 0x00, 0x00, 0x00, 0x00, /// 0x4050 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /// 0x4060 + 0x70, 0xb5, 0x72, 0xb6, 0x23, 0x48, 0x22, 0x49, 0x41, 0x60, 0x23, 0x49, 0x23, 0x4c, 0x81, 0x67, /// 0x4070 + 0x23, 0x4d, 0x62, 0xb6, 0x60, 0x68, 0x21, 0x46, 0xe8, 0x61, 0x10, 0x22, 0x08, 0x31, 0x21, 0x48, /// 0x4080 + 0x00, 0xf0, 0xea, 0xf9, 0x20, 0x49, 0x08, 0x78, 0x00, 0x28, 0x00, 0xd1, 0x1f, 0x49, 0x1c, 0x48, /// 0x4090 + 0x10, 0x22, 0x68, 0x30, 0x00, 0xf0, 0xe6, 0xf9, 0xa0, 0x78, 0xe8, 0x70, 0x58, 0x20, 0x28, 0x87, /// 0x40a0 + 0x20, 0x8b, 0x64, 0x21, 0x00, 0xf0, 0xe4, 0xf9, 0x15, 0x49, 0x40, 0x1e, 0x40, 0x31, 0x88, 0x72, /// 0x40b0 + 0x13, 0x48, 0xa1, 0x7e, 0x60, 0x30, 0x81, 0x70, 0x15, 0x49, 0x20, 0x20, 0x08, 0x81, 0x60, 0x22, /// 0x40c0 + 0x8a, 0x83, 0x13, 0x49, 0x20, 0x31, 0x8a, 0x89, 0x02, 0x43, 0x8a, 0x81, 0xe0, 0x8a, 0x64, 0x21, /// 0x40d0 + 0x00, 0xf0, 0xce, 0xf9, 0x0a, 0x49, 0x21, 0x38, 0x20, 0x31, 0x88, 0x77, 0xe0, 0x8a, 0xaf, 0x21, /// 0x40e0 + 0x09, 0x01, 0x40, 0x1a, 0x19, 0x21, 0x00, 0xf0, 0xc9, 0xf9, 0x0a, 0x49, 0x08, 0x82, 0x70, 0xbd, /// 0x40f0 + 0x09, 0x09, 0x00, 0x20, 0x00, 0x01, 0x00, 0x20, 0x0d, 0x0b, 0x00, 0x20, 0x20, 0x06, 0x00, 0x20, /// 0x4100 + 0x00, 0x00, 0x00, 0x20, 0xb0, 0x00, 0x00, 0x20, 0x60, 0x3e, 0x00, 0x00, 0x20, 0x11, 0x00, 0x20, /// 0x4110 + 0x00, 0x6c, 0x00, 0x40, 0x00, 0x30, 0x00, 0x40, 0x10, 0xb5, 0xc1, 0x48, 0x0c, 0x21, 0x01, 0x73, /// 0x4120 + 0x38, 0x21, 0x41, 0x73, 0x0a, 0x21, 0x81, 0x73, 0xbe, 0x49, 0x09, 0x8f, 0xc1, 0x73, 0x09, 0x0a, /// 0x4130 + 0x01, 0x74, 0x03, 0x20, 0x00, 0xf0, 0xa8, 0xf9, 0x10, 0xbd, 0xf8, 0xb5, 0xb8, 0x49, 0x20, 0x31, /// 0x4140 + 0x48, 0x7b, 0xc2, 0x06, 0x0e, 0xd5, 0xef, 0x22, 0x10, 0x40, 0x48, 0x73, 0xb5, 0x48, 0x80, 0x22, /// 0x4150 + 0x40, 0x38, 0x81, 0x8e, 0x11, 0x43, 0x81, 0x86, 0xb3, 0x48, 0x81, 0x89, 0x04, 0x22, 0x11, 0x43, /// 0x4160 + 0x81, 0x81, 0xf8, 0xbd, 0x43, 0x07, 0xfb, 0x22, 0x00, 0x2b, 0x0b, 0xda, 0x10, 0x40, 0x10, 0x22, /// 0x4170 + 0x10, 0x43, 0x48, 0x73, 0xad, 0x48, 0x01, 0x21, 0x81, 0x80, 0x01, 0x88, 0x30, 0x22, 0x91, 0x43, /// 0x4180 + 0x01, 0x80, 0xf8, 0xbd, 0xaa, 0x48, 0x01, 0x7e, 0x00, 0x29, 0x02, 0xd0, 0x00, 0xf0, 0x52, 0xfb, /// 0x4190 + 0xf8, 0xbd, 0xa3, 0x49, 0xa2, 0x4c, 0x80, 0x39, 0x0d, 0x68, 0xe1, 0x6b, 0xa5, 0x4b, 0x69, 0x1a, /// 0x41a0 + 0x99, 0x42, 0x03, 0xdb, 0x48, 0x21, 0xe5, 0x63, 0x01, 0x76, 0xef, 0xe7, 0xa0, 0x6b, 0x21, 0x6c, /// 0x41b0 + 0x28, 0x1a, 0x88, 0x42, 0xec, 0xdb, 0x9e, 0x48, 0xa5, 0x63, 0x20, 0x38, 0x41, 0x78, 0x4b, 0x07, /// 0x41c0 + 0x06, 0xd5, 0x11, 0x40, 0x41, 0x70, 0x32, 0x20, 0x20, 0x64, 0x00, 0xf0, 0x63, 0xf9, 0xf8, 0xbd, /// 0x41d0 + 0x0a, 0x07, 0x07, 0xd5, 0xf7, 0x22, 0x11, 0x40, 0x41, 0x70, 0x32, 0x20, 0x20, 0x64, 0x00, 0xf0, /// 0x41e0 + 0x5f, 0xf9, 0xf8, 0xbd, 0x86, 0x78, 0x31, 0x07, 0x89, 0x0f, 0x02, 0xd0, 0x00, 0xf0, 0xcf, 0xfa, /// 0x41f0 + 0xf8, 0xbd, 0x8b, 0x4f, 0xf0, 0x07, 0x80, 0x37, 0x00, 0x28, 0x22, 0xd0, 0x89, 0x48, 0x00, 0x90, /// 0x4200 + 0x00, 0x8f, 0x64, 0x21, 0x00, 0xf0, 0x34, 0xf9, 0x86, 0x49, 0x72, 0x08, 0x20, 0x39, 0x89, 0x7f, /// 0x4210 + 0x52, 0x00, 0x08, 0x1a, 0x86, 0x49, 0x23, 0x30, 0x20, 0x39, 0x8a, 0x70, 0x00, 0x28, 0x00, 0xdc, /// 0x4220 + 0x40, 0x42, 0x14, 0x28, 0xe4, 0xdb, 0x32, 0x20, 0x20, 0x64, 0x00, 0x98, 0x03, 0x88, 0x7c, 0x48, /// 0x4230 + 0xc0, 0x30, 0x83, 0x80, 0x02, 0x20, 0x02, 0x43, 0x8a, 0x70, 0x3d, 0x64, 0xff, 0xf7, 0x6c, 0xff, /// 0x4240 + 0xf8, 0xbd, 0x00, 0xf0, 0xba, 0xf9, 0x78, 0x7f, 0x00, 0x28, 0xf9, 0xd0, 0x40, 0x1e, 0x78, 0x77, /// 0x4250 + 0xf8, 0xbd, 0x10, 0xb5, 0x75, 0x48, 0x00, 0x21, 0x80, 0x88, 0x00, 0x07, 0x00, 0xd5, 0x02, 0x21, /// 0x4260 + 0x75, 0x48, 0x80, 0x8b, 0xc0, 0x04, 0x01, 0xd5, 0x01, 0x20, 0x01, 0x43, 0x73, 0x48, 0x80, 0x89, /// 0x4270 + 0xc0, 0x06, 0x01, 0xd5, 0x04, 0x20, 0x01, 0x43, 0x6a, 0x4b, 0x40, 0x3b, 0x9a, 0x8e, 0x50, 0x07, /// 0x4280 + 0x40, 0x0f, 0x48, 0x40, 0x05, 0xd0, 0xd2, 0x08, 0xd2, 0x00, 0x0a, 0x43, 0x9a, 0x86, 0x00, 0xf0, /// 0x4290 + 0x0d, 0xf9, 0x10, 0xbd, 0x70, 0xb5, 0x66, 0x4a, 0x20, 0x3a, 0x93, 0x78, 0x98, 0x07, 0x31, 0xd5, /// 0x42a0 + 0x5f, 0x4c, 0x60, 0x49, 0xc0, 0x34, 0x08, 0x88, 0xa5, 0x88, 0x40, 0x1b, 0x00, 0x28, 0x00, 0xdc, /// 0x42b0 + 0x40, 0x42, 0x63, 0x4d, 0xa8, 0x42, 0x16, 0xdd, 0xfd, 0x20, 0x03, 0x40, 0x93, 0x70, 0x0d, 0x8f, /// 0x42c0 + 0x64, 0x21, 0x28, 0x46, 0x00, 0xf0, 0xd4, 0xf8, 0x56, 0x49, 0x23, 0x38, 0x20, 0x39, 0x88, 0x77, /// 0x42d0 + 0xaf, 0x20, 0x00, 0x01, 0x28, 0x1a, 0x19, 0x21, 0x00, 0xf0, 0xd0, 0xf8, 0x56, 0x49, 0x08, 0x82, /// 0x42e0 + 0x00, 0x20, 0xa0, 0x71, 0x0e, 0xe0, 0x4e, 0x48, 0x52, 0x4c, 0x80, 0x38, 0x02, 0x68, 0xff, 0x30, /// 0x42f0 + 0x01, 0x30, 0x03, 0x6c, 0xd3, 0x1a, 0xa3, 0x42, 0x04, 0xdd, 0x8b, 0x7b, 0x80, 0x24, 0x23, 0x43, /// 0x4300 + 0x8b, 0x73, 0x02, 0x64, 0x00, 0x20, 0x70, 0xbd, 0xf8, 0xb5, 0x45, 0x48, 0x4d, 0x49, 0x80, 0x38, /// 0x4310 + 0x80, 0x6f, 0x00, 0x26, 0x88, 0x42, 0x14, 0xd1, 0x41, 0x4a, 0xa0, 0x32, 0x50, 0x79, 0x00, 0x28, /// 0x4320 + 0x14, 0xd0, 0x40, 0x49, 0x20, 0x39, 0x88, 0x79, 0x43, 0x1c, 0x8b, 0x71, 0x0a, 0x28, 0x08, 0xd9, /// 0x4330 + 0x45, 0x48, 0x01, 0x23, 0x03, 0x80, 0x03, 0x23, 0x03, 0x80, 0x41, 0x23, 0x03, 0x83, 0x56, 0x71, /// 0x4340 + 0x8e, 0x71, 0x38, 0x4f, 0xb8, 0x7b, 0x00, 0x28, 0x07, 0xd1, 0x03, 0xe0, 0x3f, 0x48, 0x00, 0xf0, /// 0x4350 + 0xb3, 0xf8, 0xf6, 0xe7, 0x38, 0x7b, 0x00, 0x28, 0x02, 0xd0, 0x3d, 0x48, 0x00, 0xf0, 0xb2, 0xf8, /// 0x4360 + 0x30, 0x48, 0x40, 0x38, 0x80, 0x8e, 0x00, 0x06, 0x01, 0xd5, 0xff, 0xf7, 0x93, 0xff, 0xff, 0xf7, /// 0x4370 + 0x70, 0xff, 0x2b, 0x4d, 0x20, 0x35, 0x28, 0x7a, 0x00, 0x28, 0x4e, 0xd1, 0xe8, 0x7a, 0x2c, 0x46, /// 0x4380 + 0x60, 0x3c, 0x00, 0x28, 0x11, 0xd0, 0x40, 0x1e, 0xe8, 0x72, 0xa0, 0x7a, 0x05, 0x28, 0x44, 0xd1, /// 0x4390 + 0x30, 0x48, 0x00, 0xf0, 0x97, 0xf8, 0x28, 0x7b, 0xe9, 0x7a, 0x40, 0x1e, 0x81, 0x42, 0x3c, 0xd1, /// 0x43a0 + 0x20, 0x46, 0x80, 0x30, 0x06, 0x86, 0x86, 0x85, 0x37, 0xe0, 0xa0, 0x7a, 0x1c, 0x4e, 0x03, 0x00, /// 0x43b0 + 0x00, 0xf0, 0x8e, 0xf8, 0x07, 0x05, 0x16, 0x1a, 0x1e, 0x22, 0x2e, 0x31, 0x33, 0x00, 0x18, 0x48, /// 0x43c0 + 0x79, 0x7b, 0x80, 0x38, 0x00, 0x68, 0xc9, 0x06, 0x01, 0xd5, 0x1d, 0x21, 0x00, 0xe0, 0x03, 0x21, /// 0x43d0 + 0x88, 0x42, 0x22, 0xd9, 0x08, 0x20, 0x28, 0x73, 0x32, 0x20, 0x30, 0x64, 0x01, 0x20, 0x15, 0xe0, /// 0x43e0 + 0x00, 0xf0, 0x7c, 0xf8, 0x02, 0x20, 0x11, 0xe0, 0x00, 0xf0, 0x7e, 0xf8, 0x03, 0x20, 0x0d, 0xe0, /// 0x43f0 + 0x00, 0xf0, 0x80, 0xf8, 0x04, 0x20, 0x09, 0xe0, 0xff, 0xf7, 0x32, 0xfe, 0x08, 0x48, 0x80, 0x38, /// 0x4400 + 0x00, 0x68, 0xf0, 0x63, 0xb0, 0x63, 0x1e, 0x20, 0x28, 0x73, 0x05, 0x20, 0xa0, 0x72, 0x04, 0xe0, /// 0x4410 + 0xff, 0xf7, 0x93, 0xfe, 0x01, 0xe0, 0x00, 0xf0, 0x73, 0xf8, 0x00, 0x20, 0xf8, 0xbd, 0x00, 0x00, /// 0x4420 + 0x80, 0x01, 0x00, 0x20, 0x40, 0x00, 0x00, 0x20, 0x20, 0x6c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x40, /// 0x4430 + 0xa0, 0x00, 0x00, 0x20, 0xdc, 0x05, 0x00, 0x00, 0x00, 0x30, 0x00, 0x40, 0x20, 0x48, 0x00, 0x40, /// 0x4440 + 0x49, 0x02, 0x00, 0x00, 0x0d, 0x0b, 0x00, 0x20, 0x00, 0x44, 0x00, 0x40, 0x18, 0x11, 0x00, 0x20, /// 0x4450 + 0x7d, 0x0f, 0x00, 0x20, 0x27, 0x0f, 0x00, 0x00, 0x03, 0xb4, 0x01, 0x48, 0x01, 0x90, 0x01, 0xbd, /// 0x4460 + 0x7f, 0x05, 0x00, 0x00, 0x03, 0xb4, 0x01, 0x48, 0x01, 0x90, 0x01, 0xbd, 0x71, 0x05, 0x00, 0x00, /// 0x4470 + 0x03, 0xb4, 0x01, 0x48, 0x01, 0x90, 0x01, 0xbd, 0xe9, 0x15, 0x00, 0x00, 0x03, 0xb4, 0x01, 0x48, /// 0x4480 + 0x01, 0x90, 0x01, 0xbd, 0x15, 0x16, 0x00, 0x00, 0x03, 0xb4, 0x01, 0x48, 0x01, 0x90, 0x01, 0xbd, /// 0x4490 + 0x2f, 0x07, 0x00, 0x00, 0x03, 0xb4, 0x01, 0x48, 0x01, 0x90, 0x01, 0xbd, 0x2b, 0x12, 0x00, 0x00, /// 0x44a0 + 0x03, 0xb4, 0x01, 0x48, 0x01, 0x90, 0x01, 0xbd, 0xfd, 0x11, 0x00, 0x00, 0x03, 0xb4, 0x01, 0x48, /// 0x44b0 + 0x01, 0x90, 0x01, 0xbd, 0xd5, 0x05, 0x00, 0x00, 0x03, 0xb4, 0x01, 0x48, 0x01, 0x90, 0x01, 0xbd, /// 0x44c0 + 0xf5, 0x03, 0x00, 0x00, 0x03, 0xb4, 0x01, 0x48, 0x01, 0x90, 0x01, 0xbd, 0xb1, 0x00, 0x00, 0x00, /// 0x44d0 + 0x03, 0xb4, 0x01, 0x48, 0x01, 0x90, 0x01, 0xbd, 0x3d, 0x16, 0x00, 0x00, 0x03, 0xb4, 0x01, 0x48, /// 0x44e0 + 0x01, 0x90, 0x01, 0xbd, 0x81, 0x12, 0x00, 0x00, 0x03, 0xb4, 0x01, 0x48, 0x01, 0x90, 0x01, 0xbd, /// 0x44f0 + 0x5d, 0x12, 0x00, 0x00, 0x03, 0xb4, 0x01, 0x48, 0x01, 0x90, 0x01, 0xbd, 0x45, 0x12, 0x00, 0x00, /// 0x4500 + 0x03, 0xb4, 0x01, 0x48, 0x01, 0x90, 0x01, 0xbd, 0x6d, 0x11, 0x00, 0x00, 0xf8, 0xb5, 0x02, 0x22, /// 0x4510 + 0x03, 0x21, 0x00, 0x20, 0x00, 0xf0, 0x94, 0xfa, 0x03, 0x21, 0x00, 0xf0, 0x97, 0xfa, 0x05, 0x46, /// 0x4520 + 0xfe, 0x49, 0x00, 0xf0, 0x99, 0xfa, 0xfe, 0x4e, 0xfc, 0x49, 0x30, 0x80, 0x28, 0x39, 0x88, 0x69, /// 0x4530 + 0x0c, 0x46, 0x42, 0x00, 0x48, 0x69, 0x20, 0x34, 0xc3, 0x0f, 0x1a, 0x43, 0x8a, 0x61, 0xea, 0x07, /// 0x4540 + 0x40, 0x00, 0xd2, 0x0f, 0x10, 0x43, 0x48, 0x61, 0xa1, 0x7e, 0xf6, 0x4f, 0x49, 0x00, 0xc9, 0x19, /// 0x4550 + 0xff, 0x31, 0x01, 0x31, 0x0d, 0x85, 0x01, 0x22, 0x03, 0x21, 0x02, 0x20, 0x00, 0xf0, 0x70, 0xfa, /// 0x4560 + 0x45, 0x08, 0x21, 0x46, 0x14, 0x31, 0x28, 0x46, 0x00, 0xf0, 0x76, 0xfa, 0x70, 0x87, 0xa0, 0x7e, /// 0x4570 + 0x03, 0x22, 0x40, 0x00, 0xc0, 0x19, 0xff, 0x30, 0x01, 0x30, 0x85, 0x86, 0x11, 0x46, 0x01, 0x20, /// 0x4580 + 0x00, 0xf0, 0x5e, 0xfa, 0x03, 0x21, 0x00, 0xf0, 0x61, 0xfa, 0x21, 0x46, 0x05, 0x46, 0x0e, 0x31, /// 0x4590 + 0x00, 0xf0, 0x62, 0xfa, 0x31, 0x46, 0x40, 0x39, 0x88, 0x87, 0xa1, 0x7e, 0x49, 0x00, 0xc9, 0x19, /// 0x45a0 + 0xff, 0x31, 0x01, 0x31, 0xcd, 0x85, 0xa0, 0x7e, 0x02, 0x28, 0x01, 0xd3, 0x00, 0x20, 0x00, 0xe0, /// 0x45b0 + 0x40, 0x1c, 0xa0, 0x76, 0x00, 0x20, 0x60, 0x71, 0xf8, 0xbd, 0xf8, 0xb5, 0xd8, 0x4f, 0xda, 0x48, /// 0x45c0 + 0x39, 0x88, 0x81, 0x42, 0x05, 0xd2, 0xd9, 0x48, 0x02, 0x8b, 0x1f, 0x23, 0x1b, 0x02, 0x9a, 0x43, /// 0x45d0 + 0x02, 0x83, 0xd2, 0x4e, 0x88, 0x3e, 0x72, 0x7b, 0x35, 0x46, 0xd0, 0x07, 0x20, 0x3d, 0x00, 0x28, /// 0x45e0 + 0x60, 0xd0, 0x78, 0x8f, 0x00, 0xf0, 0x3e, 0xfa, 0xc0, 0x1c, 0xd1, 0x49, 0xb8, 0x80, 0x09, 0x78, /// 0x45f0 + 0xc9, 0x07, 0x59, 0xd1, 0x00, 0xf0, 0x3c, 0xfa, 0xcd, 0x48, 0xce, 0x4c, 0x40, 0x30, 0x80, 0x8f, /// 0x4600 + 0x6b, 0x38, 0x0b, 0x28, 0xb0, 0x7b, 0x32, 0xd3, 0xcb, 0x49, 0xc9, 0x7e, 0x49, 0x09, 0x88, 0x42, /// 0x4610 + 0x04, 0xd2, 0xa2, 0x88, 0x92, 0x07, 0x01, 0xd5, 0x01, 0x20, 0x05, 0xe0, 0x88, 0x42, 0x05, 0xd3, /// 0x4620 + 0xa1, 0x88, 0x89, 0x07, 0x02, 0xd4, 0x03, 0x20, 0xa0, 0x80, 0x38, 0xe0, 0x00, 0xf0, 0x26, 0xfa, /// 0x4630 + 0xb1, 0x7b, 0x0a, 0x29, 0x01, 0xd2, 0x00, 0x20, 0x08, 0xe0, 0x0f, 0x29, 0x01, 0xd2, 0x01, 0x20, /// 0x4640 + 0x04, 0xe0, 0x14, 0x29, 0x01, 0xd2, 0x02, 0x20, 0x00, 0xe0, 0x03, 0x20, 0x22, 0x88, 0xf0, 0x23, /// 0x4650 + 0x9a, 0x43, 0x80, 0x01, 0x02, 0x43, 0x22, 0x80, 0x02, 0x29, 0x01, 0xd9, 0x10, 0x20, 0x00, 0xe0, /// 0x4660 + 0x00, 0x20, 0xa0, 0x81, 0x38, 0x89, 0x00, 0xf0, 0x0f, 0xfa, 0x20, 0x82, 0x17, 0xe0, 0x09, 0x28, /// 0x4670 + 0x01, 0xd2, 0x01, 0x21, 0x00, 0xe0, 0x03, 0x21, 0xa1, 0x80, 0x21, 0x88, 0xf0, 0x22, 0x91, 0x43, /// 0x4680 + 0x12, 0x28, 0x01, 0xd2, 0x00, 0x22, 0x00, 0xe0, 0xc0, 0x22, 0x11, 0x43, 0x21, 0x80, 0x02, 0x28, /// 0x4690 + 0x01, 0xd9, 0x10, 0x21, 0x00, 0xe0, 0x00, 0x21, 0xa1, 0x81, 0x00, 0xf0, 0xef, 0xf9, 0x00, 0xf0, /// 0x46a0 + 0xf9, 0xf9, 0x01, 0xe0, 0x10, 0x07, 0x05, 0xd5, 0x00, 0xf0, 0xfa, 0xf9, 0x41, 0x1d, 0x0a, 0x29, /// 0x46b0 + 0x23, 0xd8, 0x25, 0xe0, 0x9e, 0x4b, 0x37, 0x20, 0x1c, 0x8e, 0x9a, 0x4b, 0xa1, 0x42, 0x1b, 0x68, /// 0x46c0 + 0x05, 0xd8, 0xe9, 0x6b, 0x7d, 0x24, 0x59, 0x1a, 0xe4, 0x00, 0xa1, 0x42, 0x15, 0xdb, 0x94, 0x48, /// 0x46d0 + 0x9a, 0x49, 0x40, 0x38, 0x80, 0x8f, 0x88, 0x42, 0x04, 0xd9, 0x94, 0x48, 0x81, 0x8a, 0x04, 0x15, /// 0x46e0 + 0x21, 0x43, 0x81, 0x82, 0x91, 0x48, 0x81, 0x8b, 0x01, 0x24, 0x21, 0x43, 0x81, 0x83, 0x05, 0x20, /// 0x46f0 + 0x02, 0x43, 0x72, 0x73, 0x28, 0x3b, 0xab, 0x63, 0xf8, 0xbd, 0x32, 0x21, 0x29, 0x64, 0x0f, 0xe0, /// 0x4700 + 0x71, 0x7b, 0x96, 0x22, 0xcb, 0x07, 0x00, 0x2b, 0x07, 0xd0, 0x84, 0x4b, 0x28, 0x3b, 0x59, 0x7f, /// 0x4710 + 0x00, 0x29, 0x04, 0xd0, 0x49, 0x1e, 0x59, 0x77, 0x02, 0xe0, 0x09, 0x07, 0x00, 0xd5, 0x2a, 0x64, /// 0x4720 + 0x83, 0x49, 0x81, 0x4b, 0x09, 0x8e, 0x3a, 0x3b, 0xcb, 0x18, 0x7c, 0x49, 0x3a, 0x88, 0x08, 0x39, /// 0x4730 + 0x9a, 0x42, 0x19, 0xd9, 0x0a, 0x79, 0x0a, 0x2a, 0x03, 0xd3, 0x7a, 0x48, 0x00, 0x68, 0xe8, 0x63, /// 0x4740 + 0xf8, 0xbd, 0x52, 0x1c, 0x0a, 0x71, 0xc3, 0x21, 0x89, 0x00, 0xa9, 0x81, 0xa8, 0x73, 0x00, 0x28, /// 0x4750 + 0x01, 0xd0, 0x01, 0x20, 0x70, 0x74, 0x01, 0x20, 0x00, 0xf0, 0xa8, 0xf9, 0x78, 0x48, 0x81, 0x89, /// 0x4760 + 0x80, 0x22, 0x51, 0x40, 0x81, 0x81, 0xf8, 0xbd, 0x00, 0x22, 0xeb, 0xe7, 0x6d, 0x4a, 0x10, 0xb5, /// 0x4770 + 0x0c, 0x23, 0x80, 0x32, 0x13, 0x73, 0xa4, 0x23, 0x53, 0x73, 0x90, 0x73, 0x69, 0x48, 0x08, 0x22, /// 0x4780 + 0x8f, 0x30, 0x00, 0xf0, 0x99, 0xf9, 0x0c, 0x20, 0x00, 0xf0, 0x90, 0xf9, 0x10, 0xbd, 0x70, 0xb5, /// 0x4790 + 0x64, 0x48, 0x67, 0x4c, 0x00, 0x68, 0x21, 0x6f, 0x6a, 0x4a, 0x41, 0x1a, 0x8a, 0xb0, 0x91, 0x42, /// 0x47a0 + 0x3e, 0xd9, 0xa1, 0x78, 0x68, 0x4e, 0x09, 0x07, 0x15, 0xd5, 0x25, 0x46, 0x40, 0x35, 0xea, 0x8f, /// 0x47b0 + 0x51, 0x1c, 0xe9, 0x87, 0x03, 0x2a, 0x10, 0xd9, 0x59, 0x48, 0x08, 0x22, 0x40, 0x38, 0x81, 0x8e, /// 0x47c0 + 0x11, 0x43, 0x81, 0x86, 0x10, 0x46, 0x00, 0xf0, 0x7d, 0xf9, 0xa0, 0x78, 0xf7, 0x21, 0x08, 0x40, /// 0x47d0 + 0xa0, 0x70, 0x00, 0x20, 0xe8, 0x87, 0x0a, 0xb0, 0x70, 0xbd, 0x5c, 0xa3, 0x0e, 0xcb, 0x01, 0xad, /// 0x47e0 + 0x0e, 0xc5, 0x4e, 0x49, 0x20, 0x67, 0x14, 0x39, 0x08, 0x22, 0x0c, 0x46, 0x04, 0xa8, 0x00, 0xf0, /// 0x47f0 + 0x63, 0xf9, 0x08, 0x22, 0x01, 0xa9, 0x06, 0xa8, 0x00, 0xf0, 0x5e, 0xf9, 0x30, 0x46, 0x00, 0xf0, /// 0x4800 + 0x67, 0xf9, 0x10, 0x22, 0x04, 0xa9, 0x30, 0x46, 0x00, 0xf0, 0x68, 0xf9, 0x48, 0x49, 0x30, 0x46, /// 0x4810 + 0x0c, 0x31, 0x00, 0xf0, 0x69, 0xf9, 0x21, 0x46, 0x08, 0x20, 0xff, 0xf7, 0xa7, 0xff, 0xda, 0xe7, /// 0x4820 + 0xff, 0xf7, 0xcb, 0xfe, 0x3d, 0x49, 0x28, 0x39, 0x48, 0x7f, 0x00, 0x28, 0xd3, 0xd0, 0x40, 0x1e, /// 0x4830 + 0x48, 0x77, 0xd0, 0xe7, 0xf8, 0xb5, 0x3e, 0x4d, 0x3d, 0x4c, 0x20, 0x35, 0x28, 0x7e, 0x38, 0x4b, /// 0x4840 + 0x40, 0x1e, 0xc2, 0xb2, 0x2a, 0x76, 0xa0, 0x8f, 0x19, 0x88, 0x08, 0x2a, 0x10, 0xd3, 0x95, 0x07, /// 0x4850 + 0x0d, 0xd1, 0x65, 0x8f, 0x69, 0x18, 0x89, 0xb2, 0x61, 0x87, 0x5b, 0x8f, 0xc0, 0x18, 0x80, 0xb2, /// 0x4860 + 0xa0, 0x87, 0x08, 0x2a, 0x03, 0xd1, 0x09, 0x09, 0x61, 0x87, 0x00, 0x09, 0xa0, 0x87, 0xf8, 0xbd, /// 0x4870 + 0x06, 0x2a, 0xfc, 0xd8, 0x00, 0xf0, 0xf6, 0xf8, 0x37, 0x49, 0x09, 0x88, 0x28, 0x4a, 0xc9, 0x05, /// 0x4880 + 0x20, 0x32, 0x00, 0x29, 0x01, 0xda, 0x11, 0x7d, 0x08, 0x18, 0x2a, 0x49, 0x0b, 0x8a, 0x1b, 0x04, /// 0x4890 + 0x02, 0xd5, 0x15, 0x23, 0xd3, 0x56, 0x18, 0x18, 0x62, 0x8f, 0x15, 0x23, 0x43, 0x43, 0x5a, 0x43, /// 0x48a0 + 0x8b, 0x88, 0x12, 0x0b, 0x00, 0x26, 0x01, 0x2b, 0x05, 0xd0, 0x55, 0x23, 0xdb, 0x00, 0x98, 0x42, /// 0x48b0 + 0x03, 0xd9, 0x05, 0x20, 0x05, 0xe0, 0x6e, 0x76, 0x04, 0xe0, 0x08, 0x88, 0x00, 0x06, 0x80, 0x0f, /// 0x48c0 + 0x40, 0x1c, 0x68, 0x76, 0x68, 0x7e, 0x41, 0x00, 0x15, 0x48, 0x40, 0x38, 0x09, 0x18, 0x60, 0x31, /// 0x48d0 + 0x08, 0x7a, 0x50, 0x43, 0xc2, 0x09, 0x09, 0x20, 0x08, 0x56, 0x0a, 0x21, 0x48, 0x43, 0x1f, 0x49, /// 0x48e0 + 0x80, 0x18, 0x01, 0xd5, 0x00, 0x20, 0x06, 0xe0, 0x88, 0x42, 0x04, 0xd9, 0x1b, 0x4a, 0x52, 0x42, /// 0x48f0 + 0x82, 0x18, 0xd2, 0x08, 0x10, 0x18, 0x0e, 0x4a, 0x40, 0x32, 0x92, 0x8f, 0x6b, 0x3a, 0x0a, 0x2a, /// 0x4900 + 0x01, 0xd8, 0x17, 0x4a, 0x80, 0x18, 0x17, 0x4a, 0x90, 0x42, 0x00, 0xd9, 0x10, 0x46, 0x0b, 0x4a, /// 0x4910 + 0x04, 0x4f, 0x3d, 0x32, 0x80, 0x37, 0xba, 0x81, 0xc0, 0x01, 0x25, 0xe0, 0x28, 0x02, 0x00, 0x20, /// 0x4920 + 0x40, 0x00, 0x00, 0x20, 0x00, 0x01, 0x00, 0x20, 0x83, 0x02, 0x00, 0x00, 0x00, 0x30, 0x00, 0x40, /// 0x4930 + 0x80, 0x00, 0x00, 0x20, 0x00, 0x34, 0x00, 0x40, 0x20, 0x06, 0x00, 0x20, 0xcf, 0x03, 0x00, 0x00, /// 0x4940 + 0x20, 0x6c, 0x00, 0x40, 0x70, 0x17, 0x00, 0x00, 0x40, 0x03, 0x00, 0x20, 0x58, 0x69, 0x61, 0x6f, /// 0x4950 + 0x6d, 0x69, 0x52, 0x78, 0x00, 0x00, 0x00, 0x00, 0x20, 0x38, 0x00, 0x40, 0x88, 0x13, 0x00, 0x00, /// 0x4960 + 0xdc, 0x05, 0x00, 0x00, 0xe8, 0x26, 0x00, 0x00, 0x00, 0xf0, 0x70, 0xf8, 0xb8, 0x73, 0x66, 0x87, /// 0x4970 + 0xa6, 0x87, 0x2e, 0x76, 0x01, 0x20, 0x00, 0xf0, 0x99, 0xf8, 0xf8, 0xbd, 0xf8, 0xb5, 0x2b, 0x4c, /// 0x4980 + 0xa0, 0x7b, 0x80, 0x06, 0x01, 0xd5, 0x00, 0xf0, 0xb5, 0xf8, 0xa0, 0x7b, 0x40, 0x06, 0x0a, 0xd5, /// 0x4990 + 0xe0, 0x7b, 0x5a, 0x28, 0x07, 0xd1, 0x60, 0x7b, 0xef, 0x21, 0x08, 0x40, 0x40, 0x21, 0x08, 0x43, /// 0x49a0 + 0x60, 0x73, 0x23, 0x48, 0x80, 0x47, 0xa0, 0x7b, 0x04, 0x27, 0xc1, 0x06, 0x21, 0x4d, 0x00, 0x29, /// 0x49b0 + 0x02, 0xda, 0x69, 0x78, 0x39, 0x43, 0x69, 0x70, 0x00, 0x07, 0x04, 0xd5, 0x1b, 0x48, 0x20, 0x38, /// 0x49c0 + 0xc0, 0x7e, 0x00, 0xf0, 0x9d, 0xf8, 0xa0, 0x7b, 0x08, 0x26, 0x80, 0x07, 0x00, 0x28, 0x15, 0xda, /// 0x49d0 + 0x19, 0x49, 0x48, 0x7b, 0x02, 0x07, 0x03, 0xd5, 0xf7, 0x22, 0x10, 0x40, 0x48, 0x73, 0x0d, 0xe0, /// 0x49e0 + 0x30, 0x43, 0x48, 0x73, 0x00, 0xf0, 0x92, 0xf8, 0x10, 0x48, 0x80, 0x22, 0x40, 0x38, 0x81, 0x8e, /// 0x49f0 + 0x91, 0x43, 0x81, 0x86, 0x11, 0x48, 0x81, 0x89, 0xb9, 0x43, 0x81, 0x81, 0xa0, 0x7b, 0xc1, 0x07, /// 0x4a00 + 0x02, 0xd0, 0x69, 0x78, 0x31, 0x43, 0x69, 0x70, 0x00, 0x06, 0x03, 0xd5, 0xa8, 0x78, 0x01, 0x21, /// 0x4a10 + 0x08, 0x43, 0xa8, 0x70, 0x20, 0x7b, 0xc0, 0x07, 0x02, 0xd0, 0xa8, 0x78, 0x30, 0x43, 0xa8, 0x70, /// 0x4a20 + 0x00, 0x20, 0xe0, 0x73, 0x20, 0x73, 0xa0, 0x73, 0xf8, 0xbd, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, /// 0x4a30 + 0x61, 0x06, 0x00, 0x20, 0x80, 0x00, 0x00, 0x20, 0xa0, 0x01, 0x00, 0x20, 0x20, 0x6c, 0x00, 0x40, /// 0x4a40 + 0x03, 0xb4, 0x01, 0x48, 0x01, 0x90, 0x01, 0xbd, 0xcb, 0x03, 0x00, 0x00, 0x03, 0xb4, 0x01, 0x48, /// 0x4a50 + 0x01, 0x90, 0x01, 0xbd, 0xe9, 0x15, 0x00, 0x00, 0x03, 0xb4, 0x01, 0x48, 0x01, 0x90, 0x01, 0xbd, /// 0x4a60 + 0x49, 0x04, 0x00, 0x00, 0x03, 0xb4, 0x01, 0x48, 0x01, 0x90, 0x01, 0xbd, 0x9d, 0x04, 0x00, 0x00, /// 0x4a70 + 0x03, 0xb4, 0x01, 0x48, 0x01, 0x90, 0x01, 0xbd, 0x7d, 0x08, 0x00, 0x00, 0x03, 0xb4, 0x01, 0x48, /// 0x4a80 + 0x01, 0x90, 0x01, 0xbd, 0x6b, 0x04, 0x00, 0x00, 0x03, 0xb4, 0x01, 0x48, 0x01, 0x90, 0x01, 0xbd, /// 0x4a90 + 0xbf, 0x11, 0x00, 0x00, 0x03, 0xb4, 0x01, 0x48, 0x01, 0x90, 0x01, 0xbd, 0x39, 0x08, 0x00, 0x00, /// 0x4aa0 + 0x03, 0xb4, 0x01, 0x48, 0x01, 0x90, 0x01, 0xbd, 0x97, 0x11, 0x00, 0x00, 0x03, 0xb4, 0x01, 0x48, /// 0x4ab0 + 0x01, 0x90, 0x01, 0xbd, 0x2f, 0x07, 0x00, 0x00, 0x03, 0xb4, 0x01, 0x48, 0x01, 0x90, 0x01, 0xbd, /// 0x4ac0 + 0x7f, 0x05, 0x00, 0x00, 0x03, 0xb4, 0x01, 0x48, 0x01, 0x90, 0x01, 0xbd, 0xd5, 0x05, 0x00, 0x00, /// 0x4ad0 + 0x03, 0xb4, 0x01, 0x48, 0x01, 0x90, 0x01, 0xbd, 0xe5, 0x12, 0x00, 0x00, 0x03, 0xb4, 0x01, 0x48, /// 0x4ae0 + 0x01, 0x90, 0x01, 0xbd, 0x71, 0x15, 0x00, 0x00, 0x03, 0xb4, 0x01, 0x48, 0x01, 0x90, 0x01, 0xbd, /// 0x4af0 + 0x15, 0x15, 0x00, 0x00, 0x03, 0xb4, 0x01, 0x48, 0x01, 0x90, 0x01, 0xbd, 0xef, 0x05, 0x00, 0x00, /// 0x4b00 + 0x03, 0xb4, 0x01, 0x48, 0x01, 0x90, 0x01, 0xbd, 0xa3, 0x12, 0x00, 0x00, 0x03, 0xb4, 0x01, 0x48, /// 0x4b10 + 0x01, 0x90, 0x01, 0xbd, 0x05, 0x0f, 0x00, 0x00, 0x32, 0x33, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, /// 0x4b20 + 0xf0, 0x14, 0xac, 0x28, 0xac, 0x28, 0xac, 0x28, 0xbe, 0x28, 0xb9, 0x28, 0x14, 0x14, 0x00, 0x00, +}; +#endif + +extern uint32_t get_hw_version_major(void); + +#endif diff --git a/drivers/power/supply/qcom/pmic-voter.c b/drivers/power/supply/qcom/pmic-voter.c index 54ef3eeb488c..a97a1773b728 100644 --- a/drivers/power/supply/qcom/pmic-voter.c +++ b/drivers/power/supply/qcom/pmic-voter.c @@ -1,4 +1,5 @@ /* Copyright (c) 2015-2017, 2019 The Linux Foundation. All rights reserved. + * Copyright (C) 2019 XiaoMi, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -487,8 +488,15 @@ int vote(struct votable *votable, const char *client_str, bool enabled, int val) */ if (!votable->voted_on || (effective_result != votable->effective_result)) { + if (strcmp(votable->name, "FG_WS") != 0) { + pr_info("%s: current vote is now %d voted by %s,%d, previous voted %d\n", + votable->name, effective_result, + get_client_str(votable, effective_id), + effective_id, votable->effective_result); + } votable->effective_client_id = effective_id; votable->effective_result = effective_result; + pr_debug("%s: effective vote is now %d voted by %s,%d\n", votable->name, effective_result, get_client_str(votable, effective_id), diff --git a/drivers/power/supply/qcom/qpnp-fg-gen4.c b/drivers/power/supply/qcom/qpnp-fg-gen4.c index 2314b59f2254..d34ae1452097 100644 --- a/drivers/power/supply/qcom/qpnp-fg-gen4.c +++ b/drivers/power/supply/qcom/qpnp-fg-gen4.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "fg-core.h" #include "fg-reg.h" #include "fg-alg.h" @@ -280,6 +281,7 @@ struct fg_gen4_chip { struct votable *parallel_current_en_votable; struct votable *mem_attn_irq_en_votable; struct work_struct esr_calib_work; + struct work_struct vbat_sync_work; struct work_struct soc_scale_work; struct alarm esr_fast_cal_timer; struct alarm soc_scale_alarm_timer; @@ -332,7 +334,7 @@ struct bias_config { int bias_kohms; }; -static int fg_gen4_debug_mask; +static int fg_gen4_debug_mask = FG_STATUS | FG_IRQ; module_param_named( debug_mask, fg_gen4_debug_mask, int, 0600 ); @@ -917,6 +919,18 @@ static int fg_gen4_get_prop_capacity(struct fg_dev *fg, int *val) return 0; } + rc = fg_get_msoc(fg, &msoc); + if (rc < 0) + return rc; + + if (fg->empty_restart_fg && (msoc == 0)) + msoc = EMPTY_REPORT_SOC; + + if (chip->dt.linearize_soc && fg->delta_soc > 0) + *val = fg->maint_soc; + else + *val = msoc; + if (chip->soc_scale_mode) { mutex_lock(&chip->soc_scale_lock); *val = chip->soc_scale_msoc; @@ -1770,6 +1784,13 @@ static int fg_gen4_get_batt_profile(struct fg_dev *fg) fg->bp.vbatt_full_mv = -EINVAL; } + rc = of_property_read_u32(profile_node, "qcom,nom-batt-capacity-mah", + &fg->bp.nom_cap_uah); + if (rc < 0) { + pr_err("battery nominal capacity unavailable, rc:%d\n", rc); + fg->bp.nom_cap_uah = -EINVAL; + } + if (of_find_property(profile_node, "qcom,therm-coefficients", &len)) { len /= sizeof(u32); if (len == BATT_THERM_NUM_COEFFS) { @@ -2738,6 +2759,8 @@ static int fg_gen4_adjust_recharge_soc(struct fg_gen4_chip *chip) if (is_input_present(fg)) { if (fg->charge_done) { if (!fg->recharge_soc_adjusted) { + if (fg->health == POWER_SUPPLY_HEALTH_GOOD) + return 0; /* Get raw monotonic SOC for calculation */ rc = fg_get_msoc(fg, &msoc); if (rc < 0) { @@ -3407,6 +3430,7 @@ static irqreturn_t fg_vbatt_low_irq_handler(int irq, void *data) int rc, vbatt_mv, msoc_raw; s64 time_us; + schedule_work(&chip->vbat_sync_work); rc = fg_get_battery_voltage(fg, &vbatt_mv); if (rc < 0) return IRQ_HANDLED; @@ -3435,6 +3459,20 @@ static irqreturn_t fg_vbatt_low_irq_handler(int irq, void *data) if (vbatt_mv < chip->dt.cutoff_volt_mv) { if (chip->dt.rapid_soc_dec_en) { + /* + * Set vbat_low debounce window to avoid shutdown in low temperature and high + * current scene, we set the counter to maxium 5, if fg_vbatt_low_irq trigger + * exceed 5 times, decrease soc to 0% very rapidly. + */ + fg->vbat_critical_low_count++; + if (fg->vbat_critical_low_count < EMPTY_DEBOUNCE_TIME_COUNT_MAX + && vbatt_mv > VBAT_CRITICAL_LOW_THR) { + pr_info("fg->vbat_critical_low_count:%d\n", + fg->vbat_critical_low_count); + if (batt_psy_initialized(fg)) + power_supply_changed(fg->batt_psy); + return IRQ_HANDLED; + } /* * Set this flag so that slope limiter coefficient * cannot be configured during rapid SOC decrease. @@ -3581,6 +3619,29 @@ static irqreturn_t fg_delta_bsoc_irq_handler(int irq, void *data) return IRQ_HANDLED; } +static bool fg_is_input_suspend(struct fg_dev *fg) +{ + int rc = 0; + union power_supply_propval prop = {0, }; + int input_suspend = 0; + + if (fg->batt_psy) { + rc = power_supply_get_property(fg->batt_psy, + POWER_SUPPLY_PROP_INPUT_SUSPEND, + &prop); + if (rc < 0) { + pr_err("Error in getting input suspend property, rc=%d\n", rc); + return false; + } + input_suspend = prop.intval; + } + + if (input_suspend == 1) + return true; + else + return false; +} + #define CENTI_FULL_SOC 10000 static irqreturn_t fg_delta_msoc_irq_handler(int irq, void *data) { @@ -3589,6 +3650,7 @@ static irqreturn_t fg_delta_msoc_irq_handler(int irq, void *data) int rc, batt_soc, batt_temp, msoc_raw; bool input_present = is_input_present(fg); u32 batt_soc_cp; + bool input_suspend = false; rc = fg_get_msoc_raw(fg, &msoc_raw); if (!rc) @@ -3597,12 +3659,15 @@ static irqreturn_t fg_delta_msoc_irq_handler(int irq, void *data) get_batt_psy_props(fg); + input_suspend = fg_is_input_suspend(fg); + rc = fg_get_sram_prop(fg, FG_SRAM_BATT_SOC, &batt_soc); if (rc < 0) pr_err("Failed to read battery soc rc: %d\n", rc); else cycle_count_update(chip->counter, (u32)batt_soc >> 24, - fg->charge_status, fg->charge_done, input_present); + fg->charge_status, fg->charge_done, + (input_present & (!input_suspend))); rc = fg_gen4_get_battery_temp(fg, &batt_temp); if (rc < 0) { @@ -4037,6 +4102,8 @@ static void pl_current_en_work(struct work_struct *work) return; vote(chip->parallel_current_en_votable, FG_PARALLEL_EN_VOTER, en, 0); + + vote(chip->mem_attn_irq_en_votable, MEM_ATTN_IRQ_VOTER, false, 0); } static void pl_enable_work(struct work_struct *work) @@ -4053,14 +4120,21 @@ static void pl_enable_work(struct work_struct *work) vote(fg->awake_votable, ESR_FCC_VOTER, false, 0); } +static void vbat_sync_work(struct work_struct *work) +{ + pr_err("sys_sync:vbat_sync_work\n"); + sys_sync(); +} + static void status_change_work(struct work_struct *work) { struct fg_dev *fg = container_of(work, struct fg_dev, status_change_work); struct fg_gen4_chip *chip = container_of(fg, struct fg_gen4_chip, fg); - int rc, batt_soc, batt_temp; + int rc, batt_soc, batt_temp, msoc_raw; bool input_present, qnovo_en; u32 batt_soc_cp; + bool input_suspend = false; if (fg->battery_missing) { pm_relax(fg->dev); @@ -4085,6 +4159,16 @@ static void status_change_work(struct work_struct *work) get_batt_psy_props(fg); + if (fg->charge_done && !fg->report_full) { + fg->report_full = true; + } else if (!fg->charge_done && fg->report_full) { + rc = fg_get_msoc_raw(fg, &msoc_raw); + if (rc < 0) + pr_err("Error in getting msoc, rc=%d\n", rc); + if (msoc_raw < FULL_SOC_REPORT_THR - 4) + fg->report_full = false; + } + rc = fg_get_sram_prop(fg, FG_SRAM_BATT_SOC, &batt_soc); if (rc < 0) { pr_err("Failed to read battery soc rc: %d\n", rc); @@ -4098,9 +4182,12 @@ static void status_change_work(struct work_struct *work) } input_present = is_input_present(fg); + fg->input_present = input_present; + input_suspend = fg_is_input_suspend(fg); qnovo_en = is_qnovo_en(fg); cycle_count_update(chip->counter, (u32)batt_soc >> 24, - fg->charge_status, fg->charge_done, input_present); + fg->charge_status, fg->charge_done, + (input_present & (!input_suspend))); batt_soc_cp = div64_u64((u64)(u32)batt_soc * CENTI_FULL_SOC, BATT_SOC_32BIT); @@ -4401,9 +4488,13 @@ static int fg_psy_get_property(struct power_supply *psy, pval->intval = (int)temp; break; case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: - rc = fg_gen4_get_nominal_capacity(chip, &temp); - if (!rc) - pval->intval = (int)temp; + if (-EINVAL != fg->bp.nom_cap_uah) { + pval->intval = fg->bp.nom_cap_uah * 1000; + } else { + rc = fg_gen4_get_nominal_capacity(chip, &temp); + if (!rc) + pval->intval = (int)temp; + } break; case POWER_SUPPLY_PROP_CHARGE_COUNTER: rc = fg_gen4_get_charge_counter(chip, &pval->intval); @@ -4537,6 +4628,10 @@ static int fg_psy_set_property(struct power_supply *psy, if (chip->sp) soh_profile_update(chip->sp, chip->soh); break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + rc = set_cycle_count(chip->counter, pval->intval); + pr_info("Cycle count is modified to %d by userspace\n", pval->intval); + break; case POWER_SUPPLY_PROP_CLEAR_SOH: if (chip->first_profile_load && !pval->intval) { fg_dbg(fg, FG_STATUS, "Clearing first profile load bit\n"); @@ -4560,6 +4655,11 @@ static int fg_psy_set_property(struct power_supply *psy, chip->batt_age_level = pval->intval; schedule_delayed_work(&fg->profile_load_work, 0); break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + if (fg->vbatt_full_volt_uv != pval->intval) + rc = fg_set_constant_chg_voltage(fg, pval->intval); + fg->vbatt_full_volt_uv = pval->intval; + break; case POWER_SUPPLY_PROP_CALIBRATE: rc = fg_gen4_set_calibrate_level(chip, pval->intval); break; @@ -4580,8 +4680,10 @@ static int fg_property_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_ESR_ACTUAL: case POWER_SUPPLY_PROP_ESR_NOMINAL: case POWER_SUPPLY_PROP_SOH: + case POWER_SUPPLY_PROP_CYCLE_COUNT: case POWER_SUPPLY_PROP_CLEAR_SOH: case POWER_SUPPLY_PROP_BATT_AGE_LEVEL: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: case POWER_SUPPLY_PROP_CALIBRATE: return 1; default: @@ -4614,6 +4716,7 @@ static enum power_supply_property fg_psy_props[] = { POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_COUNTER, POWER_SUPPLY_PROP_CHARGE_COUNTER_SHADOW, + POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_CYCLE_COUNTS, POWER_SUPPLY_PROP_SOC_REPORTING_READY, POWER_SUPPLY_PROP_CLEAR_SOH, @@ -4745,6 +4848,7 @@ static int fg_parallel_current_en_cb(struct votable *votable, void *data, return rc; val = enable ? SMB_MEASURE_EN_BIT : 0; + mask = SMB_MEASURE_EN_BIT; rc = fg_masked_write(fg, BATT_INFO_FG_CNV_CHAR_CFG(fg), mask, val); if (rc < 0) @@ -4754,6 +4858,8 @@ static int fg_parallel_current_en_cb(struct votable *votable, void *data, vote(chip->mem_attn_irq_en_votable, MEM_ATTN_IRQ_VOTER, false, 0); fg_dbg(fg, FG_STATUS, "Parallel current summing: %d\n", enable); + /*vote(chip->mem_attn_irq_en_votable, MEM_ATTN_IRQ_VOTER, false, 0);*/ + return rc; } @@ -5612,7 +5718,7 @@ static int fg_gen4_parse_nvmem_dt(struct fg_gen4_chip *chip) #define DEFAULT_CL_MAX_LIM_DECIPERC 0 #define DEFAULT_CL_DELTA_BATT_SOC 10 #define BTEMP_DELTA_LOW 0 -#define BTEMP_DELTA_HIGH 3 +#define BTEMP_DELTA_HIGH 10 #define DEFAULT_ESR_PULSE_THRESH_MA 47 #define DEFAULT_ESR_MEAS_CURR_MA 120 #define DEFAULT_SCALE_VBATT_THR_MV 3400 @@ -5946,6 +6052,116 @@ static int fg_gen4_parse_dt(struct fg_gen4_chip *chip) return 0; } +#define SOC_WORK_MS 20000 +static void soc_work_fn(struct work_struct *work) +{ + struct fg_dev *fg = container_of(work, + struct fg_dev, soc_work.work); + struct fg_gen4_chip *chip = container_of(fg, + struct fg_gen4_chip, fg); + int msoc = 0, soc = 0, curr_ua = 0, volt_uv = 0, temp = 0; + int esr_uohms = 0; + int cycle_count; + int rc; + static int prev_soc = -EINVAL; + + rc = fg_gen4_get_prop_capacity(fg, &soc); + if (rc < 0) + pr_err("Error in getting capacity, rc=%d\n", rc); + + rc = fg_get_msoc_raw(fg, &msoc); + if (rc < 0) + pr_err("Error in getting msoc, rc=%d\n", rc); + + rc = fg_get_battery_resistance(fg, &esr_uohms); + if (rc < 0) + pr_err("Error in getting esr_uohms, rc=%d\n", rc); + + fg_get_battery_current(fg, &curr_ua); + if (rc < 0) + pr_err("failed to get current, rc=%d\n", rc); + + rc = fg_get_battery_voltage(fg, &volt_uv); + if (rc < 0) + pr_err("failed to get voltage, rc=%d\n", rc); + + rc = fg_gen4_get_battery_temp(fg, &temp); + if (rc < 0) + pr_err("Error in getting batt_temp, rc=%d\n", rc); + + rc = get_cycle_count(chip->counter, &cycle_count); + if (rc < 0) + pr_err("failed to get cycle count, rc=%d\n", rc); + + pr_info("adjust_soc: s %d r %d i %d v %d t %d cc %d m 0x%02x\n", + soc, + esr_uohms, + curr_ua/1000, + volt_uv/1000, + temp, + cycle_count, + msoc); + + if (temp < 450 && fg->last_batt_temp >= 450) { + /* follow the way that fg_notifier_cb use wake lock */ + pm_stay_awake(fg->dev); + schedule_work(&fg->status_change_work); + } + + fg->last_batt_temp = temp; + + /* if soc changes, report power supply changed uevent */ + if (soc != prev_soc) { + if (fg->batt_psy) + power_supply_changed(fg->batt_psy); + prev_soc = soc; + } + + schedule_delayed_work( + &fg->soc_work, + msecs_to_jiffies(SOC_WORK_MS)); +} + +static void empty_restart_fg_work(struct work_struct *work) +{ + struct fg_dev *fg = container_of(work, struct fg_dev, + empty_restart_fg_work.work); + union power_supply_propval prop = {0, }; + int usb_present = 0; + int rc; + + if (usb_psy_initialized(fg)) { + rc = power_supply_get_property(fg->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &prop); + if (rc < 0) { + pr_err("Couldn't read usb present prop rc=%d\n", rc); + return; + } + usb_present = prop.intval; + } + + /* only when usb is absent, restart fg */ + if (!usb_present) { + if (fg->profile_load_status == PROFILE_LOADED) { + pr_info("soc empty after cold to warm, need to restart fg\n"); + fg->empty_restart_fg = true; + rc = fg_restart(fg, SOC_READY_WAIT_TIME_MS); + if (rc < 0) { + pr_err("Error in restarting FG, rc=%d\n", rc); + fg->empty_restart_fg = false; + return; + } + pr_info("FG restart done\n"); + if (batt_psy_initialized(fg)) + power_supply_changed(fg->batt_psy); + } else { + schedule_delayed_work( + &fg->empty_restart_fg_work, + msecs_to_jiffies(RESTART_FG_WORK_MS)); + } + } +} + static void fg_gen4_cleanup(struct fg_gen4_chip *chip) { struct fg_dev *fg = &chip->fg; @@ -5957,7 +6173,9 @@ static void fg_gen4_cleanup(struct fg_gen4_chip *chip) fg_gen4_exit_soc_scale(chip); cancel_delayed_work_sync(&fg->profile_load_work); + cancel_delayed_work_sync(&fg->empty_restart_fg_work); cancel_delayed_work_sync(&fg->sram_dump_work); + cancel_delayed_work_sync(&fg->soc_work); cancel_work_sync(&chip->pl_current_en_work); power_supply_unreg_notifier(&fg->nb); @@ -5981,6 +6199,35 @@ static void fg_gen4_cleanup(struct fg_gen4_chip *chip) dev_set_drvdata(fg->dev, NULL); } +#define IBAT_OLD_WORD 317 +#define IBAT_OLD_OFFSET 0 +#define BATT_CURRENT_NUMR 488281 +#define BATT_CURRENT_DENR 1000 +int fg_get_batt_isense(struct fg_dev *fg, int *val) +{ + int rc; + u8 buf[2]; + int64_t temp = 0; + + rc = fg_sram_read(fg, IBAT_OLD_WORD, IBAT_OLD_OFFSET, buf, 2, + FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in reading %04x[%d] rc=%d\n", IBAT_OLD_WORD, + IBAT_OLD_OFFSET, rc); + return rc; + } + + temp = buf[0] | buf[1] << 8; + + /* Sign bit is bit 15 */ + temp = sign_extend32(temp, 15); + *val = div_s64((s64)temp * BATT_CURRENT_NUMR, BATT_CURRENT_DENR); + pr_info("read batt isense: %d[%d]%d\n", + (*val)/10, *val, (*val)/1000); + + return 0; +} + static void fg_gen4_post_init(struct fg_gen4_chip *chip) { int i; @@ -6030,6 +6277,8 @@ static int fg_gen4_probe(struct platform_device *pdev) chip->ki_coeff_full_soc[0] = -EINVAL; chip->ki_coeff_full_soc[1] = -EINVAL; chip->esr_soh_cycle_count = -EINVAL; + fg->vbat_critical_low_count = 0; + fg->vbatt_full_volt_uv = 0; chip->calib_level = -EINVAL; fg->regmap = dev_get_regmap(fg->dev->parent, NULL); if (!fg->regmap) { @@ -6047,9 +6296,12 @@ static int fg_gen4_probe(struct platform_device *pdev) init_completion(&chip->mem_attn); INIT_WORK(&fg->status_change_work, status_change_work); INIT_WORK(&chip->esr_calib_work, esr_calib_work); + INIT_WORK(&chip->vbat_sync_work, vbat_sync_work); INIT_WORK(&chip->soc_scale_work, soc_scale_work); INIT_DELAYED_WORK(&fg->profile_load_work, profile_load_work); INIT_DELAYED_WORK(&fg->sram_dump_work, sram_dump_work); + INIT_DELAYED_WORK(&fg->soc_work, soc_work_fn); + INIT_DELAYED_WORK(&fg->empty_restart_fg_work, empty_restart_fg_work); INIT_DELAYED_WORK(&chip->pl_enable_work, pl_enable_work); INIT_WORK(&chip->pl_current_en_work, pl_current_en_work); @@ -6226,6 +6478,12 @@ static int fg_gen4_probe(struct platform_device *pdev) if (!fg->battery_missing) schedule_delayed_work(&fg->profile_load_work, 0); + schedule_delayed_work(&fg->soc_work, 0); + + if ((volt_uv >= VBAT_RESTART_FG_EMPTY_UV) + && (msoc == 0) && (batt_temp >= TEMP_THR_RESTART_FG)) + schedule_delayed_work(&fg->empty_restart_fg_work, + msecs_to_jiffies(RESTART_FG_START_WORK_MS)); fg_gen4_post_init(chip); pr_debug("FG GEN4 driver probed successfully\n"); @@ -6247,7 +6505,7 @@ static void fg_gen4_shutdown(struct platform_device *pdev) { struct fg_gen4_chip *chip = dev_get_drvdata(&pdev->dev); struct fg_dev *fg = &chip->fg; - int rc, bsoc; + int rc, bsoc, msoc; fg_unregister_interrupts(fg, chip, FG_GEN4_IRQ_MAX); @@ -6261,13 +6519,19 @@ static void fg_gen4_shutdown(struct platform_device *pdev) rc); } + rc = fg_gen4_get_prop_capacity(fg, &msoc); + if (rc < 0) { + pr_err("Error in getting capacity, rc=%d\n", rc); + return; + } + rc = fg_get_sram_prop(fg, FG_SRAM_BATT_SOC, &bsoc); if (rc < 0) { pr_err("Error in getting BATT_SOC, rc=%d\n", rc); return; } - if (fg->charge_full) { + if (fg->charge_full || (msoc == 100)) { /* We need 2 most significant bytes here */ bsoc = (u32)bsoc >> 16; @@ -6291,6 +6555,7 @@ static int fg_gen4_suspend(struct device *dev) struct fg_gen4_chip *chip = dev_get_drvdata(dev); struct fg_dev *fg = &chip->fg; + cancel_delayed_work_sync(&fg->soc_work); cancel_delayed_work_sync(&chip->ttf->ttf_work); if (fg_sram_dump) cancel_delayed_work_sync(&fg->sram_dump_work); @@ -6301,11 +6566,18 @@ static int fg_gen4_resume(struct device *dev) { struct fg_gen4_chip *chip = dev_get_drvdata(dev); struct fg_dev *fg = &chip->fg; + int val = 0; + if (!fg->input_present) + fg_get_batt_isense(fg, &val); + + schedule_delayed_work( + &fg->soc_work, msecs_to_jiffies(SOC_WORK_MS)); schedule_delayed_work(&chip->ttf->ttf_work, 0); if (fg_sram_dump) schedule_delayed_work(&fg->sram_dump_work, msecs_to_jiffies(fg_sram_dump_period_ms)); + return 0; } diff --git a/drivers/power/supply/qcom/qpnp-smb5.c b/drivers/power/supply/qcom/qpnp-smb5.c index bdd75c003cc7..cd4428b95fcc 100644 --- a/drivers/power/supply/qcom/qpnp-smb5.c +++ b/drivers/power/supply/qcom/qpnp-smb5.c @@ -1,4 +1,5 @@ /* Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * Copyright (C) 2019 XiaoMi, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -73,6 +74,13 @@ static struct smb_params smb5_pmi632_params = { .max_u = 1000000, .step_u = 250000, }, + .dc_icl = { + .name = "DC input current limit", + .reg = DCDC_CFG_REF_MAX_PSNS_REG, + .min_u = 0, + .max_u = 1500000, + .step_u = 50000, + }, .jeita_cc_comp_hot = { .name = "jeita fcc reduction", .reg = JEITA_CCCOMP_CFG_HOT_REG, @@ -160,8 +168,8 @@ static struct smb_params smb5_pm8150b_params = { .name = "DC input current limit", .reg = DCDC_CFG_REF_MAX_PSNS_REG, .min_u = 0, - .max_u = DCIN_ICL_MAX_UA, - .step_u = 50000, + .max_u = 1200000, + .step_u = 40000, }, .jeita_cc_comp_hot = { .name = "jeita fcc reduction", @@ -207,6 +215,7 @@ static struct smb_params smb5_pm8150b_params = { struct smb_dt_props { int usb_icl_ua; + int dc_icl_ua; struct device_node *revid_dev_node; enum float_options float_option; int chg_inhibit_thr_mv; @@ -233,7 +242,7 @@ struct smb5 { struct smb_dt_props dt; }; -static int __debug_mask; +static int __debug_mask = PR_MISC | PR_OEM | PR_WLS; module_param_named( debug_mask, __debug_mask, int, 0600 ); @@ -255,6 +264,38 @@ enum { SMB_THERM, }; +static int smb5_get_prop_input_voltage_regulation(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc; +/* + if (!chg->idtp_psy) { + chg->idtp_psy = power_supply_get_by_name("idt"); + if (!chg->idtp_psy) + return -EINVAL; + } +*/ + + chg->idtp_psy = power_supply_get_by_name("idt"); + if (chg->idtp_psy) + chg->wls_chip_psy = chg->idtp_psy; + else { + chg->wip_psy = power_supply_get_by_name("rx1618"); + if (chg->wip_psy) + chg->wls_chip_psy = chg->wip_psy; + else + return -EINVAL; + } + + if (chg->wls_chip_psy) + rc = power_supply_get_property(chg->wls_chip_psy, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, val); + + val->intval = 1000*val->intval;//IDT in mV + + return rc; +} + static const struct clamp_config clamp_levels[] = { { {0x11C6, 0x11F9, 0x13F1}, {0x60, 0x2E, 0x90} }, { {0x11C6, 0x11F9, 0x13F1}, {0x60, 0x2B, 0x9C} }, @@ -384,8 +425,17 @@ static int smb5_configure_internal_pull(struct smb_charger *chg, int type, return rc; } +int smblib_change_psns_to_curr(struct smb_charger *chg, int uv) +{ + dev_info(chg->dev, "get Vpsns = %d uV \n", uv); + uv = uv * PSNS_CURRENT_SAMPLE_RATE / PSNS_CURRENT_SAMPLE_RESIS; + + return uv; +} + #define MICRO_1P5A 1500000 #define MICRO_P1A 100000 +#define MICRO_1P8A_FOR_DCP 1800000 #define MICRO_1PA 1000000 #define MICRO_3PA 3000000 #define OTG_DEFAULT_DEGLITCH_TIME_MS 50 @@ -427,6 +477,17 @@ static int smb5_parse_dt(struct smb5 *chip) of_property_read_bool(node, "qcom,usb-pd-disable"); chg->lpd_disabled = of_property_read_bool(node, "qcom,lpd-disable"); + chg->lpd_enabled = of_property_read_bool(node, + "qcom,lpd-enable"); + + chg->dynamic_fv_enabled = of_property_read_bool(node, + "qcom,dynamic-fv-enable"); + + chg->qc_class_ab = of_property_read_bool(node, + "qcom,distinguish-qc-class-ab"); + + chg->support_wireless = of_property_read_bool(node, + "qcom,support-wireless"); rc = of_property_read_u32(node, "qcom,wd-bark-time-secs", &chip->dt.wd_bark_time); @@ -456,6 +517,11 @@ static int smb5_parse_dt(struct smb5 *chip) if (rc < 0) chip->dt.usb_icl_ua = -EINVAL; + rc = of_property_read_u32(node, + "qcom,dc-icl-ua", &chip->dt.dc_icl_ua); + if (rc < 0) + chip->dt.dc_icl_ua = -EINVAL; + rc = of_property_read_u32(node, "qcom,otg-cl-ua", &chg->otg_cl_ua); if (rc < 0) @@ -475,6 +541,272 @@ static int smb5_parse_dt(struct smb5 *chip) rc = of_property_read_u32(node, "qcom,chg-term-base-current-ma", &chip->dt.term_current_thresh_lo_ma); + if (of_find_property(node, "qcom,lpd_hwversion", &byte_len)) { + chg->lpd_hwversion = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->lpd_hwversion == NULL) + return -ENOMEM; + + chg->lpd_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,lpd_hwversion", + chg->lpd_hwversion, + byte_len / sizeof(u32)); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } +#ifdef CONFIG_THERMAL + if (of_find_property(node, "qcom,thermal-mitigation-dcp", &byte_len)) { + chg->thermal_mitigation_dcp = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_mitigation_dcp == NULL) + return -ENOMEM; + + chg->thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-mitigation-dcp", + chg->thermal_mitigation_dcp, + chg->thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + + if (of_find_property(node, "qcom,thermal-mitigation-dc", &byte_len)) { + chg->thermal_mitigation_dc = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_mitigation_dc == NULL) + return -ENOMEM; + + chg->dc_thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-mitigation-dc", + chg->thermal_mitigation_dc, + chg->dc_thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + + if (of_find_property(node, "qcom,thermal-mitigation-epp", &byte_len)) { + chg->thermal_mitigation_epp = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_mitigation_epp == NULL) + return -ENOMEM; + + chg->dc_thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-mitigation-epp", + chg->thermal_mitigation_epp, + chg->dc_thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + + if (of_find_property(node, "qcom,thermal-mitigation-bpp-qc3", &byte_len)) { + chg->thermal_mitigation_bpp_qc3 = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_mitigation_bpp_qc3 == NULL) + return -ENOMEM; + + chg->dc_thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-mitigation-bpp-qc3", + chg->thermal_mitigation_bpp_qc3, + chg->dc_thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + + if (of_find_property(node, "qcom,thermal-mitigation-bpp-qc2", &byte_len)) { + chg->thermal_mitigation_bpp_qc2 = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_mitigation_bpp_qc2 == NULL) + return -ENOMEM; + + chg->dc_thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-mitigation-bpp-qc2", + chg->thermal_mitigation_bpp_qc2, + chg->dc_thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + + if (of_find_property(node, "qcom,thermal-mitigation-bpp", &byte_len)) { + chg->thermal_mitigation_bpp = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_mitigation_bpp == NULL) + return -ENOMEM; + + chg->dc_thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-mitigation-bpp", + chg->thermal_mitigation_bpp, + chg->dc_thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + + if (of_find_property(node, "qcom,thermal-mitigation-qc2", &byte_len)) { + chg->thermal_mitigation_qc2 = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_mitigation_qc2 == NULL) + return -ENOMEM; + + chg->thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-mitigation-qc2", + chg->thermal_mitigation_qc2, + chg->thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + + if (of_find_property(node, "qcom,thermal-mitigation-pd-base", &byte_len)) { + chg->thermal_mitigation_pd_base = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_mitigation_pd_base == NULL) + return -ENOMEM; + + chg->thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-mitigation-pd-base", + chg->thermal_mitigation_pd_base, + chg->thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + + if (of_find_property(node, "qcom,thermal-fcc-qc3-normal", &byte_len)) { + chg->thermal_fcc_qc3_normal = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_fcc_qc3_normal == NULL) + return -ENOMEM; + + chg->thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-fcc-qc3-normal", + chg->thermal_fcc_qc3_normal, + chg->thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + + if (of_find_property(node, "qcom,thermal-fcc-qc3-cp", &byte_len)) { + chg->thermal_fcc_qc3_cp = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_fcc_qc3_cp == NULL) + return -ENOMEM; + + chg->thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-fcc-qc3-cp", + chg->thermal_fcc_qc3_cp, + chg->thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + + if (of_find_property(node, "qcom,thermal-fcc-qc3-classb-cp", &byte_len)) { + chg->thermal_fcc_qc3_classb_cp = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_fcc_qc3_classb_cp == NULL) + return -ENOMEM; + + chg->thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-fcc-qc3-classb-cp", + chg->thermal_fcc_qc3_classb_cp, + chg->thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + + if (of_find_property(node, "qcom,thermal-fcc-pps-cp", &byte_len)) { + chg->thermal_fcc_pps_cp = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_fcc_pps_cp == NULL) + return -ENOMEM; + + chg->thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-fcc-pps-cp", + chg->thermal_fcc_pps_cp, + chg->thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + + if (of_find_property(node, "qcom,thermal-mitigation-icl", &byte_len)) { + chg->thermal_mitigation_icl = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_mitigation_icl == NULL) + return -ENOMEM; + + chg->thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-mitigation-icl", + chg->thermal_mitigation_icl, + chg->thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } +#else if (of_find_property(node, "qcom,thermal-mitigation", &byte_len)) { chg->thermal_mitigation = devm_kzalloc(chg->dev, byte_len, GFP_KERNEL); @@ -493,7 +825,7 @@ static int smb5_parse_dt(struct smb5 *chip) return rc; } } - +#endif rc = of_property_read_u32(node, "qcom,charger-temp-max", &chg->charger_temp_max); if (rc < 0) @@ -544,7 +876,7 @@ static int smb5_parse_dt(struct smb5 *chip) return -EINVAL; } - chg->dcp_icl_ua = chip->dt.usb_icl_ua; + chg->dcp_icl_ua = MICRO_1P8A_FOR_DCP; chg->suspend_input_on_debug_batt = of_property_read_bool(node, "qcom,suspend-input-on-debug-batt"); @@ -586,6 +918,11 @@ static int smb5_parse_dt(struct smb5 *chip) "qcom,adc-based-aicl"); /* Extract ADC channels */ + rc = smblib_get_iio_channel(chg, "usb_in_voltage", + &chg->iio.usbin_v_chan); + if (rc < 0) + return rc; + rc = smblib_get_iio_channel(chg, "mid_voltage", &chg->iio.mid_chan); if (rc < 0) return rc; @@ -629,6 +966,14 @@ static int smb5_parse_dt(struct smb5 *chip) if (rc < 0) return rc; + rc = smblib_get_iio_channel(chg, "hw_version_gpio5", &chg->iio.hw_version_gpio5); + if (rc < 0) + return rc; + + rc = smblib_get_iio_channel(chg, "project_gpio6", &chg->iio.project_gpio6); + if (rc < 0) + return rc; + chip->dt.disable_suspend_on_collapse = of_property_read_bool(node, "qcom,disable-suspend-on-collapse"); @@ -705,6 +1050,7 @@ static enum power_supply_property smb5_usb_props[] = { POWER_SUPPLY_PROP_TYPEC_CC_ORIENTATION, POWER_SUPPLY_PROP_LOW_POWER, POWER_SUPPLY_PROP_PD_ACTIVE, + POWER_SUPPLY_PROP_PD_AUTHENTICATION, POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, POWER_SUPPLY_PROP_INPUT_CURRENT_NOW, POWER_SUPPLY_PROP_BOOST_CURRENT, @@ -712,6 +1058,9 @@ static enum power_supply_property smb5_usb_props[] = { POWER_SUPPLY_PROP_CTM_CURRENT_MAX, POWER_SUPPLY_PROP_HW_CURRENT_MAX, POWER_SUPPLY_PROP_REAL_TYPE, + POWER_SUPPLY_PROP_HVDCP3_TYPE, + POWER_SUPPLY_PROP_QUICK_CHARGE_TYPE, + POWER_SUPPLY_PROP_PR_SWAP, POWER_SUPPLY_PROP_PD_VOLTAGE_MAX, POWER_SUPPLY_PROP_PD_VOLTAGE_MIN, POWER_SUPPLY_PROP_CONNECTOR_TYPE, @@ -721,6 +1070,7 @@ static enum power_supply_property smb5_usb_props[] = { POWER_SUPPLY_PROP_VOLTAGE_MAX_LIMIT, POWER_SUPPLY_PROP_SMB_EN_MODE, POWER_SUPPLY_PROP_SMB_EN_REASON, + POWER_SUPPLY_PROP_TYPE_RECHECK, POWER_SUPPLY_PROP_ADAPTER_CC_MODE, POWER_SUPPLY_PROP_SCOPE, POWER_SUPPLY_PROP_MOISTURE_DETECTED, @@ -748,6 +1098,11 @@ static int smb5_usb_get_prop(struct power_supply *psy, rc = smblib_get_prop_usb_present(chg, val); break; case POWER_SUPPLY_PROP_ONLINE: + if (chg->report_usb_absent) { + val->intval = 0; + break; + } + rc = smblib_get_prop_usb_online(chg, val); if (!val->intval) break; @@ -789,6 +1144,21 @@ static int smb5_usb_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_REAL_TYPE: val->intval = chg->real_charger_type; break; + case POWER_SUPPLY_PROP_HVDCP3_TYPE: + if (chg->real_charger_type != POWER_SUPPLY_TYPE_USB_HVDCP_3) + val->intval = HVDCP3_NONE; /* 0: none hvdcp3 insert */ + else { + if (chg->is_qc_class_a) + val->intval = HVDCP3_CLASSA_18W; /* 18W hvdcp3 insert */ + else if (chg->is_qc_class_b) + val->intval = HVDCP3_CLASSB_27W; /* 27W hvdcp3 insert */ + else + val->intval = HVDCP3_NONE; + } + break; + case POWER_SUPPLY_PROP_QUICK_CHARGE_TYPE: + val->intval = smblib_get_quick_charge_type(chg); + break; case POWER_SUPPLY_PROP_TYPEC_MODE: if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) val->intval = POWER_SUPPLY_TYPEC_NONE; @@ -819,6 +1189,9 @@ static int smb5_usb_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_PD_ACTIVE: val->intval = chg->pd_active; break; + case POWER_SUPPLY_PROP_PD_AUTHENTICATION: + val->intval = chg->pd_verifed; + break; case POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED: rc = smblib_get_prop_input_current_settled(chg, val); break; @@ -882,6 +1255,9 @@ static int smb5_usb_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_SMB_EN_REASON: val->intval = chg->cp_reason; break; + case POWER_SUPPLY_PROP_TYPE_RECHECK: + rc = smblib_get_prop_type_recheck(chg, val); + break; case POWER_SUPPLY_PROP_MOISTURE_DETECTED: val->intval = chg->moisture_present; break; @@ -950,6 +1326,11 @@ static int smb5_usb_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_PD_ACTIVE: rc = smblib_set_prop_pd_active(chg, val); break; + case POWER_SUPPLY_PROP_PD_AUTHENTICATION: + chg->pd_verifed = val->intval; + rc = vote(chg->usb_icl_votable, PD_VERIFED_VOTER, + !chg->pd_verifed, PD_UNVERIFED_CURRENT); + break; case POWER_SUPPLY_PROP_PD_IN_HARD_RESET: rc = smblib_set_prop_pd_in_hard_reset(chg, val); break; @@ -997,6 +1378,8 @@ static int smb5_usb_set_prop(struct power_supply *psy, else rc = -EINVAL; break; + case POWER_SUPPLY_PROP_TYPE_RECHECK: + rc = smblib_set_prop_type_recheck(chg, val); case POWER_SUPPLY_PROP_VOLTAGE_MAX_LIMIT: smblib_set_prop_usb_voltage_max_limit(chg, val); break; @@ -1027,6 +1410,7 @@ static int smb5_usb_prop_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_MAX_LIMIT: case POWER_SUPPLY_PROP_ADAPTER_CC_MODE: case POWER_SUPPLY_PROP_APSD_RERUN: + case POWER_SUPPLY_PROP_PD_AUTHENTICATION: return 1; default: break; @@ -1050,10 +1434,11 @@ static int smb5_init_usb_psy(struct smb5 *chip) struct power_supply_config usb_cfg = {}; struct smb_charger *chg = &chip->chg; + chg->usb_psy_desc = usb_psy_desc; usb_cfg.drv_data = chip; usb_cfg.of_node = chg->dev->of_node; chg->usb_psy = devm_power_supply_register(chg->dev, - &usb_psy_desc, + &chg->usb_psy_desc, &usb_cfg); if (IS_ERR(chg->usb_psy)) { pr_err("Couldn't register USB power supply\n"); @@ -1086,6 +1471,11 @@ static int smb5_usb_port_get_prop(struct power_supply *psy, val->intval = POWER_SUPPLY_TYPE_USB; break; case POWER_SUPPLY_PROP_ONLINE: + if (chg->report_usb_absent) { + val->intval = 0; + break; + } + rc = smblib_get_prop_usb_online(chg, val); if (!val->intval) break; @@ -1450,15 +1840,15 @@ static int smb5_dc_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_MAX: rc = smblib_get_prop_dc_voltage_max(chg, val); break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION: + smb5_get_prop_input_voltage_regulation(chg, val); + break; case POWER_SUPPLY_PROP_REAL_TYPE: val->intval = POWER_SUPPLY_TYPE_WIPOWER; break; case POWER_SUPPLY_PROP_DC_RESET: val->intval = 0; break; - case POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION: - rc = smblib_get_prop_voltage_wls_output(chg, val); - break; default: return -EINVAL; } @@ -1503,6 +1893,8 @@ static int smb5_dc_prop_is_writeable(struct power_supply *psy, { switch (psp) { case POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION: + case POWER_SUPPLY_PROP_INPUT_SUSPEND: + case POWER_SUPPLY_PROP_CURRENT_MAX: return 1; default: break; @@ -1532,18 +1924,222 @@ static int smb5_init_dc_psy(struct smb5 *chip) &dc_psy_desc, &dc_cfg); if (IS_ERR(chg->dc_psy)) { - pr_err("Couldn't register USB power supply\n"); + pr_err("Couldn't register dc power supply\n"); return PTR_ERR(chg->dc_psy); } return 0; } +static int smb5_get_prop_wireless_signal(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc; + +/* + if (!chg->idtp_psy) { + chg->idtp_psy = power_supply_get_by_name("idt"); + if (!chg->idtp_psy) + return -EINVAL; + } +*/ + chg->idtp_psy = power_supply_get_by_name("idt"); + if (chg->idtp_psy) + chg->wls_chip_psy = chg->idtp_psy; + else { + chg->wip_psy = power_supply_get_by_name("rx1618"); + if (chg->wip_psy) + chg->wls_chip_psy = chg->wip_psy; + else + return -EINVAL; + } + + if (chg->wls_chip_psy) + rc = power_supply_get_property(chg->wls_chip_psy, + POWER_SUPPLY_PROP_SIGNAL_STRENGTH, val); + + return rc; +} + +static int smb5_set_prop_input_voltage_regulation(struct smb_charger *chg, + const union power_supply_propval *val) +{ + int rc; + +/* + if (!chg->idtp_psy) { + chg->idtp_psy = power_supply_get_by_name("idt"); + if (!chg->idtp_psy) + return -EINVAL; + } +*/ + + chg->idtp_psy = power_supply_get_by_name("idt"); + if (chg->idtp_psy) + chg->wls_chip_psy = chg->idtp_psy; + else { + chg->wip_psy = power_supply_get_by_name("rx1618"); + if (chg->wip_psy) + chg->wls_chip_psy = chg->wip_psy; + else + return -EINVAL; + } + + if (chg->wls_chip_psy) + rc = power_supply_set_property(chg->wls_chip_psy, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, val); + + return rc; +} + +static int smb5_get_prop_wirless_type(struct smb_charger *chg, + union power_supply_propval *val) +{ + chg->idtp_psy = power_supply_get_by_name("idt"); + if (chg->idtp_psy) + power_supply_get_property(chg->idtp_psy, + POWER_SUPPLY_PROP_TX_ADAPTER, val); + + return 1; +} + +/************************* + * WIRELESS PSY REGISTRATION * + *************************/ + +static enum power_supply_property smb5_wireless_props[] = { + POWER_SUPPLY_PROP_WIRELESS_VERSION, + POWER_SUPPLY_PROP_SIGNAL_STRENGTH, + POWER_SUPPLY_PROP_WIRELESS_WAKELOCK, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, + POWER_SUPPLY_PROP_WIRELESS_CP_EN, + POWER_SUPPLY_PROP_WIRELESS_POWER_GOOD_EN, + POWER_SUPPLY_PROP_TX_ADAPTER, +}; + +static int smb5_wireless_set_prop(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct smb5 *chip = power_supply_get_drvdata(psy); + struct smb_charger *chg = &chip->chg; + int rc = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_WIRELESS_VERSION: + dev_info(chg->dev, "set version=%d\n", val->intval); + break; + case POWER_SUPPLY_PROP_WIRELESS_WAKELOCK: + rc = smblib_set_prop_wireless_wakelock(chg, val); + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION: + smb5_set_prop_input_voltage_regulation(chg, val); + break; + case POWER_SUPPLY_PROP_WIRELESS_CP_EN: + smblib_set_wirless_cp_enable(chg, val); + break; + case POWER_SUPPLY_PROP_WIRELESS_POWER_GOOD_EN: + smblib_set_wirless_power_good_enable(chg, val); + break; + default: + return -EINVAL; + } + + return rc; +} + +static int smb5_wireless_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct smb5 *chip = power_supply_get_drvdata(psy); + struct smb_charger *chg = &chip->chg; + int rc = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_WIRELESS_VERSION: + rc = smblib_get_prop_wireless_version(chg, val); + break; + case POWER_SUPPLY_PROP_SIGNAL_STRENGTH: + smb5_get_prop_wireless_signal(chg, val); + break; + case POWER_SUPPLY_PROP_WIRELESS_WAKELOCK: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION: + smb5_get_prop_input_voltage_regulation(chg, val); + break; + case POWER_SUPPLY_PROP_WIRELESS_CP_EN: + val->intval = chg->flag_dc_present; + break; + case POWER_SUPPLY_PROP_WIRELESS_POWER_GOOD_EN: + val->intval = chg->power_good_en; + break; + case POWER_SUPPLY_PROP_TX_ADAPTER: + smb5_get_prop_wirless_type(chg, val); + break; + default: + return -EINVAL; + } + if (rc < 0) { + pr_debug("Couldn't get prop %d rc = %d\n", psp, rc); + return -ENODATA; + } + return 0; +} + +static int smb5_wireless_prop_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_WIRELESS_VERSION: + case POWER_SUPPLY_PROP_WIRELESS_WAKELOCK: + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION: + case POWER_SUPPLY_PROP_WIRELESS_CP_EN: + case POWER_SUPPLY_PROP_WIRELESS_POWER_GOOD_EN: + return 1; + default: + break; + } + + return 0; +} + +static const struct power_supply_desc wireless_psy_desc = { + .name = "wireless", + .type = POWER_SUPPLY_TYPE_WIRELESS, + .properties = smb5_wireless_props, + .num_properties = ARRAY_SIZE(smb5_wireless_props), + .get_property = smb5_wireless_get_prop, + .set_property = smb5_wireless_set_prop, + .property_is_writeable = smb5_wireless_prop_is_writeable, +}; + +static int smb5_init_wireless_psy(struct smb5 *chip) +{ + struct power_supply_config wireless_cfg = {}; + struct smb_charger *chg = &chip->chg; + + wireless_cfg.drv_data = chip; + wireless_cfg.of_node = chg->dev->of_node; + chg->wireless_psy = power_supply_register(chg->dev, + &wireless_psy_desc, + &wireless_cfg); + if (IS_ERR(chg->wireless_psy)) { + pr_err("Couldn't register wireless power supply\n"); + return PTR_ERR(chg->wireless_psy); + } + + return 0; +} + /************************* * BATT PSY REGISTRATION * *************************/ static enum power_supply_property smb5_batt_props[] = { POWER_SUPPLY_PROP_INPUT_SUSPEND, + POWER_SUPPLY_PROP_LIQUID_DETECTION, + POWER_SUPPLY_PROP_DYNAMIC_FV_ENABLED, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_PRESENT, @@ -1578,6 +2174,7 @@ static enum power_supply_property smb5_batt_props[] = { POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_FORCE_RECHARGE, POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE, + POWER_SUPPLY_PROP_DC_THERMAL_LEVELS, }; #define DEBUG_ACCESSORY_TEMP_DECIDEGC 250 @@ -1613,6 +2210,9 @@ static int smb5_batt_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX: rc = smblib_get_prop_system_temp_level_max(chg, val); break; + case POWER_SUPPLY_PROP_DC_THERMAL_LEVELS: + rc = smblib_get_prop_dc_temp_level(chg, val); + break; case POWER_SUPPLY_PROP_CHARGER_TEMP: rc = smblib_get_prop_charger_temp(chg, val); break; @@ -1643,8 +2243,9 @@ static int smb5_batt_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_CURRENT_NOW: rc = smblib_get_prop_from_bms(chg, POWER_SUPPLY_PROP_CURRENT_NOW, val); - if (!rc) - val->intval *= (-1); + /* we keep ibat to real value for 6485 or factory test */ + /* if (!rc) + val->intval *= (-1); */ break; case POWER_SUPPLY_PROP_CURRENT_QNOVO: val->intval = get_client_vote_locked(chg->fcc_votable, @@ -1668,7 +2269,7 @@ static int smb5_batt_get_prop(struct power_supply *psy, POWER_SUPPLY_PROP_TEMP, val); break; case POWER_SUPPLY_PROP_TECHNOLOGY: - val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO; break; case POWER_SUPPLY_PROP_CHARGE_DONE: rc = smblib_get_prop_batt_charge_done(chg, val); @@ -1717,6 +2318,15 @@ static int smb5_batt_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE: val->intval = chg->fcc_stepper_enable; break; + case POWER_SUPPLY_PROP_LIQUID_DETECTION: + if (chg->support_liquid == true) + rc = smblib_get_prop_liquid_status(chg, val); + else + val->intval = 0; + break; + case POWER_SUPPLY_PROP_DYNAMIC_FV_ENABLED: + val->intval = chg->dynamic_fv_enabled; + break; default: pr_err("batt power supply prop %d not supported\n", psp); return -EINVAL; @@ -1747,6 +2357,10 @@ static int smb5_batt_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: rc = smblib_set_prop_system_temp_level(chg, val); break; + case POWER_SUPPLY_PROP_DC_THERMAL_LEVELS: + if (chg->support_wireless) + rc = smblib_set_prop_dc_temp_level(chg, val); + break; case POWER_SUPPLY_PROP_CAPACITY: rc = smblib_set_prop_batt_capacity(chg, val); break; @@ -1815,6 +2429,13 @@ static int smb5_batt_set_prop(struct power_supply *psy, vote(chg->chg_disable_votable, FORCE_RECHARGE_VOTER, false, 0); break; + case POWER_SUPPLY_PROP_LIQUID_DETECTION: + chg->lpd_status = val->intval; + power_supply_changed(chg->batt_psy); + break; + case POWER_SUPPLY_PROP_DYNAMIC_FV_ENABLED: + chg->dynamic_fv_enabled = !!val->intval; + break; case POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE: chg->fcc_stepper_enable = val->intval; break; @@ -1839,6 +2460,9 @@ static int smb5_batt_prop_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED: case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED: case POWER_SUPPLY_PROP_DIE_HEALTH: + case POWER_SUPPLY_PROP_DC_THERMAL_LEVELS: + case POWER_SUPPLY_PROP_LIQUID_DETECTION: + case POWER_SUPPLY_PROP_DYNAMIC_FV_ENABLED: return 1; default: break; @@ -2386,15 +3010,22 @@ static int smb5_configure_iterm_thresholds_adc(struct smb5 *chip) max_limit_ma); raw_hi_thresh = sign_extend32(raw_hi_thresh, 15); buf = (u8 *)&raw_hi_thresh; - raw_hi_thresh = buf[1] | (buf[0] << 8); - - rc = smblib_batch_write(chg, CHGR_ADC_ITERM_UP_THD_MSB_REG, - (u8 *)&raw_hi_thresh, 2); + rc = smblib_write(chg, CHGR_ADC_ITERM_UP_THD_MSB_REG, + buf[1]); if (rc < 0) { - dev_err(chg->dev, "Couldn't configure ITERM threshold HIGH rc=%d\n", - rc); + dev_err(chg->dev, "Couldn't set term MSB rc=%d\n", + rc); return rc; } + + rc = smblib_write(chg, CHGR_ADC_ITERM_UP_THD_LSB_REG, + buf[0]); + if (rc < 0) { + dev_err(chg->dev, "Couldn't set term LSB rc=%d\n", + rc); + return rc; + } + } if (chip->dt.term_current_thresh_lo_ma) { @@ -2544,6 +3175,12 @@ static int smb5_init_hw(struct smb5 *chip) &chg->default_aicl_cont_threshold_mv); chg->aicl_cont_threshold_mv = chg->default_aicl_cont_threshold_mv; + if (chip->dt.dc_icl_ua < 0) { + smblib_get_charge_param(chg, &chg->param.dc_icl, + &chip->dt.dc_icl_ua); + chip->dt.dc_icl_ua = smblib_change_psns_to_curr(chg, chip->dt.dc_icl_ua); + } + if (chg->charger_temp_max == -EINVAL) { rc = smblib_get_thermal_threshold(chg, DIE_REG_H_THRESHOLD_MSB_REG, @@ -2689,7 +3326,19 @@ static int smb5_init_hw(struct smb5 *chip) /* Initialize DC peripheral configurations */ rc = smb5_init_dc_peripheral(chg); if (rc < 0) + + /* set dc icl by default voter */ + vote(chg->dc_icl_votable, + DCIN_ADAPTER_VOTER, true, chip->dt.dc_icl_ua); + + /* Disable DC Input missing poller function */ + rc = smblib_masked_write(chg, DCIN_LOAD_CFG_REG, + INPUT_MISS_POLL_EN_BIT, 0); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't disable DC Input missing poller rc=%d\n", rc); return rc; + } /* * AICL configuration: enable aicl and aicl rerun and based on DT @@ -2718,6 +3367,12 @@ static int smb5_init_hw(struct smb5 *chip) return rc; } + rc = smblib_masked_write(chg, DCIN_BASE + 0x65 , BIT(5), 0); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't configure IMP rc=%d\n", rc); + } + /* enable the charging path */ rc = vote(chg->chg_disable_votable, DEFAULT_VOTER, false, 0); if (rc < 0) { @@ -2754,7 +3409,10 @@ static int smb5_init_hw(struct smb5 *chip) rc = smblib_masked_write(chg, WD_CFG_REG, WATCHDOG_TRIGGER_AFP_EN_BIT | WDOG_TIMER_EN_ON_PLUGIN_BIT | - BARK_WDOG_INT_EN_BIT, val); + BARK_WDOG_INT_EN_BIT | + WDOG_TIMER_EN_BIT, + WDOG_TIMER_EN_ON_PLUGIN_BIT | + BARK_WDOG_INT_EN_BIT); if (rc < 0) { pr_err("Couldn't configue WD config rc=%d\n", rc); return rc; @@ -2923,6 +3581,13 @@ static int smb5_init_hw(struct smb5 *chip) } } + rc = smblib_masked_write(chg, TYPE_C_DEBUG_ACC_SNK_CFG, 0x1F, 0x07); + if (rc < 0) { + dev_err(chg->dev, "Couldn't configure TYPE_C_DEBUG_ACC_SNK_CFG rc=%d\n", + rc); + return rc; + } + if (chg->smb_pull_up != -EINVAL) { rc = smb5_configure_internal_pull(chg, SMB_THERM, get_valid_pullup(chg->smb_pull_up)); @@ -2982,10 +3647,18 @@ static int smb5_determine_initial_status(struct smb5 *chip) } chg->early_usb_attach = val.intval; + rc = smblib_get_prop_dc_present(chg, &val); + if (rc < 0) { + pr_err("Couldn't get usb present rc=%d\n", rc); + return rc; + } + chg->early_dc_attach = val.intval; + if (chg->bms_psy) smblib_suspend_on_debug_battery(chg); usb_plugin_irq_handler(0, &irq_data); + dc_plugin_irq_handler(0, &irq_data); typec_attach_detach_irq_handler(0, &irq_data); typec_state_change_irq_handler(0, &irq_data); usb_source_change_irq_handler(0, &irq_data); @@ -3066,7 +3739,6 @@ static struct smb_irq_info smb5_irqs[] = { [BAT_TEMP_IRQ] = { .name = "bat-temp", .handler = batt_temp_changed_irq_handler, - .wake = true, }, [ALL_CHNL_CONV_DONE_IRQ] = { .name = "all-chnl-conv-done", @@ -3136,8 +3808,8 @@ static struct smb_irq_info smb5_irqs[] = { }, [DCIN_UV_IRQ] = { .name = "dcin-uv", - .handler = dcin_uv_irq_handler, - .wake = true, + .handler = dcin_uv_handler, + .wake = true, }, [DCIN_OV_IRQ] = { .name = "dcin-ov", @@ -3163,7 +3835,6 @@ static struct smb_irq_info smb5_irqs[] = { [TYPEC_OR_RID_DETECTION_CHANGE_IRQ] = { .name = "typec-or-rid-detect-change", .handler = typec_or_rid_detection_change_irq_handler, - .wake = true, }, [TYPEC_VPD_DETECT_IRQ] = { .name = "typec-vpd-detect", @@ -3341,6 +4012,11 @@ static int smb5_request_interrupts(struct smb5 *chip) } } + /*enable batt_temp irq when plugin usb poweron charging*/ + if (chg->irq_info[BAT_TEMP_IRQ].irq && (chg->early_usb_attach || chg->early_dc_attach)) { + enable_irq_wake(chg->irq_info[BAT_TEMP_IRQ].irq); + chg->batt_temp_irq_enabled = true; + } vote(chg->limited_irq_disable_votable, CHARGER_TYPE_VOTER, true, 0); vote(chg->hdc_irq_disable_votable, CHARGER_TYPE_VOTER, true, 0); @@ -3503,6 +4179,9 @@ static int smb5_probe(struct platform_device *pdev) chg->connector_health = -EINVAL; chg->otg_present = false; chg->main_fcc_max = -EINVAL; + chg->fake_dc_on = false; + chg->support_liquid = false; + chg->init_once = false; mutex_init(&chg->adc_lock); chg->regmap = dev_get_regmap(chg->dev->parent, NULL); @@ -3539,6 +4218,8 @@ static int smb5_probe(struct platform_device *pdev) /* set driver data before resources request it */ platform_set_drvdata(pdev, chip); + device_init_wakeup(chg->dev, true); + /* extcon registration */ chg->extcon = devm_extcon_dev_allocate(chg->dev, smblib_extcon_cable); if (IS_ERR(chg->extcon)) { @@ -3652,6 +4333,13 @@ static int smb5_probe(struct platform_device *pdev) goto cleanup; } + rc = smb5_init_wireless_psy(chip); + if (rc < 0) { + pr_err("Couldn't initialize wireless psy rc=%d\n", rc); + goto cleanup; + } + + rc = smb5_request_interrupts(chip); if (rc < 0) { pr_err("Couldn't request interrupts rc=%d\n", rc); @@ -3671,10 +4359,10 @@ static int smb5_probe(struct platform_device *pdev) pr_err("Failed in getting charger status rc=%d\n", rc); goto free_irq; } - - device_init_wakeup(chg->dev, true); + schedule_delayed_work(&chg->reg_work, 30 * HZ); pr_info("QPNP SMB5 probed successfully\n"); + smblib_support_liquid_feature(chg); return rc; diff --git a/drivers/power/supply/qcom/smb1390-charger.c b/drivers/power/supply/qcom/smb1390-charger.c index f8fd9dbcdd63..7da9fe00c50d 100644 --- a/drivers/power/supply/qcom/smb1390-charger.c +++ b/drivers/power/supply/qcom/smb1390-charger.c @@ -1,4 +1,5 @@ /* Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * Copyright (C) 2019 XiaoMi, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -87,6 +88,7 @@ #define ILIM_VOTER "ILIM_VOTER" #define FCC_VOTER "FCC_VOTER" #define ICL_VOTER "ICL_VOTER" +#define ICL_CHANGE_VOTER "ICL_CHANGE_VOTER" #define TAPER_END_VOTER "TAPER_END_VOTER" #define WIRELESS_VOTER "WIRELESS_VOTER" #define SRC_VOTER "SRC_VOTER" @@ -139,6 +141,7 @@ struct smb1390 { int irqs[NUM_IRQS]; bool status_change_running; bool taper_work_running; + bool taper_early_trigger; struct smb1390_iio iio; int irq_status; int taper_entry_fv; @@ -343,6 +346,20 @@ static ssize_t stat2_show(struct class *c, struct class_attribute *attr, } static CLASS_ATTR_RO(stat2); +static ssize_t model_name_show(struct class *c, struct class_attribute *attr, + char *buf) +{ + struct smb1390 *chip = container_of(c, struct smb1390, cp_class); + int rc, val; + + rc = smb1390_read(chip, CORE_STATUS1_REG, &val); + if (rc < 0) + return snprintf(buf, PAGE_SIZE, "%s\n", "unknown"); + else + return snprintf(buf, PAGE_SIZE, "%s\n", "smb1390"); +} +static CLASS_ATTR_RO(model_name); + static ssize_t enable_show(struct class *c, struct class_attribute *attr, char *buf) { @@ -469,6 +486,7 @@ static struct attribute *cp_class_attrs[] = { &class_attr_toggle_switcher.attr, &class_attr_die_temp.attr, &class_attr_isns.attr, + &class_attr_model_name.attr, NULL, }; ATTRIBUTE_GROUPS(cp_class); @@ -519,11 +537,11 @@ static int smb1390_ilim_vote_cb(struct votable *votable, void *data, } /* ILIM less than 1A is not accurate; disable charging */ - if (ilim_uA < 1000000) { - pr_debug("ILIM %duA is too low to allow charging\n", ilim_uA); + if (ilim_uA < 900000) { + pr_info("ILIM %duA is too low to allow charging\n", ilim_uA); vote(chip->disable_votable, ILIM_VOTER, true, 0); } else { - pr_debug("setting ILIM to %duA\n", ilim_uA); + pr_info("setting ILIM to %duA\n", ilim_uA); rc = smb1390_masked_write(chip, CORE_FTRIM_ILIM_REG, CFG_ILIM_MASK, DIV_ROUND_CLOSEST(ilim_uA - 500000, 100000)); @@ -575,12 +593,17 @@ static int smb1390_notifier_cb(struct notifier_block *nb, return NOTIFY_OK; } + +#define TAPER_CAPACITY_THR 55 +#define TAPER_CAPCITY_DELTA 1 +#define BATT_COOL_THR 220 static void smb1390_status_change_work(struct work_struct *work) { struct smb1390 *chip = container_of(work, struct smb1390, status_change_work); union power_supply_propval pval = {0, }; int max_fcc_ma, rc; + int capacity, batt_temp, charge_type; if (!is_psy_voter_available(chip)) goto out; @@ -612,6 +635,7 @@ static void smb1390_status_change_work(struct work_struct *work) */ if (pval.intval == POWER_SUPPLY_CP_WIRELESS) { vote(chip->ilim_votable, ICL_VOTER, false, 0); + vote(chip->ilim_votable, ICL_CHANGE_VOTER, false, 0); rc = power_supply_get_property(chip->dc_psy, POWER_SUPPLY_PROP_CURRENT_MAX, &pval); if (rc < 0) @@ -650,6 +674,49 @@ static void smb1390_status_change_work(struct work_struct *work) if (get_effective_result(chip->disable_votable)) goto out; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CAPACITY, &pval); + if (rc < 0) { + pr_err("Couldn't get batt capacity rc=%d\n", rc); + goto out; + } + capacity = pval.intval; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_TEMP, &pval); + if (rc < 0) { + pr_err("Couldn't get batt temp rc=%d\n", rc); + goto out; + } + batt_temp = pval.intval; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CHARGE_TYPE, &pval); + if (rc < 0) { + pr_err("Couldn't get charge type rc=%d\n", rc); + goto out; + } + charge_type = pval.intval; + + pr_info("capacity:%d, batt_temp:%d, charge_type:%d\n", + capacity, batt_temp, charge_type); + + if ((capacity < TAPER_CAPACITY_THR) + && (batt_temp >= BATT_COOL_THR) + && (charge_type == POWER_SUPPLY_CHARGE_TYPE_FAST) + && chip->taper_early_trigger) { + if (is_client_vote_enabled(chip->fcc_votable, + CP_VOTER)) { + /* reset cp_voter here */ + vote(chip->fcc_votable, CP_VOTER, false, 0); + /* input current is always half the charge current */ + vote(chip->ilim_votable, FCC_VOTER, true, + get_effective_result(chip->fcc_votable) / 2); + chip->taper_early_trigger = false; + } + } + rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_CHARGE_TYPE, &pval); if (rc < 0) { @@ -673,6 +740,7 @@ static void smb1390_status_change_work(struct work_struct *work) BATT_PROFILE_VOTER); vote(chip->fcc_votable, CP_VOTER, max_fcc_ma > 0 ? true : false, max_fcc_ma); + chip->taper_early_trigger = false; vote(chip->disable_votable, SOC_LEVEL_VOTER, true, 0); } @@ -686,12 +754,24 @@ static void smb1390_taper_work(struct work_struct *work) struct smb1390 *chip = container_of(work, struct smb1390, taper_work); union power_supply_propval pval = {0, }; int rc, fcc_uA; + int capacity; if (!is_psy_voter_available(chip)) goto out; chip->taper_entry_fv = get_effective_result(chip->fv_votable); while (true) { + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CAPACITY, &pval); + if (rc < 0) { + pr_err("Couldn't get batt capacity rc=%d\n", rc); + goto out; + } + capacity = pval.intval; + if ((capacity < (TAPER_CAPACITY_THR - TAPER_CAPCITY_DELTA)) + && !chip->taper_early_trigger) + chip->taper_early_trigger = true; + rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_CHARGE_TYPE, &pval); if (rc < 0) { @@ -794,7 +874,7 @@ static void smb1390_destroy_votables(struct smb1390 *chip) static int smb1390_init_hw(struct smb1390 *chip) { - int rc; + int rc, val; /* * charge pump is initially disabled; this indirectly votes to allow @@ -820,6 +900,15 @@ static int smb1390_init_hw(struct smb1390 *chip) if (rc < 0) return rc; + rc = smb1390_read(chip, 0x1032, &val); + pr_err("default smb1390 400K 0x1032_REG: 0x%x\n", val); + + rc = smb1390_masked_write(chip, 0x1032, 0x0F, 0x07); + rc = smb1390_read(chip, 0x1032, &val); + pr_err("modify smb1390 800K 0x1032_REG: 0x%x\n", val); + + if (rc < 0) + return rc; return 0; } diff --git a/drivers/power/supply/qcom/smb5-lib.c b/drivers/power/supply/qcom/smb5-lib.c index cd1ae35a17c6..48f43a6b653b 100644 --- a/drivers/power/supply/qcom/smb5-lib.c +++ b/drivers/power/supply/qcom/smb5-lib.c @@ -1,4 +1,5 @@ /* Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * Copyright (C) 2019 XiaoMi, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -47,8 +48,14 @@ || typec_mode == POWER_SUPPLY_TYPEC_SOURCE_HIGH) \ && (!chg->typec_legacy || chg->typec_legacy_use_rp_icl)) +static bool off_charge_flag; + +bool smblib_rsbux_low(struct smb_charger *chg, int r_thr); +static int smblib_get_prop_typec_mode(struct smb_charger *chg); + static void update_sw_icl_max(struct smb_charger *chg, int pst); static int smblib_get_prop_typec_mode(struct smb_charger *chg); +static int smblib_get_prop_dfp_mode(struct smb_charger *chg); int smblib_read(struct smb_charger *chg, u16 addr, u8 *val) { @@ -81,6 +88,10 @@ int smblib_batch_write(struct smb_charger *chg, u16 addr, u8 *val, int smblib_masked_write(struct smb_charger *chg, u16 addr, u8 mask, u8 val) { + if (addr == TYPE_C_MODE_CFG_REG) { + smblib_dbg(chg, PR_MISC, "set 0x1544 mask:0x%x,val:0x%x\n", + mask, val); + } return regmap_update_bits(chg->regmap, addr, mask, val); } @@ -106,6 +117,23 @@ int smblib_get_iio_channel(struct smb_charger *chg, const char *propname, return rc; } +static void smblib_wireless_set_enable(struct smb_charger *chg, int enable) +{ + int rc = 0; + union power_supply_propval val = {0, }; + + chg->idtp_psy = power_supply_get_by_name("idt"); + if (chg->idtp_psy) + { + val.intval = enable; + rc = power_supply_set_property(chg->idtp_psy, POWER_SUPPLY_PROP_PIN_ENABLED, &val); + if (rc < 0) { + smblib_err(chg, "Could not set charger control limit =%d\n", rc); + return; + } + } +} + #define DIV_FACTOR_MICRO_V_I 1 #define DIV_FACTOR_MILI_V_I 1000 #define DIV_FACTOR_DECIDEGC 100 @@ -556,6 +584,7 @@ static const struct apsd_result *smblib_get_apsd_result(struct smb_charger *chg) rc); return result; } + smblib_err(chg, "read APSD_RESULT_STATUS stat=0x%x\n", stat); stat &= APSD_RESULT_STATUS_MASK; for (i = 0; i < ARRAY_SIZE(smblib_apsd_results); i++) { @@ -1061,6 +1090,118 @@ static int smblib_request_dpdm(struct smb_charger *chg, bool enable) return rc; } +#define PERIPHERAL_MASK 0xFF +static u16 peripheral_base; +static char log[256] = ""; +static char version[8] = "smb:01:"; +static inline void dump_reg(struct smb_charger *chg, u16 addr, + const char *name) +{ + u8 reg; + int rc; + char reg_data[50] = ""; + + if (NULL == name) { + strlcat(log, "\n", sizeof(log)); + printk(log); + return; + } + + rc = smblib_read(chg, addr, ®); + if (rc < 0) + smblib_err(chg, "Couldn't read OTG status rc=%d\n", rc); + /* print one peripheral base registers in one line */ + if (peripheral_base != (addr & ~PERIPHERAL_MASK)) { + peripheral_base = addr & ~PERIPHERAL_MASK; + memset(log, 0, sizeof(log)); + snprintf(reg_data, sizeof(reg_data), "%s%04x ", version, peripheral_base); + strlcat(log, reg_data, sizeof(log)); + } + memset(reg_data, 0, sizeof(reg_data)); + snprintf(reg_data, sizeof(reg_data), "%02x ", reg); + strlcat(log, reg_data, sizeof(log)); + + smblib_dbg(chg, PR_REGISTER, "%s - %04X = %02X\n", + name, addr, reg); +} + +static void dump_regs(struct smb_charger *chg) +{ + u16 addr; + + /* charger peripheral */ + for (addr = 0x6; addr <= 0xE; addr++) + dump_reg(chg, CHGR_BASE + addr, "CHGR Status"); + + for (addr = 0x10; addr <= 0x1B; addr++) + dump_reg(chg, CHGR_BASE + addr, "CHGR INT"); + + for (addr = 0x50; addr <= 0x70; addr++) + dump_reg(chg, CHGR_BASE + addr, "CHGR Config"); + + dump_reg(chg, CHGR_BASE + addr, NULL); + + for (addr = 0x10; addr <= 0x1B; addr++) + dump_reg(chg, BATIF_BASE + addr, "BATIF INT"); + + for (addr = 0x50; addr <= 0x52; addr++) + dump_reg(chg, BATIF_BASE + addr, "BATIF Config"); + + for (addr = 0x60; addr <= 0x62; addr++) + dump_reg(chg, BATIF_BASE + addr, "BATIF Config"); + + for (addr = 0x70; addr <= 0x71; addr++) + dump_reg(chg, BATIF_BASE + addr, "BATIF Config"); + + dump_reg(chg, BATIF_BASE + addr, NULL); + + for (addr = 0x6; addr <= 0x10; addr++) + dump_reg(chg, USBIN_BASE + addr, "USBIN Status"); + + for (addr = 0x12; addr <= 0x19; addr++) + dump_reg(chg, USBIN_BASE + addr, "USBIN INT "); + + for (addr = 0x40; addr <= 0x43; addr++) + dump_reg(chg, USBIN_BASE + addr, "USBIN Cmd "); + + for (addr = 0x58; addr <= 0x70; addr++) + dump_reg(chg, USBIN_BASE + addr, "USBIN Config "); + + for (addr = 0x80; addr <= 0x84; addr++) + dump_reg(chg, USBIN_BASE + addr, "USBIN Config "); + + dump_reg(chg, USBIN_BASE + addr, NULL); + + for (addr = 0x06; addr <= 0x1B; addr++) + dump_reg(chg, TYPEC_BASE + addr, "TYPEC Status"); + + for (addr = 0x42; addr <= 0x72; addr++) + dump_reg(chg, TYPEC_BASE + addr, "TYPEC Config"); + + dump_reg(chg, TYPEC_BASE + 0x44, "TYPEC MODE CFG"); + dump_reg(chg, TYPEC_BASE + addr, NULL); + + for (addr = 0x6; addr <= 0x10; addr++) + dump_reg(chg, MISC_BASE + addr, "MISC Status"); + + for (addr = 0x15; addr <= 0x1B; addr++) + dump_reg(chg, MISC_BASE + addr, "MISC INT"); + + for (addr = 0x51; addr <= 0x62; addr++) + dump_reg(chg, MISC_BASE + addr, "MISC Config"); + + for (addr = 0x70; addr <= 0x76; addr++) + dump_reg(chg, MISC_BASE + addr, "MISC Config"); + + for (addr = 0x80; addr <= 0x84; addr++) + dump_reg(chg, MISC_BASE + addr, "MISC Config"); + + for (addr = 0x90; addr <= 0x94; addr++) + dump_reg(chg, MISC_BASE + addr, "MISC Config"); + + dump_reg(chg, MISC_BASE + addr, NULL); +} + void smblib_rerun_apsd(struct smb_charger *chg) { int rc; @@ -1080,6 +1221,7 @@ static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg) /* if PD is active, APSD is disabled so won't have a valid result */ if (chg->pd_active) { chg->real_charger_type = POWER_SUPPLY_TYPE_USB_PD; + chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_PD; } else if (chg->qc3p5_detected) { chg->real_charger_type = POWER_SUPPLY_TYPE_USB_HVDCP_3P5; } else { @@ -1088,8 +1230,10 @@ static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg) * detected as as SDP */ if (!(apsd_result->pst == POWER_SUPPLY_TYPE_USB_FLOAT && - chg->real_charger_type == POWER_SUPPLY_TYPE_USB)) + chg->real_charger_type == POWER_SUPPLY_TYPE_USB)) { chg->real_charger_type = apsd_result->pst; + chg->usb_psy_desc.type = apsd_result->pst; + } } smblib_dbg(chg, PR_MISC, "APSD=%s PD=%d QC3P5=%d\n", @@ -1235,6 +1379,7 @@ static void smblib_uusb_removal(struct smb_charger *chg) del_timer_sync(&chg->apsd_timer); chg->apsd_ext_timeout = false; + chg->report_usb_absent = false; /* write back the default FLOAT charger configuration */ rc = smblib_masked_write(chg, USBIN_OPTIONS_2_CFG_REG, @@ -1305,6 +1450,7 @@ void smblib_suspend_on_debug_battery(struct smb_charger *chg) int smblib_rerun_apsd_if_required(struct smb_charger *chg) { union power_supply_propval val; + const struct apsd_result *apsd_result; int rc; rc = smblib_get_prop_usb_present(chg, &val); @@ -1313,16 +1459,32 @@ int smblib_rerun_apsd_if_required(struct smb_charger *chg) return rc; } - if (!val.intval) + if (!val.intval || chg->fake_usb_insertion) return 0; + /* rc = smblib_request_dpdm(chg, true); if (rc < 0) smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc); + */ chg->uusb_apsd_rerun_done = true; - smblib_rerun_apsd(chg); + if (!off_charge_flag) { + rc = smblib_request_dpdm(chg, true); + if (rc < 0) + smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc); + smblib_rerun_apsd(chg); + } else { + apsd_result = smblib_update_usb_type(chg); + /* if apsd result is SDP and off-charge mode, no need rerun apsd */ + if (!(apsd_result->bit & SDP_CHARGER_BIT)) { + rc = smblib_request_dpdm(chg, true); + if (rc < 0) + smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc); + smblib_rerun_apsd(chg); + } + } return 0; } @@ -1416,6 +1578,7 @@ int smblib_set_icl_current(struct smb_charger *chg, int icl_ua) /* configure current */ if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB + && (icl_ua <= USBIN_500MA) && (chg->typec_legacy || chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT || chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)) { @@ -1430,7 +1593,7 @@ int smblib_set_icl_current(struct smb_charger *chg, int icl_ua) * current limit is 500mA or below for better accuracy; in case * of error, proceed to use USB high-current mode. */ - if (icl_ua <= USBIN_500MA) { + if (icl_ua < USBIN_100MA) { rc = set_sdp_current(chg, icl_ua); if (rc >= 0) goto unsuspend; @@ -1452,6 +1615,7 @@ set_mode: } unsuspend: + /* unsuspend after configuring current and override */ rc = smblib_set_usb_suspend(chg, false); if (rc < 0) { @@ -1651,6 +1815,45 @@ static int smblib_dc_suspend_vote_callback(struct votable *votable, void *data, return smblib_set_dc_suspend(chg, (bool)suspend); } +static int smblib_dc_icl_vote_callback(struct votable *votable, void *data, + int icl_ua, const char *client) +{ + struct smb_charger *chg = data; + int rc = 0; + bool suspend; + int Vpsns; + + if (icl_ua < 0) { + smblib_dbg(chg, PR_MISC, "No Voter hence suspending\n"); + icl_ua = 0; + } + + suspend = (icl_ua <= USBIN_25MA); + if (suspend) + goto suspend; + + Vpsns = (icl_ua/PSNS_CURRENT_SAMPLE_RATE) * PSNS_CURRENT_SAMPLE_RESIS; + if (chg->dc_temp_level == 13 || chg->dc_temp_level == 9) + Vpsns += PSNS_COMP_UV_FOR_HIGH_THERMAL; + + smblib_dbg(chg, PR_OEM, "set icl_ua %d uA and get Vpsns = %d uV\n", icl_ua, Vpsns); + rc = smblib_set_charge_param(chg, &chg->param.dc_icl, Vpsns); + if (rc < 0) { + smblib_err(chg, "Couldn't set DC input current limit rc=%d\n", + rc); + return rc; + } + +suspend: + rc = vote(chg->dc_suspend_votable, USER_VOTER, suspend, 0); + if (rc < 0) { + smblib_err(chg, "Couldn't vote to %s DC rc=%d\n", + suspend ? "suspend" : "resume", rc); + return rc; + } + return rc; +} + static int smblib_awake_vote_callback(struct votable *votable, void *data, int awake, const char *client) { @@ -1895,7 +2098,7 @@ int smblib_get_prop_input_suspend(struct smb_charger *chg, { val->intval = (get_client_vote(chg->usb_icl_votable, USER_VOTER) == 0) - && get_client_vote(chg->dc_suspend_votable, USER_VOTER); + || get_client_vote(chg->dc_suspend_votable, USER_VOTER); return 0; } @@ -1917,8 +2120,55 @@ int smblib_get_prop_batt_present(struct smb_charger *chg, return rc; } +int smblib_get_prop_batt_voltage_now(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc; + if (!chg->bms_psy) + return -EINVAL; + + rc = power_supply_get_property(chg->bms_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, val); + return rc; +} + +static void smblib_check_usb_status(struct smb_charger *chg) +{ + int rc; + int usb_present = 0, vbat_uv = 0; + union power_supply_propval pval = {0,}; + + rc = smblib_get_prop_usb_present(chg, &pval); + if (rc < 0) { + smblib_err(chg, "Couldn't get usb present rc = %d\n", rc); + return; + } + usb_present = pval.intval; + + if (!usb_present) + return; + + rc = smblib_get_prop_batt_voltage_now(chg, &pval); + if (rc < 0) { + pr_err("Couldn't get vbat rc=%d\n", rc); + return; + } + vbat_uv = pval.intval; + + /* + * if battery soc is 0%, vbat is below 3400mV and usb is present in + * normal mode(not power-off charging mode), set online to + * false to notify system to power off. + */ + if ((usb_present == 1) && (!off_charge_flag) + && (vbat_uv <= CUTOFF_VOL_THR)) { + chg->report_usb_absent = true; + power_supply_changed(chg->batt_psy); + } +} + int smblib_get_prop_batt_capacity(struct smb_charger *chg, - union power_supply_propval *val) + union power_supply_propval *val) { int rc = -EINVAL; @@ -1929,9 +2179,72 @@ int smblib_get_prop_batt_capacity(struct smb_charger *chg, rc = smblib_get_prop_from_bms(chg, POWER_SUPPLY_PROP_CAPACITY, val); + if (val->intval == 0) + smblib_check_usb_status(chg); + return rc; } +static bool smblib_wireless_to_usb_charging(struct smb_charger *chg, + bool usb_online, bool dc_online) +{ + if ((usb_online || chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_HIGH) + && chg->fake_dc_on) { + /* insert usb when wireless charging, set status + * to charge and charger type to DCP + * insert pd charger when wireless charging, power good irq will + * trigger very qucickly difference with other charger type. + */ + if (!chg->pd_active) { + chg->real_charger_type = POWER_SUPPLY_TYPE_USB_DCP; + chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_DCP; + } + return true; + } else if (smblib_get_prop_dfp_mode(chg) != POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER + && smblib_get_prop_dfp_mode(chg) != POWER_SUPPLY_TYPEC_NONE + && chg->power_good_en) { + smblib_dbg(chg, PR_WLS, "otg mode, set charging when pwr on\n"); + return true; + } + else + return false; +} + +static bool smblib_is_jeita_warm_charging(struct smb_charger *chg) +{ + union power_supply_propval pval = {0, }; + bool usb_online, dc_online; + int rc; + + rc = smblib_get_prop_usb_online(chg, &pval); + if (rc < 0) { + smblib_err(chg, "Couldn't get usb online property rc=%d\n", + rc); + return rc; + } + usb_online = (bool)pval.intval; + + rc = smblib_get_prop_dc_online(chg, &pval); + if (rc < 0) { + smblib_err(chg, "Couldn't get dc online property rc=%d\n", + rc); + return rc; + } + dc_online = (bool)pval.intval; + + if (!usb_online && !dc_online) + return false; + + rc = smblib_get_prop_batt_health(chg, &pval); + if (rc < 0) + smblib_err(chg, "Couldn't get batt health rc=%d\n", rc); + + if (POWER_SUPPLY_HEALTH_WARM == pval.intval) + return true; + else + return false; +} + static bool is_charging_paused(struct smb_charger *chg) { int rc; @@ -2004,18 +2317,29 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, stat = stat & BATTERY_CHARGER_STATUS_MASK; if (!usb_online && !dc_online) { - switch (stat) { - case TERMINATE_CHARGE: - case INHIBIT_CHARGE: - val->intval = POWER_SUPPLY_STATUS_FULL; - break; - default: - val->intval = POWER_SUPPLY_STATUS_DISCHARGING; - break; - } + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; return rc; } + rc = smblib_get_prop_batt_health(chg, &pval); + if (rc < 0) + smblib_err(chg, "Couldn't get batt health rc=%d\n", rc); + + if ((get_client_vote_locked(chg->usb_icl_votable, JEITA_VOTER) == 0) || + (get_client_vote_locked(chg->dc_suspend_votable, JEITA_VOTER) == 1)) { + if (pval.intval != POWER_SUPPLY_HEALTH_OVERHEAT + && pval.intval != POWER_SUPPLY_HEALTH_COLD) { + val->intval = POWER_SUPPLY_STATUS_CHARGING; + return 0; + } + } + + if (pval.intval == POWER_SUPPLY_HEALTH_OVERHEAT || + pval.intval == POWER_SUPPLY_HEALTH_COLD) { + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + return 0; + } + switch (stat) { case TRICKLE_CHARGE: case PRE_CHARGE: @@ -2025,17 +2349,45 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, break; case TERMINATE_CHARGE: case INHIBIT_CHARGE: - val->intval = POWER_SUPPLY_STATUS_FULL; + if (POWER_SUPPLY_HEALTH_WARM == pval.intval + || POWER_SUPPLY_HEALTH_OVERHEAT == pval.intval) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else { + val->intval = POWER_SUPPLY_STATUS_FULL; + chg->last_batt_stat = val->intval; + } break; case DISABLE_CHARGE: case PAUSE_CHARGE: - val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + if (smblib_is_jeita_warm_charging(chg) + || smblib_wireless_to_usb_charging(chg, usb_online, dc_online)) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; break; default: val->intval = POWER_SUPPLY_STATUS_UNKNOWN; break; } + if (!usb_online && chg->typec_mode != POWER_SUPPLY_TYPEC_SOURCE_HIGH + && dc_online && chg->fake_dc_on) { + /* + * fix insert wireless charger the logo will show twice. + * Also include wireless swith to pd charger, "wireless charger stop" issue. + */ + if (val->intval == POWER_SUPPLY_STATUS_CHARGING) + chg->fake_dc_on = 0; + else if (chg->last_batt_stat == POWER_SUPPLY_STATUS_FULL && + val->intval != POWER_SUPPLY_STATUS_CHARGING) { + val->intval = POWER_SUPPLY_STATUS_FULL; + chg->last_batt_stat = val->intval; + } + else if (val->intval != POWER_SUPPLY_STATUS_FULL) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + return 0; + } + if (is_charging_paused(chg)) { val->intval = POWER_SUPPLY_STATUS_CHARGING; return 0; @@ -2051,16 +2403,18 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, return 0; } - if (val->intval != POWER_SUPPLY_STATUS_CHARGING) + if (val->intval != POWER_SUPPLY_STATUS_CHARGING + || pval.intval == POWER_SUPPLY_HEALTH_WARM) return 0; if (!usb_online && dc_online && chg->fake_batt_status == POWER_SUPPLY_STATUS_FULL) { val->intval = POWER_SUPPLY_STATUS_FULL; + chg->last_batt_stat = val->intval; return 0; } - rc = smblib_read(chg, BATTERY_CHARGER_STATUS_5_REG, &stat); + /* rc = smblib_read(chg, BATTERY_CHARGER_STATUS_5_REG, &stat); if (rc < 0) { smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_2 rc=%d\n", rc); @@ -2071,7 +2425,7 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, ENABLE_FULLON_MODE_BIT; if (!stat) - val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; */ return 0; } @@ -2132,7 +2486,7 @@ int smblib_get_prop_batt_health(struct smb_charger *chg, * If Vbatt is within 40mV above Vfloat, then don't * treat it as overvoltage. */ - effective_fv_uv = get_effective_result(chg->fv_votable); + effective_fv_uv = get_effective_result_locked(chg->fv_votable); if (pval.intval >= effective_fv_uv + 40000) { val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; smblib_err(chg, "battery over-voltage vbat_fg = %duV, fv = %duV\n", @@ -2177,6 +2531,13 @@ int smblib_get_prop_system_temp_level_max(struct smb_charger *chg, return 0; } +int smblib_get_prop_dc_temp_level(struct smb_charger *chg, + union power_supply_propval *val) +{ + val->intval = chg->dc_temp_level; + return 0; +} + int smblib_get_prop_input_current_limited(struct smb_charger *chg, union power_supply_propval *val) { @@ -2258,9 +2619,63 @@ int smblib_get_prop_batt_charge_done(struct smb_charger *chg, stat = stat & BATTERY_CHARGER_STATUS_MASK; val->intval = (stat == TERMINATE_CHARGE); + + if (val->intval == 1) { + vote(chg->awake_votable, CHG_AWAKE_VOTER, false, 0); + vote(chg->awake_votable, DC_AWAKE_VOTER, false, 0); + } return 0; } +int smblib_get_prop_liquid_status(struct smb_charger *chg, + union power_supply_propval *val) +{ + val->intval = 0; + + if (chg->lpd_status) { + val->intval = 1; + } else { + val->intval = 0; + } + return 0; +} + +#define HW_ER_RATIO 2 +#define RF_ADC 1875 + +bool smblib_support_liquid_feature(struct smb_charger *chg) +{ + int hw_version; + int rc, i, data; + int error_value = RF_ADC*HW_ER_RATIO/100; + + if (chg->lpd_enabled == true) { + if (chg->init_once == false) { + rc = smblib_read_iio_channel(chg, chg->iio.hw_version_gpio5, + DIV_FACTOR_MILI_V_I, &hw_version); + if (rc < 0) { + smblib_err(chg, "Couldn't read hw_version_gpio5, rc = %d\n", rc); + return rc; + } else { + smblib_err(chg, "hw_version_gpio5 ADC = %d\n", hw_version); + } + + chg->init_once = true; + + for (i = 0; i < chg->lpd_levels; i++) { + data = (chg->lpd_hwversion[i] * 100) / (100000 + chg->lpd_hwversion[i]); + if (abs(RF_ADC * data / 100 - hw_version) < error_value) { + chg->support_liquid = true; + break; + } + } + } + } + + smblib_err(chg, "support_liquid is %d\n", chg->support_liquid); + return chg->support_liquid; +} + /*********************** * BATTERY PSY SETTERS * ***********************/ @@ -2313,9 +2728,355 @@ int smblib_set_prop_batt_status(struct smb_charger *chg, return 0; } +#define CHARGING_PERIOD_S 600 +#define NOT_CHARGING_PERIOD_S 1200 +static void smblib_reg_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + reg_work.work); + int rc, usb_present; + union power_supply_propval val; + int icl_settle, usb_cur_in, usb_vol_in, icl_sts; + int charger_type, typec_mode, typec_orientation; + + dump_regs(chg); + rc = smblib_get_prop_usb_present(chg, &val); + if (rc < 0) { + pr_err("Couldn't get usb present rc=%d\n", rc); + schedule_delayed_work(&chg->reg_work, + NOT_CHARGING_PERIOD_S * HZ); + return; + } + usb_present = val.intval; + + if (usb_present) { + smblib_dbg(chg, PR_OEM, "ICL vote value is %d voted by %s\n", + get_effective_result(chg->usb_icl_votable), + get_effective_client(chg->usb_icl_votable)); + smblib_dbg(chg, PR_OEM, "FCC vote value is %d voted by %s\n", + get_effective_result(chg->fcc_votable), + get_effective_client(chg->fcc_votable)); + smblib_dbg(chg, PR_OEM, "FV vote value is %d voted by %s\n", + get_effective_result(chg->fv_votable), + get_effective_client(chg->fv_votable)); + + power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_NOW, + &val); + usb_cur_in = val.intval; + + power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + &val); + usb_vol_in = val.intval; + + power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_CURRENT_MAX, + &val); + icl_settle = val.intval; + + power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_REAL_TYPE, + &val); + charger_type = val.intval; + + power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_TYPEC_MODE, + &val); + typec_mode = val.intval; + + power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_TYPEC_CC_ORIENTATION, + &val); + typec_orientation = val.intval; + + smblib_dbg(chg, PR_OEM, + "ICL settle value[%d], usbin adc current[%d], vbusin adc vol[%d]\n", + icl_settle, usb_cur_in, usb_vol_in); + if (!chg->usb_main_psy) { + chg->usb_main_psy = power_supply_get_by_name("main"); + } + else { + power_supply_get_property(chg->usb_main_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, + &val); + icl_sts = val.intval; + smblib_dbg(chg, PR_OEM, "AICL_STS[%d]\n", icl_sts); + } + + smblib_dbg(chg, PR_OEM, + "Type-C orientation[%d], Type-C mode[%d], Real Charger Type[%d]\n", + typec_orientation, typec_mode, charger_type); + + schedule_delayed_work(&chg->reg_work, + CHARGING_PERIOD_S * HZ); + } else { + schedule_delayed_work(&chg->reg_work, + NOT_CHARGING_PERIOD_S * HZ); + } +} + +#define ADAPTER_NONE 0x00 +#define ADAPTER_SDP 0x01 +#define ADAPTER_CDP 0x02 +#define ADAPTER_DCP 0x03 +#define ADAPTER_QC2 0x05 +#define ADAPTER_QC3 0x06 +#define ADAPTER_PD 0x07 +#define ADAPTER_AUTH_FAILED 0x08 +#define ADAPTER_XIAOMI_QC3 0x09 +#define ADAPTER_XIAOMI_PD 0x0a +#define ADAPTER_ZIMI_CAR_POWER 0x0b + +#ifdef CONFIG_THERMAL +static int smblib_dc_therm_charging(struct smb_charger *chg, + int temp_level) +{ + int thermal_icl_ua = 0; + int rc; + union power_supply_propval pval = {0, }; + union power_supply_propval val = {0, }; + + if (!chg->wls_psy) { + chg->wls_psy = power_supply_get_by_name("wireless"); + if (!chg->wls_psy) + return -ENODEV; + } + rc = power_supply_get_property(chg->wls_psy, + POWER_SUPPLY_PROP_TX_ADAPTER, + &pval); + + rc = power_supply_get_property(chg->wls_psy, + POWER_SUPPLY_PROP_WIRELESS_VERSION, + &val); + switch (pval.intval) { + case ADAPTER_XIAOMI_QC3: + case ADAPTER_ZIMI_CAR_POWER: + thermal_icl_ua = chg->thermal_mitigation_dc[temp_level]; + break; + case ADAPTER_QC2: + thermal_icl_ua = chg->thermal_mitigation_bpp_qc2[temp_level]; + break; + case ADAPTER_QC3: + if (val.intval == 1)/*is epp*/ + thermal_icl_ua = chg->thermal_mitigation_epp[temp_level]; + else + thermal_icl_ua = chg->thermal_mitigation_bpp_qc3[temp_level]; + break; + case ADAPTER_XIAOMI_PD: + case ADAPTER_PD: + if (val.intval == 1)/*is epp*/ + thermal_icl_ua = chg->thermal_mitigation_epp[temp_level]; + else + thermal_icl_ua = chg->thermal_mitigation_bpp[temp_level]; + break; + case ADAPTER_AUTH_FAILED: + if (val.intval == 1)/*is epp*/ + thermal_icl_ua = chg->thermal_mitigation_epp[temp_level]; + else + thermal_icl_ua = chg->thermal_mitigation_bpp[temp_level]; + break; + case ADAPTER_CDP: + case ADAPTER_DCP: + thermal_icl_ua = chg->thermal_mitigation_bpp[temp_level]; + break; + default: + thermal_icl_ua = chg->thermal_mitigation_bpp[temp_level]; + break; + } + vote(chg->dc_icl_votable, THERMAL_DAEMON_VOTER, true, thermal_icl_ua); + + return rc; +} +#endif +int smblib_set_prop_dc_temp_level(struct smb_charger *chg, + const union power_supply_propval *val) +{ + union power_supply_propval dc_present; + union power_supply_propval batt_temp; + int rc; + + rc = smblib_get_prop_dc_present(chg, &dc_present); + if (rc < 0) { + pr_err("Couldn't get dc present rc=%d\n", rc); + return -EINVAL; + } + + rc = smblib_get_prop_from_bms(chg, + POWER_SUPPLY_PROP_TEMP, &batt_temp); + if (rc < 0) { + pr_err("Couldn't get batt temp rc=%d\n", rc); + return -EINVAL; + } + + if (val->intval < 0) + return -EINVAL; + if (chg->dc_thermal_levels <= 0) + return -EINVAL; + if (val->intval > chg->dc_thermal_levels) + return -EINVAL; + chg->dc_temp_level = val->intval; + + if (chg->dc_temp_level == chg->dc_thermal_levels) + return vote(chg->chg_disable_votable, + THERMAL_DAEMON_VOTER, true, 0); + + vote(chg->chg_disable_votable, THERMAL_DAEMON_VOTER, false, 0); + if (chg->dc_temp_level == 0) + return vote(chg->dc_icl_votable, THERMAL_DAEMON_VOTER, false, 0); + + smblib_dbg(chg, PR_OEM, "thermal level:%d, batt temp:%d, thermal_levels:%d dc_present=%d\n", + val->intval, batt_temp.intval, chg->dc_thermal_levels,dc_present.intval); +#ifdef CONFIG_THERMAL + smblib_dc_therm_charging(chg, val->intval); +#else + vote(chg->dc_icl_votable, THERMAL_DAEMON_VOTER, true, + chg->thermal_mitigation_dc[chg->dc_temp_level]); + +#endif + return 0; +} + +#ifdef CONFIG_THERMAL +static int smblib_therm_charging(struct smb_charger *chg) +{ + int thermal_icl_ua = 0; + int thermal_fcc_ua = 0; + int rc; + + if (chg->system_temp_level >= MAX_TEMP_LEVEL) + return 0; + + switch (chg->real_charger_type) { + case POWER_SUPPLY_TYPE_USB_HVDCP: + thermal_icl_ua = chg->thermal_mitigation_qc2[chg->system_temp_level]; + break; + case POWER_SUPPLY_TYPE_USB_HVDCP_3: + if (chg->cp_reason == POWER_SUPPLY_CP_HVDCP3) { + if (chg->is_qc_class_a) + thermal_fcc_ua = + chg->thermal_fcc_qc3_cp[chg->system_temp_level]; + else + thermal_fcc_ua = + chg->thermal_fcc_qc3_classb_cp[chg->system_temp_level]; + } else { + thermal_fcc_ua = + chg->thermal_fcc_qc3_normal[chg->system_temp_level]; + } + break; + case POWER_SUPPLY_TYPE_USB_PD: + if (chg->cp_reason == POWER_SUPPLY_CP_PPS) { + thermal_fcc_ua = + chg->thermal_fcc_pps_cp[chg->system_temp_level]; + } else { + if (chg->voltage_min_uv >= PD_MICRO_5V + && chg->voltage_min_uv < PD_MICRO_5P9V) + thermal_icl_ua = + chg->thermal_mitigation_pd_base[chg->system_temp_level]; + else if (chg->voltage_min_uv >= PD_MICRO_5P9V + && chg->voltage_min_uv < PD_MICRO_6P5V) + thermal_icl_ua = + chg->thermal_mitigation_pd_base[chg->system_temp_level] + * PD_6P5V_PERCENT / 100; + else if (chg->voltage_min_uv >= PD_MICRO_6P5V + && chg->voltage_min_uv < PD_MICRO_7P5V) + thermal_icl_ua = + chg->thermal_mitigation_pd_base[chg->system_temp_level] + * PD_7P5V_PERCENT / 100; + else if (chg->voltage_min_uv >= PD_MICRO_7P5V + && chg->voltage_min_uv <= PD_MICRO_8P5V) + thermal_icl_ua = + chg->thermal_mitigation_pd_base[chg->system_temp_level] + * PD_8P5V_PERCENT / 100; + else if (chg->voltage_min_uv >= PD_MICRO_8P5V) + thermal_icl_ua = + chg->thermal_mitigation_pd_base[chg->system_temp_level] + * PD_9V_PERCENT / 100; + else + thermal_icl_ua = + chg->thermal_mitigation_pd_base[chg->system_temp_level]; + } + break; + case POWER_SUPPLY_TYPE_USB_DCP: + default: + thermal_icl_ua = chg->thermal_mitigation_dcp[chg->system_temp_level]; + break; + } + + if (chg->system_temp_level == 0) { + /* if therm_lvl_sel is 0, clear thermal voter */ + rc = vote(chg->usb_icl_votable, THERMAL_DAEMON_VOTER, false, 0); + if (rc < 0) + pr_err("Couldn't disable USB thermal ICL vote rc=%d\n", + rc); + rc = vote(chg->fcc_votable, THERMAL_DAEMON_VOTER, false, 0); + if (rc < 0) + pr_err("Couldn't disable USB thermal ICL vote rc=%d\n", + rc); + } else { + pr_info("thermal_icl_ua is %d, chg->system_temp_level: %d\n", + thermal_icl_ua, chg->system_temp_level); + pr_info("thermal_fcc_ua is %d\n", thermal_fcc_ua); + + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3 + || (chg->cp_reason == POWER_SUPPLY_CP_PPS + && chg->real_charger_type == POWER_SUPPLY_TYPE_USB_PD)) { + if (chg->system_temp_level >= ICL_LIMIT_LEVEL_THR) + thermal_icl_ua = + chg->thermal_mitigation_icl[chg->system_temp_level]; + if (chg->system_temp_level >= ICL_LIMIT_LEVEL_THR) { + rc = vote(chg->usb_icl_votable, THERMAL_DAEMON_VOTER, + true, thermal_icl_ua); + if (rc < 0) + pr_err("Couldn't enable USB thermal ICL vote rc=%d\n", + rc); + } else { + rc = vote(chg->usb_icl_votable, THERMAL_DAEMON_VOTER, + false, 0); + if (rc < 0) + pr_err("Couldn't disable USB thermal ICL vote rc=%d\n", + rc); + } + } else { + if (thermal_icl_ua > 0) { + rc = vote(chg->usb_icl_votable, THERMAL_DAEMON_VOTER, true, + thermal_icl_ua); + if (rc < 0) + pr_err("Couldn't enable USB thermal ICL vote rc=%d\n", + rc); + } + } + if (thermal_fcc_ua > 0) { + rc = vote(chg->fcc_votable, THERMAL_DAEMON_VOTER, true, + thermal_fcc_ua); + if (rc < 0) + pr_err("Couldn't enable USB thermal ICL vote rc=%d\n", + rc); + } + } + + return rc; +} +#endif + int smblib_set_prop_system_temp_level(struct smb_charger *chg, const union power_supply_propval *val) { + int rc; + union power_supply_propval batt_temp = {0, }; + + rc = smblib_get_prop_from_bms(chg, + POWER_SUPPLY_PROP_TEMP, &batt_temp); + if (rc < 0) { + pr_err("Couldn't get batt temp rc=%d\n", rc); + return -EINVAL; + } + + smblib_dbg(chg, PR_OEM, "thermal level:%d, batt temp:%d, thermal_levels:%d " + "chg->system_temp_level:%d, charger_type:%d\n", + val->intval, batt_temp.intval, chg->thermal_levels, + chg->system_temp_level, chg->real_charger_type); + if (val->intval < 0) return -EINVAL; @@ -2327,16 +3088,21 @@ int smblib_set_prop_system_temp_level(struct smb_charger *chg, chg->system_temp_level = val->intval; - if (chg->system_temp_level == chg->thermal_levels) + if (chg->system_temp_level >= (chg->thermal_levels - 1)) return vote(chg->chg_disable_votable, THERMAL_DAEMON_VOTER, true, 0); vote(chg->chg_disable_votable, THERMAL_DAEMON_VOTER, false, 0); + +#ifdef CONFIG_THERMAL + smblib_therm_charging(chg); +#else if (chg->system_temp_level == 0) return vote(chg->fcc_votable, THERMAL_DAEMON_VOTER, false, 0); vote(chg->fcc_votable, THERMAL_DAEMON_VOTER, true, chg->thermal_mitigation[chg->system_temp_level]); +#endif return 0; } @@ -2353,6 +3119,9 @@ int smblib_set_prop_rechg_soc_thresh(struct smb_charger *chg, int rc; u8 new_thr = DIV_ROUND_CLOSEST(val->intval * 255, 100); + if (val->intval == RECHARGE_SOC_THR) + new_thr += 1; + rc = smblib_write(chg, CHARGE_RCHG_SOC_THRESHOLD_CFG_REG, new_thr); if (rc < 0) { @@ -2489,6 +3258,11 @@ static void smblib_hvdcp_adaptive_voltage_change(struct smb_charger *chg) u8 stat; if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP) { + if (chg->qc2_unsupported) { + smblib_hvdcp_set_fsw(chg, QC_5V_BIT); + power_supply_changed(chg->usb_main_psy); + return; + } rc = smblib_read(chg, QC_CHANGE_STATUS_REG, &stat); if (rc < 0) { smblib_err(chg, @@ -2516,8 +3290,18 @@ int smblib_dp_dm(struct smb_charger *chg, int val) union power_supply_propval pval; u8 stat; + if (chg->raise_vbus_to_detect) + return rc; + switch (val) { case POWER_SUPPLY_DP_DM_DP_PULSE: + /* + * if hvdcp_opti wrongly send more than 30 dp pulse(11V) to smb5, + * ignore them to allow maxium vbus as 11V, as charge pump do not + * need the vin more than 11V, and protect the device. + */ + if (chg->pulse_cnt > MAX_PLUSE_COUNT_ALLOWED) + return rc; /* * Pre-emptively increment pulse count to enable the setting * of FSW prior to increasing voltage. @@ -2541,6 +3325,14 @@ int smblib_dp_dm(struct smb_charger *chg, int val) smblib_dbg(chg, PR_PARALLEL, "DP_DM_DP_PULSE rc=%d cnt=%d\n", rc, chg->pulse_cnt); + if (chg->is_qc_class_a && chg->sec_cp_present) { + if (chg->pulse_cnt >= HIGH_NUM_PULSE_THR + && !chg->high_vbus_detected) { + vote(chg->usb_icl_votable, QC_A_CP_ICL_MAX_VOTER, true, + HVDCP_CLASS_A_FOR_CP_UA); + chg->high_vbus_detected = true; + } + } break; case POWER_SUPPLY_DP_DM_DM_PULSE: rc = smblib_dm_pulse(chg); @@ -2548,6 +3340,14 @@ int smblib_dp_dm(struct smb_charger *chg, int val) chg->pulse_cnt--; smblib_dbg(chg, PR_PARALLEL, "DP_DM_DM_PULSE rc=%d cnt=%d\n", rc, chg->pulse_cnt); + if (chg->is_qc_class_a && chg->sec_cp_present) { + if (chg->pulse_cnt < HIGH_NUM_PULSE_THR + && chg->high_vbus_detected) { + vote(chg->usb_icl_votable, QC_A_CP_ICL_MAX_VOTER, false, + 0); + chg->high_vbus_detected = false; + } + } break; case POWER_SUPPLY_DP_DM_ICL_DOWN: target_icl_ua = get_effective_result(chg->usb_icl_votable); @@ -2585,6 +3385,11 @@ int smblib_dp_dm(struct smb_charger *chg, int val) pr_err("Failed to force 5V\n"); break; case POWER_SUPPLY_DP_DM_FORCE_9V: + return 0; + + if (chg->qc2_unsupported) + return 0; + if (chg->qc2_unsupported_voltage == QC2_NON_COMPLIANT_9V) { smblib_err(chg, "Couldn't set 9V: unsupported\n"); return -EINVAL; @@ -2608,8 +3413,15 @@ int smblib_dp_dm(struct smb_charger *chg, int val) rc = smblib_force_vbus_voltage(chg, FORCE_9V_BIT); if (rc < 0) pr_err("Failed to force 9V\n"); + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + HVDCP2_CURRENT_UA); break; case POWER_SUPPLY_DP_DM_FORCE_12V: + return 0; + + if (chg->qc2_unsupported) + return 0; + if (chg->qc2_unsupported_voltage == QC2_NON_COMPLIANT_12V) { smblib_err(chg, "Couldn't set 12V: unsupported\n"); return -EINVAL; @@ -2633,6 +3445,8 @@ int smblib_dp_dm(struct smb_charger *chg, int val) rc = smblib_force_vbus_voltage(chg, FORCE_12V_BIT); if (rc < 0) pr_err("Failed to force 12V\n"); + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + HVDCP2_CURRENT_UA); break; case POWER_SUPPLY_DP_DM_CONFIRMED_HVDCP3P5: chg->qc3p5_detected = true; @@ -2669,6 +3483,36 @@ int smblib_disable_hw_jeita(struct smb_charger *chg, bool disable) return 0; } +int smblib_get_prop_wireless_version(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc; + +/* + if (!chg->idtp_psy) { + chg->idtp_psy = power_supply_get_by_name("idt"); + if (!chg->idtp_psy) + return -EINVAL; + } +*/ + + chg->idtp_psy = power_supply_get_by_name("idt"); + if (chg->idtp_psy) + chg->wls_chip_psy = chg->idtp_psy; + else { + chg->wip_psy = power_supply_get_by_name("rx1618"); + if (chg->wip_psy) + chg->wls_chip_psy = chg->wip_psy; + else + return -EINVAL; + } + + if (chg->wls_chip_psy) + rc = power_supply_get_property(chg->wls_chip_psy, + POWER_SUPPLY_PROP_WIRELESS_VERSION, val); + return rc; +} + static int smblib_set_sw_thermal_regulation(struct smb_charger *chg, bool enable) { @@ -2977,8 +3821,9 @@ int smblib_get_prop_dc_present(struct smb_charger *chg, int smblib_get_prop_dc_online(struct smb_charger *chg, union power_supply_propval *val) { - int rc = 0; + int dc_present, rc = 0; u8 stat; + union power_supply_propval pval = {0, }; if (chg->chg_param.smb_version == PMI632_SUBTYPE) { val->intval = 0; @@ -2990,20 +3835,38 @@ int smblib_get_prop_dc_online(struct smb_charger *chg, return rc; } - if (is_client_vote_enabled(chg->dc_suspend_votable, + if (is_client_vote_enabled_locked(chg->dc_suspend_votable, CHG_TERMINATION_VOTER)) { rc = smblib_get_prop_dc_present(chg, val); return rc; } + rc = smblib_get_prop_dc_present(chg, &pval); + if (rc < 0) { + smblib_err(chg, "Couldn't get usb present rc = %d\n", rc); + return rc; + } + + dc_present = pval.intval; + + + if (dc_present && + get_client_vote_locked(chg->dc_suspend_votable, JEITA_VOTER) == 1) { + val->intval = true; + return rc; + } + + if (chg->fake_dc_on) { + val->intval = true; + return rc; + } + rc = smblib_read(chg, POWER_PATH_STATUS_REG, &stat); if (rc < 0) { smblib_err(chg, "Couldn't read POWER_PATH_STATUS rc=%d\n", rc); return rc; } - smblib_dbg(chg, PR_REGISTER, "POWER_PATH_STATUS = 0x%02x\n", - stat); val->intval = (stat & USE_DCIN_BIT) && (stat & VALID_INPUT_POWER_SOURCE_STS_BIT); @@ -3011,10 +3874,25 @@ int smblib_get_prop_dc_online(struct smb_charger *chg, return rc; } +int smblib_set_prop_wireless_wakelock(struct smb_charger *chg, + const union power_supply_propval *val) +{ + if (val->intval) { + vote(chg->awake_votable, DC_AWAKE_VOTER, true, 0); + //schedule_delayed_work(&chg->dc_input_current_work, + // msecs_to_jiffies(100)); //need enable + } else { + vote(chg->awake_votable, DC_AWAKE_VOTER, false, 0); + //cancel_delayed_work_sync(&chg->dc_input_current_work); + } + return 0; +} + int smblib_get_prop_dc_current_max(struct smb_charger *chg, union power_supply_propval *val) { - return smblib_get_charge_param(chg, &chg->param.dc_icl, &val->intval); + val->intval = get_effective_result_locked(chg->dc_icl_votable); + return 0; } int smblib_get_prop_dc_voltage_max(struct smb_charger *chg, @@ -3039,7 +3917,7 @@ int smblib_get_prop_dc_voltage_now(struct smb_charger *chg, POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, val); if (rc < 0) { - dev_err(chg->dev, "Couldn't get POWER_SUPPLY_PROP_VOLTAGE_REGULATION, rc=%d\n", + dev_err(chg->dev, "Couldn't get POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, rc=%d\n", rc); return rc; } @@ -3054,7 +3932,10 @@ int smblib_get_prop_dc_voltage_now(struct smb_charger *chg, int smblib_set_prop_dc_current_max(struct smb_charger *chg, const union power_supply_propval *val) { - return smblib_set_charge_param(chg, &chg->param.dc_icl, val->intval); + int rc; + + rc = vote(chg->dc_icl_votable, DCIN_ADAPTER_VOTER, true, val->intval); + return rc; } #define DCIN_AICL_RERUN_DELAY_MS 5000 @@ -3062,6 +3943,7 @@ int smblib_set_prop_voltage_wls_output(struct smb_charger *chg, const union power_supply_propval *val) { int rc; + union power_supply_propval pval = {0, }; if (!chg->wls_psy) { chg->wls_psy = power_supply_get_by_name("wireless"); @@ -3069,6 +3951,29 @@ int smblib_set_prop_voltage_wls_output(struct smb_charger *chg, return -ENODEV; } + rc = power_supply_get_property(chg->wls_psy, + POWER_SUPPLY_PROP_TX_ADAPTER, + &pval); + + if (pval.intval == 9 || pval.intval == 11 || pval.intval == 12) { + if (val->intval >= 9200000 && val->intval < 9500000) + vote(chg->dc_icl_votable, DCIN_LIMIT_VOTER, true, 2000000); + else if (val->intval > 9500000) + vote(chg->dc_icl_votable, DCIN_LIMIT_VOTER, true, 1800000); + else + vote(chg->dc_icl_votable, DCIN_LIMIT_VOTER, false, 0); + } else if (pval.intval == 6) { + if (val->intval > 9000000 && val->intval <= 9500000) + vote(chg->dc_icl_votable, DCIN_LIMIT_VOTER, true, 1100000); + else if (val->intval > 9500000) + vote(chg->dc_icl_votable, DCIN_LIMIT_VOTER, true, 1000000); + else + vote(chg->dc_icl_votable, DCIN_LIMIT_VOTER, false, 0); + } else { + smblib_dbg(chg, PR_WLS, "WLS chg type error %d\n", pval.intval); + return rc; + } + rc = power_supply_set_property(chg->wls_psy, POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, val); @@ -3167,8 +4072,9 @@ int smblib_get_prop_usb_present(struct smb_charger *chg, int smblib_get_prop_usb_online(struct smb_charger *chg, union power_supply_propval *val) { - int rc = 0; + int usb_present, rc = 0; u8 stat; + union power_supply_propval pval = {0, }; if (get_client_vote_locked(chg->usb_icl_votable, USER_VOTER) == 0) { val->intval = false; @@ -3181,6 +4087,34 @@ int smblib_get_prop_usb_online(struct smb_charger *chg, return rc; } + rc = smblib_get_prop_usb_present(chg, &pval); + if (rc < 0) { + smblib_err(chg, "Couldn't get usb present rc = %d\n", rc); + return rc; + } + + usb_present = pval.intval; + + if (usb_present && chg->typec_mode == POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER) { + val->intval = true; + return rc; + } + + if (usb_present && + get_client_vote_locked(chg->usb_icl_votable, JEITA_VOTER) == 0) { + val->intval = true; + return rc; + } + + if (usb_present && chg->power_good_en) { + /* show online when insert usb in wireless charging */ + if (chg->typec_mode == POWER_SUPPLY_TYPEC_SINK) + val->intval = false; + else + val->intval = true; + return rc; + } + rc = smblib_read(chg, POWER_PATH_STATUS_REG, &stat); if (rc < 0) { smblib_err(chg, "Couldn't read POWER_PATH_STATUS rc=%d\n", @@ -3408,6 +4342,23 @@ int smblib_get_prop_vph_voltage_now(struct smb_charger *chg, return 0; } +int smblib_get_usb_in_voltage_now(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc, ret = 0; + + if (chg->iio.usbin_v_chan) { + rc = iio_read_channel_processed(chg->iio.usbin_v_chan, + &val->intval); + if (rc < 0) + ret = -ENODATA; + } else { + ret = -ENODATA; + } + + return ret; +} + bool smblib_rsbux_low(struct smb_charger *chg, int r_thr) { int r_sbu1, r_sbu2; @@ -3439,6 +4390,7 @@ bool smblib_rsbux_low(struct smb_charger *chg, int r_thr) goto cleanup; } + pr_info("r_sbu1 val is %d\n", r_sbu1); if (r_sbu1 < r_thr) { ret = true; goto cleanup; @@ -3457,6 +4409,7 @@ bool smblib_rsbux_low(struct smb_charger *chg, int r_thr) goto cleanup; } + pr_info("r_sbu2 val is %d\n", r_sbu2); if (r_sbu2 < r_thr) ret = true; cleanup: @@ -3541,6 +4494,8 @@ static int smblib_get_prop_ufp_mode(struct smb_charger *chg) { int rc; u8 stat; + union power_supply_propval val = {0, }; + int usb_present = 0; rc = smblib_read(chg, TYPE_C_SNK_STATUS_REG, &stat); if (rc < 0) { @@ -3566,6 +4521,26 @@ static int smblib_get_prop_ufp_mode(struct smb_charger *chg) break; } + /* workaround for scp cable or similar A TO C cables */ + rc = smblib_read(chg, TYPE_C_SNK_DEBUG_ACC_STATUS_REG, &stat); + if (rc < 0) { + smblib_err(chg, "Couldn't read TYPE_C_STATUS_1 rc=%d\n", rc); + return POWER_SUPPLY_TYPEC_NONE; + } + + rc = smblib_get_prop_usb_present(chg, &val); + if (rc < 0) + smblib_err(chg, "Couldn't get usb present rc = %d\n", rc); + else + usb_present = val.intval; + + if (chg->snk_debug_acc_detected && usb_present) { + return POWER_SUPPLY_TYPEC_SOURCE_DEFAULT; + } + if (stat & SNK_DEBUG_ACC_RPSTD_PRSTD_BIT && usb_present) { + chg->snk_debug_acc_detected = true; + return POWER_SUPPLY_TYPEC_SOURCE_DEFAULT; + } return POWER_SUPPLY_TYPEC_NONE; } @@ -3583,7 +4558,6 @@ static int smblib_get_prop_dfp_mode(struct smb_charger *chg) rc); return POWER_SUPPLY_TYPEC_NONE; } - smblib_dbg(chg, PR_REGISTER, "TYPE_C_SRC_STATUS_REG = 0x%02x\n", stat); switch (stat & DETECTED_SNK_TYPE_MASK) { case AUDIO_ACCESS_RA_RA_BIT: @@ -3632,7 +4606,7 @@ int smblib_get_prop_typec_power_role(struct smb_charger *chg, rc); return rc; } - smblib_dbg(chg, PR_REGISTER, "TYPE_C_MODE_CFG_REG = 0x%02x\n", + smblib_dbg(chg, PR_MISC, "TYPE_C_MODE_CFG_REG = 0x%02x\n", ctrl); if (ctrl & TYPEC_DISABLE_CMD_BIT) { @@ -3947,6 +4921,8 @@ static int smblib_get_typec_connector_temp_status(struct smb_charger *chg) return POWER_SUPPLY_HEALTH_COOL; } +#define HVDCP_START_CURRENT_UA 1000000 + int smblib_get_skin_temp_status(struct smb_charger *chg) { int rc; @@ -4039,14 +5015,28 @@ int smblib_set_prop_pd_current_max(struct smb_charger *chg, return rc; } +#define FLOAT_CHARGER_UA 1000000 +#define SUSPEND_CURRENT_UA 2000 static int smblib_handle_usb_current(struct smb_charger *chg, int usb_current) { int rc = 0, rp_ua, typec_mode; union power_supply_propval val = {0, }; + bool is_float = false; + + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_FLOAT + && (usb_current == SUSPEND_CURRENT_UA)) + is_float = true; + + if ((usb_current > 0 && usb_current < USBIN_500MA) + || (usb_current == USBIN_900MA)) + usb_current = USBIN_500MA; if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_FLOAT) { - if (usb_current == -ETIMEDOUT) { + if (usb_current == -ETIMEDOUT + || is_float) { + /* we do not use USB500mA for float charger */ +#if 0 if ((chg->float_cfg & FLOAT_OPTIONS_MASK) == FORCE_FLOAT_SDP_CFG_BIT) { /* @@ -4061,7 +5051,7 @@ static int smblib_handle_usb_current(struct smb_charger *chg, rc); return rc; } - +#endif if (chg->connector_type == POWER_SUPPLY_CONNECTOR_TYPEC) { /* @@ -4071,6 +5061,8 @@ static int smblib_handle_usb_current(struct smb_charger *chg, typec_mode = smblib_get_prop_typec_mode(chg); rp_ua = get_rp_based_dcp_current(chg, typec_mode); + if (rp_ua == DCP_CURRENT_UA) + rp_ua = FLOAT_CHARGER_UA; rc = vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, rp_ua); if (rc < 0) @@ -4088,6 +5080,7 @@ static int smblib_handle_usb_current(struct smb_charger *chg, * real_charger_type */ chg->real_charger_type = POWER_SUPPLY_TYPE_USB; + chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_USB; rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, true, usb_current); if (rc < 0) @@ -4139,7 +5132,7 @@ int smblib_set_prop_sdp_current_max(struct smb_charger *chg, } /* handle the request only when USB is present */ - if (pval.intval) + if (pval.intval && (val->intval != 0)) rc = smblib_handle_usb_current(chg, val->intval); } else if (chg->system_suspend_supported) { if (val->intval <= USBIN_25MA) @@ -4152,6 +5145,32 @@ int smblib_set_prop_sdp_current_max(struct smb_charger *chg, return rc; } +int smblib_get_prop_type_recheck(struct smb_charger *chg, + union power_supply_propval *val) +{ + int status = 0; + + if (chg->recheck_charger) + status |= BIT(0) << 8; + + status |= chg->precheck_charger_type << 4; + status |= chg->real_charger_type; + + val->intval = status; + + return 0; +} + +int smblib_set_prop_type_recheck(struct smb_charger *chg, + const union power_supply_propval *val) +{ + if (val->intval == 0) { + cancel_delayed_work_sync(&chg->charger_type_recheck); + chg->recheck_charger = false; + } + return 0; +} + int smblib_set_prop_boost_current(struct smb_charger *chg, const union power_supply_propval *val) { @@ -4296,6 +5315,8 @@ int smblib_set_prop_typec_power_role(struct smb_charger *chg, return -EINVAL; } + smblib_dbg(chg, PR_MISC, "set power_role to 0x%x\n", power_role); + rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG, TYPEC_POWER_ROLE_CMD_MASK | TYPEC_TRY_MODE_MASK, power_role); @@ -4414,6 +5435,11 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0); + /*set the icl to PD_UNVERIFED_CURRENT when pd is not verifed*/ + rc = vote(chg->usb_icl_votable, PD_VERIFED_VOTER, true, PD_UNVERIFED_CURRENT); + if (rc < 0) + smblib_err(chg, "Couldn't unvote PD_VERIFED_VOTER, rc=%d\n", rc); + /* * For PPS, Charge Pump is preferred over parallel charger if * present. @@ -4450,7 +5476,12 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, smblib_hvdcp_detect_enable(chg, true); } } + if (!chg->fake_usb_insertion) + smblib_update_usb_type(chg); +#ifdef CONFIG_THERMAL + smblib_therm_charging(chg); +#endif smblib_usb_pd_adapter_allowance_override(chg, !!chg->pd_active ? FORCE_5V : FORCE_NULL); smblib_update_usb_type(chg); @@ -4545,6 +5576,9 @@ static int smblib_soft_jeita_arb_wa(struct smb_charger *chg) int rc = 0; bool soft_jeita; + /* we still use old soft jeita method */ + return 0; + rc = smblib_get_prop_batt_health(chg, &pval); if (rc < 0) { smblib_err(chg, "Couldn't get battery health rc=%d\n", rc); @@ -4685,12 +5719,18 @@ int smblib_get_charge_current(struct smb_charger *chg, typec_source_rd = smblib_get_prop_ufp_mode(chg); - /* QC 2.0/3.0 adapter */ - if (apsd_result->bit & (QC_3P0_BIT | QC_2P0_BIT)) { + /* QC 3.0 adapter */ + if (apsd_result->bit & QC_3P0_BIT) { *total_current_ua = HVDCP_CURRENT_UA; return 0; } + /* QC 2.0 adapter */ + if (apsd_result->bit & QC_2P0_BIT) { + *total_current_ua = HVDCP2_CURRENT_UA; + return 0; + } + if (non_compliant && !chg->typec_legacy_use_rp_icl) { switch (apsd_result->bit) { case CDP_CHARGER_BIT: @@ -4871,6 +5911,33 @@ static void smblib_eval_chg_termination(struct smb_charger *chg, u8 batt_status) } } +irqreturn_t dcin_uv_handler(int irq, void *data) +{ + struct smb_irq_data *irq_data = data; + struct smb_charger *chg = irq_data->parent_data; + + int rc; + u8 stat; + + rc = smblib_read(chg, POWER_PATH_STATUS_REG, &stat); + if (rc < 0) { + smblib_err(chg, "Couldn't read POWER_PATH_STATUS rc=%d\n", + rc); + return IRQ_HANDLED; + } + + if ((stat & POWER_PATH_MASK) == 0x2) { /*power by battery*/ + chg->fake_dc_on = 1; /*use for delay 1.8s*/ + chg->fake_dc_flag = 1; + schedule_delayed_work(&chg->dc_plug_out_delay_work, + msecs_to_jiffies(1800)); + vote(chg->awake_votable, DC_UV_AWAKE_VOTER, true, 0); + } + smblib_dbg(chg, PR_WLS, "Delay dc plug out and power path 0x%x\n", stat); + + return IRQ_HANDLED; +} + irqreturn_t chg_state_change_irq_handler(int irq, void *data) { struct smb_irq_data *irq_data = data; @@ -4902,8 +5969,7 @@ irqreturn_t batt_temp_changed_irq_handler(int irq, void *data) struct smb_charger *chg = irq_data->parent_data; int rc; - smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); - + smblib_dbg(chg, PR_OEM, "IRQ: %s\n", irq_data->name); if (chg->jeita_configured != JEITA_CFG_COMPLETE) return IRQ_HANDLED; @@ -4914,6 +5980,10 @@ irqreturn_t batt_temp_changed_irq_handler(int irq, void *data) return IRQ_HANDLED; } + /* we still use old soft jeita method */ + rerun_election(chg->fcc_votable); + power_supply_changed(chg->batt_psy); + return IRQ_HANDLED; } @@ -5003,6 +6073,8 @@ unsuspend_input: reset_storm_count(wdata); } + smblib_dbg(chg, PR_OEM, "IRQ: %s\n", irq_data->name); + if (!chg->irq_info[SWITCHER_POWER_OK_IRQ].irq_data) return IRQ_HANDLED; @@ -5011,6 +6083,7 @@ unsuspend_input: /* Workaround for non-QC2.0-compliant chargers follows */ if (!chg->qc2_unsupported_voltage && + !chg->qc2_unsupported && apsd->pst == POWER_SUPPLY_TYPE_USB_HVDCP) { rc = smblib_read(chg, QC_CHANGE_STATUS_REG, &stat); if (rc < 0) @@ -5056,6 +6129,20 @@ unsuspend_input: rc); smblib_rerun_apsd(chg); + + pr_info("qc2_unsupported charger detected\n"); + rc = smblib_force_vbus_voltage(chg, FORCE_5V_BIT); + if (rc < 0) + pr_err("Failed to force 5V\n"); + rc = smblib_usb_pd_adapter_allowance_override(chg, !!chg->pd_active ? FORCE_5V : FORCE_NULL); + if(rc < 0) + pr_err("Failed to set adapter allowance to 5V (adapted)\n"); + rc = smblib_set_opt_switcher_freq(chg, chg->chg_freq.freq_5V); + if (rc < 0) + pr_err("Failed to set chg_freq.freq_5V\n"); + vote(chg->usb_icl_votable, QC2_UNSUPPORTED_VOTER, true, + QC2_UNSUPPORTED_UA); + chg->qc2_unsupported = true; } return IRQ_HANDLED; @@ -5115,6 +6202,46 @@ irqreturn_t icl_change_irq_handler(int irq, void *data) return IRQ_HANDLED; } +static void smblib_cc_un_compliant_charge_work(struct work_struct *work) +{ + union power_supply_propval val = {0, }; + int rc, usb_present = 0; + + struct smb_charger *chg = container_of(work, struct smb_charger, + cc_un_compliant_charge_work.work); + + rc = smblib_get_prop_usb_present(chg, &val); + if (rc < 0) { + smblib_err(chg, "Couldn't get usb present rc = %d\n", rc); + return; + } + + usb_present = val.intval; + + if (usb_present && chg->typec_mode == POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER) { + chg->real_charger_type = POWER_SUPPLY_TYPE_USB_FLOAT; + chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_FLOAT; + if ((strcmp(get_effective_client(chg->usb_icl_votable), "OTG_VOTER") == 0) && + (get_effective_result(chg->usb_icl_votable) == 0)) + vote(chg->usb_icl_votable, OTG_VOTER, false, 0); + + if (get_client_vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER) != 500000) + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, 500000); + } + + if (usb_present + && (chg->typec_mode == POWER_SUPPLY_TYPEC_NONE || + chg->typec_mode == POWER_SUPPLY_TYPEC_NON_COMPLIANT || + chg->snk_debug_acc_detected == true) + && (chg->cc_un_compliant_detected == false)) { + chg->cc_un_compliant_detected = true; + //smblib_apsd_enable(chg, true); + smblib_hvdcp_detect_enable(chg, true); + smblib_rerun_apsd_if_required(chg); + } +} + + static void smblib_micro_usb_plugin(struct smb_charger *chg, bool vbus_rising) { if (!vbus_rising) { @@ -5141,6 +6268,7 @@ void smblib_usb_plugin_hard_reset_locked(struct smb_charger *chg) vbus_rising = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT); if (vbus_rising) { + vote(chg->awake_votable, CHG_AWAKE_VOTER, true, 0); /* Remove FCC_STEPPER 1.5A init vote to allow FCC ramp up */ if (chg->fcc_stepper_enable) vote(chg->fcc_votable, FCC_STEPPER_VOTER, false, 0); @@ -5158,6 +6286,16 @@ void smblib_usb_plugin_hard_reset_locked(struct smb_charger *chg) } } + cancel_delayed_work_sync(&chg->charger_type_recheck); + chg->recheck_charger = false; + chg->precheck_charger_type = POWER_SUPPLY_TYPE_UNKNOWN; + if (chg->cc_un_compliant_detected) { + smblib_hvdcp_detect_enable(chg, false); + chg->cc_un_compliant_detected = false; + } + + vote(chg->awake_votable, CHG_AWAKE_VOTER, false, 0); + /* Force 1500mA FCC on USB removal if fcc stepper is enabled */ if (chg->fcc_stepper_enable) vote(chg->fcc_votable, FCC_STEPPER_VOTER, @@ -5165,7 +6303,7 @@ void smblib_usb_plugin_hard_reset_locked(struct smb_charger *chg) } power_supply_changed(chg->usb_psy); - smblib_dbg(chg, PR_INTERRUPT, "IRQ: usbin-plugin %s\n", + smblib_dbg(chg, PR_OEM, "IRQ: usbin-plugin %s\n", vbus_rising ? "attached" : "detached"); } @@ -5189,6 +6327,14 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) chg->chg_freq.freq_removal); if (vbus_rising) { + if (smblib_get_prop_dfp_mode(chg) != POWER_SUPPLY_TYPEC_NONE + && smblib_get_prop_dfp_mode(chg) + != POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER) { + chg->fake_usb_insertion = true; + return; + } + + vote(chg->awake_votable, CHG_AWAKE_VOTER, true, 0); cancel_delayed_work_sync(&chg->pr_swap_detach_work); vote(chg->awake_votable, DETACH_DETECT_VOTER, false, 0); rc = smblib_request_dpdm(chg, true); @@ -5209,7 +6355,18 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) vote(chg->awake_votable, PL_DELAY_VOTER, true, 0); schedule_delayed_work(&chg->pl_enable_work, msecs_to_jiffies(PL_DELAY_MS)); + schedule_delayed_work(&chg->charger_type_recheck, + msecs_to_jiffies(CHARGER_RECHECK_DELAY_MS)); + schedule_delayed_work(&chg->cc_un_compliant_charge_work, + msecs_to_jiffies(CC_UN_COMPLIANT_START_DELAY_MS)); } else { + if (chg->fake_usb_insertion) { + chg->fake_usb_insertion = false; + return; + } + + cancel_delayed_work_sync(&chg->charger_type_recheck); + /* Disable SW Thermal Regulation */ rc = smblib_set_sw_thermal_regulation(chg, false); if (rc < 0) @@ -5265,6 +6422,13 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) if (rc < 0) smblib_err(chg, "Couldn't disable DPDM rc=%d\n", rc); + if (chg->cc_un_compliant_detected) { + smblib_hvdcp_detect_enable(chg, false); + chg->cc_un_compliant_detected = false; + } + chg->recheck_charger = false; + chg->precheck_charger_type = POWER_SUPPLY_TYPE_UNKNOWN; + vote(chg->awake_votable, CHG_AWAKE_VOTER, false, 0); smblib_update_usb_type(chg); } @@ -5275,9 +6439,10 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) !vbus_rising, 0); power_supply_changed(chg->usb_psy); + if (chg->dual_role) dual_role_instance_changed(chg->dual_role); - smblib_dbg(chg, PR_INTERRUPT, "IRQ: usbin-plugin %s\n", + smblib_dbg(chg, PR_OEM, "IRQ: usbin-plugin %s\n", vbus_rising ? "attached" : "detached"); } @@ -5308,6 +6473,135 @@ static void smblib_handle_sdp_enumeration_done(struct smb_charger *chg, rising ? "rising" : "falling"); } +static void smblib_raise_qc3_vbus_work(struct work_struct *work) +{ + union power_supply_propval val = {0, }; + int i, usb_present = 0, vbus_now = 0; + int vol_qc_ab_thr = 0; + int rc; + struct smb_charger *chg = container_of(work, struct smb_charger, + raise_qc3_vbus_work.work); + + rc = smblib_get_prop_usb_present(chg, &val); + if (rc < 0) { + smblib_err(chg, "Couldn't get usb present rc = %d\n", rc); + return; + } + + usb_present = val.intval; + if (usb_present) { + chg->raise_vbus_to_detect = true; + for (i = 0; i < MAX_PULSE; i++) { + rc = smblib_dp_pulse(chg); + msleep(40); + } + msleep(200); + rc = smblib_get_prop_usb_present(chg, &val); + if (rc < 0) { + smblib_err(chg, "Couldn't get usb present rc = %d\n", rc); + return; + } + + usb_present = val.intval; + pr_info("usb_present is %d\n", usb_present); + if (!usb_present) { + chg->raise_vbus_to_detect = false; + rc = smblib_force_vbus_voltage(chg, FORCE_5V_BIT); + if (rc < 0) + pr_err("Failed to force 5V\n"); + return; + } + + rc = smblib_get_usb_in_voltage_now(chg, &val); + if (rc < 0) + pr_err("Couldn't get usb voltage rc=%d\n", rc); + vbus_now = val.intval; + pr_info("vbus_now is %d\n", vbus_now); + + if (chg->snk_debug_acc_detected && usb_present) + vol_qc_ab_thr = VOL_THR_FOR_QC_CLASS_AB + + COMP_FOR_LOW_RESISTANCE_CABLE; + else + vol_qc_ab_thr = VOL_THR_FOR_QC_CLASS_AB; + if (vbus_now <= vol_qc_ab_thr) { + chg->is_qc_class_a = true; + vote(chg->fcc_votable, + CLASSA_QC_FCC_VOTER, true, QC_CLASS_A_CURRENT_UA); + } else { + chg->is_qc_class_b = true; + if (chg->usb_psy) + power_supply_changed(chg->usb_psy); + } + rc = smblib_force_vbus_voltage(chg, FORCE_5V_BIT); + if (rc < 0) + pr_err("Failed to force 5V\n"); + if (chg->is_qc_class_a) + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + HVDCP_CLASS_A_MAX_UA); + else + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + HVDCP_CURRENT_UA); + /* select charge pump as second charger */ + rc = smblib_select_sec_charger(chg, POWER_SUPPLY_CHARGER_SEC_CP, + POWER_SUPPLY_CP_HVDCP3, false); + if (rc < 0) + dev_err(chg->dev, + "Couldn't enable secondary chargers rc=%d\n", rc); +#ifdef CONFIG_THERMAL + if (chg->cp_reason == POWER_SUPPLY_CP_HVDCP3) + smblib_therm_charging(chg); +#endif + chg->raise_vbus_to_detect = false; + } +} + +struct quick_charge adapter_cap[10] = { + { POWER_SUPPLY_TYPE_USB, QUICK_CHARGE_NORMAL }, + { POWER_SUPPLY_TYPE_USB_DCP, QUICK_CHARGE_NORMAL }, + { POWER_SUPPLY_TYPE_USB_CDP, QUICK_CHARGE_NORMAL }, + { POWER_SUPPLY_TYPE_USB_ACA, QUICK_CHARGE_NORMAL }, + { POWER_SUPPLY_TYPE_USB_FLOAT, QUICK_CHARGE_NORMAL }, + { POWER_SUPPLY_TYPE_USB_PD, QUICK_CHARGE_FAST }, + { POWER_SUPPLY_TYPE_USB_HVDCP, QUICK_CHARGE_FAST }, + { POWER_SUPPLY_TYPE_USB_HVDCP_3, QUICK_CHARGE_FAST }, + { POWER_SUPPLY_TYPE_WIRELESS, QUICK_CHARGE_FAST }, + {0, 0}, +}; + +int smblib_get_quick_charge_type(struct smb_charger *chg) +{ + int i = 0, rc; + union power_supply_propval pval = {0, }; + + if (!chg) { + dev_err(chg->dev, "get quick charge type faied\n"); + return -EINVAL; + } + + rc = smblib_get_prop_batt_status(chg, &pval); + if (rc < 0) + return -EINVAL; + + if (pval.intval == POWER_SUPPLY_STATUS_DISCHARGING) + return 0; + + if ((chg->real_charger_type == POWER_SUPPLY_TYPE_USB_PD) && chg->pd_verifed) { + return QUICK_CHARGE_FLASH; + } + + if (chg->is_qc_class_b) + return QUICK_CHARGE_FLASH; + + while (adapter_cap[i].adap_type != 0) { + if (chg->real_charger_type == adapter_cap[i].adap_type) { + return adapter_cap[i].adap_cap; + } + i++; + } + + return 0; +} + #define APSD_EXTENDED_TIMEOUT_MS 400 /* triggers when HVDCP 3.0 authentication has finished */ static void smblib_handle_hvdcp_3p0_auth_done(struct smb_charger *chg, @@ -5327,28 +6621,41 @@ static void smblib_handle_hvdcp_3p0_auth_done(struct smb_charger *chg, if (apsd_result->bit & QC_3P0_BIT) { /* for QC3, switch to CP if present */ - if (chg->sec_cp_present) { - rc = smblib_select_sec_charger(chg, - POWER_SUPPLY_CHARGER_SEC_CP, - POWER_SUPPLY_CP_HVDCP3, false); - if (rc < 0) - dev_err(chg->dev, - "Couldn't enable secondary chargers rc=%d\n", - rc); + if ((chg->sec_cp_present) && (!chg->qc_class_ab)) { + rc = smblib_select_sec_charger(chg, + POWER_SUPPLY_CHARGER_SEC_CP, POWER_SUPPLY_CP_HVDCP3, false); + if (rc < 0) + dev_err(chg->dev, + "Couldn't enable secondary chargers rc=%d\n", rc); + } else { + if (!chg->detect_low_power_qc3_charger) { + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + HVDCP_START_CURRENT_UA); + schedule_delayed_work(&chg->raise_qc3_vbus_work, 0); + chg->detect_low_power_qc3_charger = true; + } } + } else if (apsd_result->bit & QC_2P0_BIT + && (!chg->qc2_unsupported)) { + pr_info("force 9V for QC2 charger\n"); + rc = smblib_force_vbus_voltage(chg, FORCE_9V_BIT); + if (rc < 0) + pr_err("Failed to force 9V\n"); + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + HVDCP2_CURRENT_UA); + } + /* QC3.5 detection timeout */ if (!chg->apsd_ext_timeout && !timer_pending(&chg->apsd_timer)) { smblib_dbg(chg, PR_MISC, - "APSD Extented timer started at %lld\n", + "APSD Extented timer started at %d\n", jiffies_to_msecs(jiffies)); - mod_timer(&chg->apsd_timer, msecs_to_jiffies(APSD_EXTENDED_TIMEOUT_MS) + jiffies); } - } smblib_dbg(chg, PR_INTERRUPT, "IRQ: hvdcp-3p0-auth-done rising; %s detected\n", apsd_result->name); @@ -5365,8 +6672,17 @@ static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg, CHARGER_TYPE_VOTER, false, 0); vote(chg->hdc_irq_disable_votable, CHARGER_TYPE_VOTER, false, 0); - vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, - HVDCP_CURRENT_UA); + if (!chg->raise_vbus_to_detect) { + if (chg->is_qc_class_a) + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + HVDCP_CLASS_A_MAX_UA); + else if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP) + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + HVDCP2_CURRENT_UA); + else + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + HVDCP_CURRENT_UA); + } } else { /* A plain DCP, enforce DCP ICL if specified */ vote(chg->usb_icl_votable, DCP_VOTER, @@ -5427,25 +6743,38 @@ static void update_sw_icl_max(struct smb_charger *chg, int pst) /* if flash is active force 500mA */ vote(chg->usb_icl_votable, USB_PSY_VOTER, true, is_flash_active(chg) ? - SDP_CURRENT_UA : SDP_100_MA); + SDP_CURRENT_UA : USBIN_500MA); } vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0); break; case POWER_SUPPLY_TYPE_USB_CDP: + if (is_client_vote_enabled(chg->usb_icl_votable, + USB_PSY_VOTER)) + vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, CDP_CURRENT_UA); break; case POWER_SUPPLY_TYPE_USB_DCP: + if (is_client_vote_enabled(chg->usb_icl_votable, + USB_PSY_VOTER)) + vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); rp_ua = get_rp_based_dcp_current(chg, typec_mode); vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, rp_ua); break; case POWER_SUPPLY_TYPE_USB_FLOAT: + if (is_client_vote_enabled(chg->usb_icl_votable, + USB_PSY_VOTER)) + vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); /* * limit ICL to 100mA, the USB driver will enumerate to check * if this is a SDP and appropriately set the current */ - vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, - SDP_100_MA); + if (!chg->recheck_charger) + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + SDP_100_MA); + else + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + FLOAT_CHARGER_UA); break; case POWER_SUPPLY_TYPE_UNKNOWN: default: @@ -5455,6 +6784,20 @@ static void update_sw_icl_max(struct smb_charger *chg, int pst) } } +#ifdef CONFIG_THERMAL +static void determine_thermal_current(struct smb_charger *chg) +{ + if (chg->system_temp_level > 0 + && chg->system_temp_level < (chg->thermal_levels - 1)) { + /* + * consider thermal limit only when it is active and not at + * the highest level + */ + smblib_therm_charging(chg); + } +} +#endif + static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) { const struct apsd_result *apsd_result; @@ -5472,15 +6815,31 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) case FLOAT_CHARGER_BIT: if (chg->use_extcon) smblib_notify_device_mode(chg, true); + if (chg->typec_mode == POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER) + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, 500000); break; case OCP_CHARGER_BIT: case DCP_CHARGER_BIT: + if (chg->typec_mode == POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER){ + chg->real_charger_type = POWER_SUPPLY_TYPE_USB_FLOAT; + chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_FLOAT; + if ((strcmp(get_effective_client(chg->usb_icl_votable), "OTG_VOTER") == 0) && + (get_effective_result(chg->usb_icl_votable) == 0)) + vote(chg->usb_icl_votable, OTG_VOTER, false, 0); + + if (get_client_vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER) != 500000) + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, 500000); + } break; default: break; } - smblib_dbg(chg, PR_INTERRUPT, "IRQ: apsd-done rising; %s detected\n", +#ifdef CONFIG_THERMAL + determine_thermal_current(chg); +#endif + + smblib_dbg(chg, PR_OEM, "IRQ: apsd-done rising; %s detected\n", apsd_result->name); } @@ -5490,6 +6849,8 @@ irqreturn_t usb_source_change_irq_handler(int irq, void *data) struct smb_charger *chg = irq_data->parent_data; int rc = 0; u8 stat; + if (chg->fake_usb_insertion) + return IRQ_HANDLED; /* PD session is ongoing, ignore BC1.2 and QC detection */ if (chg->pd_active) @@ -5500,7 +6861,7 @@ irqreturn_t usb_source_change_irq_handler(int irq, void *data) smblib_err(chg, "Couldn't read APSD_STATUS rc=%d\n", rc); return IRQ_HANDLED; } - smblib_dbg(chg, PR_INTERRUPT, "APSD_STATUS = 0x%02x\n", stat); + smblib_dbg(chg, PR_OEM, "APSD_STATUS = 0x%02x\n", stat); if ((chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) && (stat & APSD_DTC_STATUS_DONE_BIT) @@ -5544,7 +6905,7 @@ irqreturn_t usb_source_change_irq_handler(int irq, void *data) smblib_err(chg, "Couldn't read APSD_STATUS rc=%d\n", rc); return IRQ_HANDLED; } - smblib_dbg(chg, PR_INTERRUPT, "APSD_STATUS = 0x%02x\n", stat); + smblib_dbg(chg, PR_OEM, "APSD_STATUS = 0x%02x\n", stat); return IRQ_HANDLED; } @@ -5581,10 +6942,13 @@ enum alarmtimer_restart smblib_lpd_recheck_timer(struct alarm *alarm, chg->lpd_stage = LPD_STAGE_NONE; chg->lpd_reason = LPD_NONE; + if (chg->support_liquid == true) { + schedule_work(&chg->lpd_disable_chg_work); + } + return ALARMTIMER_NORESTART; } -#define RSBU_K_300K_UV 3000000 static bool smblib_src_lpd(struct smb_charger *chg) { union power_supply_propval pval; @@ -5614,6 +6978,13 @@ static bool smblib_src_lpd(struct smb_charger *chg) break; } + chg->typec_mode = smblib_get_prop_typec_mode(chg); + if ((chg->typec_mode == POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER) + && (chg->support_liquid == true)) { + lpd_flag = false; + return lpd_flag; + } + if (lpd_flag) { chg->lpd_stage = LPD_STAGE_COMMIT; pval.intval = POWER_SUPPLY_TYPEC_PR_SINK; @@ -5622,10 +6993,22 @@ static bool smblib_src_lpd(struct smb_charger *chg) smblib_err(chg, "Couldn't write 0x%02x to TYPE_C_INTRPT_ENB_SOFTWARE_CTRL rc=%d\n", pval.intval, rc); chg->lpd_reason = LPD_MOISTURE_DETECTED; + + chg->lpd_status = true; + + if (chg->support_liquid == true) { + vote(chg->usb_icl_votable, LIQUID_DETECTION_VOTER, true, 0); + if (chg->batt_psy) + power_supply_changed(chg->batt_psy); + } + alarm_start_relative(&chg->lpd_recheck_timer, + ms_to_ktime(15000)); + chg->moisture_present = true; alarm_start_relative(&chg->lpd_recheck_timer, ms_to_ktime(60000)); power_supply_changed(chg->usb_psy); + } else { chg->lpd_reason = LPD_NONE; chg->typec_mode = smblib_get_prop_typec_mode(chg); @@ -5648,9 +7031,33 @@ static void typec_src_fault_condition_cfg(struct smb_charger *chg, bool src) static void typec_sink_insertion(struct smb_charger *chg) { - int rc; + int rc = 0; + int usb_present = 0; + int typec_mode; + union power_supply_propval pval = {0, }; + + rc = smblib_get_prop_usb_present(chg, &pval); + if (rc < 0) { + smblib_err(chg, "Couldn't get usb present rc = %d\n", rc); + return; + } + + usb_present = pval.intval; + + typec_mode = smblib_get_prop_typec_mode(chg); + if (usb_present && typec_mode == POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER) { + chg->real_charger_type = POWER_SUPPLY_TYPE_USB_FLOAT; + chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_FLOAT; + if (get_client_vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER) != 500000) + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, 500000); + vote(chg->usb_icl_votable, OTG_VOTER, false, 0); + power_supply_changed(chg->batt_psy); + } else { + vote(chg->usb_icl_votable, OTG_VOTER, true, 0); + } typec_src_fault_condition_cfg(chg, true); + rc = smblib_set_charge_param(chg, &chg->param.freq_switcher, chg->chg_freq.freq_above_otg_threshold); if (rc < 0) @@ -5704,6 +7111,7 @@ static void typec_sink_removal(struct smb_charger *chg) { int rc; + vote(chg->usb_icl_votable, OTG_VOTER, false, 0); typec_src_fault_condition_cfg(chg, false); rc = smblib_set_charge_param(chg, &chg->param.freq_switcher, chg->chg_freq.freq_removal); @@ -5734,6 +7142,8 @@ static void typec_src_removal(struct smb_charger *chg) "Couldn't disable secondary charger rc=%d\n", rc); chg->qc3p5_detected = false; + chg->snk_debug_acc_detected = false; + typec_src_fault_condition_cfg(chg, false); smblib_hvdcp_detect_enable(chg, false); smblib_update_usb_type(chg); @@ -5750,6 +7160,7 @@ static void typec_src_removal(struct smb_charger *chg) } cancel_delayed_work_sync(&chg->pl_enable_work); + cancel_delayed_work_sync(&chg->raise_qc3_vbus_work); /* reset input current limit voters */ vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, @@ -5775,6 +7186,11 @@ static void typec_src_removal(struct smb_charger *chg) vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0); vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0); vote(chg->awake_votable, PL_DELAY_VOTER, false, 0); + vote(chg->awake_votable, CHG_AWAKE_VOTER, false, 0); + vote(chg->fcc_votable, CLASSA_QC_FCC_VOTER, false, 0); + vote(chg->usb_icl_votable, QC_A_CP_ICL_MAX_VOTER, false, 0); + vote(chg->usb_icl_votable, QC2_UNSUPPORTED_VOTER, false, 0); + vote(chg->usb_icl_votable, PD_VERIFED_VOTER, false, 0); /* Remove SW thermal regulation WA votes */ vote(chg->usb_icl_votable, SW_THERM_REGULATION_VOTER, false, 0); @@ -5853,6 +7269,18 @@ static void typec_src_removal(struct smb_charger *chg) del_timer_sync(&chg->apsd_timer); chg->apsd_ext_timeout = false; + chg->detect_low_power_qc3_charger = false; + chg->raise_vbus_to_detect = false; + chg->is_qc_class_a = false; + chg->is_qc_class_b = false; + chg->high_vbus_detected = false; + chg->qc2_unsupported = false; + chg->cc_un_compliant_detected = false; + chg->recheck_charger = false; + + pr_err("%s:", __func__); + if (chg->pd_verifed) + chg->pd_verifed = false; } static void typec_mode_unattached(struct smb_charger *chg) @@ -5915,7 +7343,7 @@ irqreturn_t typec_or_rid_detection_change_irq_handler(int irq, void *data) struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; - smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); + smblib_dbg(chg, PR_OEM, "IRQ: %s\n", irq_data->name); if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) { if (chg->uusb_moisture_protection_enabled) { @@ -5972,7 +7400,7 @@ irqreturn_t typec_state_change_irq_handler(int irq, void *data) smblib_handle_rp_change(chg, typec_mode); chg->typec_mode = typec_mode; - smblib_dbg(chg, PR_INTERRUPT, "IRQ: cc-state-change; Type-C %s detected\n", + smblib_dbg(chg, PR_OEM, "IRQ: cc-state-change; Type-C %s detected\n", smblib_typec_mode_name[chg->typec_mode]); power_supply_changed(chg->usb_psy); @@ -6021,7 +7449,7 @@ irqreturn_t typec_attach_detach_irq_handler(int irq, void *data) bool attached = false; int rc; - smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); + smblib_dbg(chg, PR_OEM, "IRQ: %s\n", irq_data->name); rc = smblib_read(chg, TYPE_C_STATE_MACHINE_STATUS_REG, &stat); if (rc < 0) { @@ -6035,6 +7463,9 @@ irqreturn_t typec_attach_detach_irq_handler(int irq, void *data) if (attached) { smblib_lpd_clear_ra_open_work(chg); + if (chg->lpd_status) + chg->lpd_status = false; + rc = smblib_read(chg, TYPE_C_MISC_STATUS_REG, &stat); if (rc < 0) { smblib_err(chg, "Couldn't read TYPE_C_MISC_STATUS_REG rc=%d\n", @@ -6054,6 +7485,7 @@ irqreturn_t typec_attach_detach_irq_handler(int irq, void *data) } else { chg->sink_src_mode = SINK_MODE; typec_src_insertion(chg); + smblib_wireless_set_enable(chg, false); } if (chg->typec_role_swap_failed) { @@ -6071,6 +7503,7 @@ irqreturn_t typec_attach_detach_irq_handler(int irq, void *data) case SINK_MODE: case AUDIO_ACCESS_MODE: typec_src_removal(chg); + smblib_wireless_set_enable(chg, true); break; case UNATTACHED_MODE: default: @@ -6260,14 +7693,151 @@ irqreturn_t dcin_uv_irq_handler(int irq, void *data) return IRQ_HANDLED; } +static int smblib_get_wireless_output_vol(struct smb_charger *chg) +{ + + int rc,wireless_vout = 0; + + rc = iio_read_channel_processed(chg->iio.vph_v_chan, + &wireless_vout); + if (rc < 0) { + smblib_err(chg, "Couldn't get vph vol rc = %d\n", + rc); + return -ENODATA; + } + wireless_vout *= 2; + wireless_vout /= 100000; + wireless_vout *= 100000; + + return wireless_vout; + +} + +int smblib_set_wirless_cp_enable(struct smb_charger *chg, + const union power_supply_propval *val) +{ + union power_supply_propval pval; + int wireless_vout = 0; + int rc; + + wireless_vout = smblib_get_wireless_output_vol(chg); + if (wireless_vout < 0) { + dev_err(chg->dev, "Couldn't get vph_pwr wireless_vout=%d\n", + wireless_vout); + return -ENODATA; + } + + if (chg->flag_dc_present && val->intval) { + pval.intval = wireless_vout; + rc = smblib_set_prop_voltage_wls_output(chg, &pval); + if (rc < 0) + dev_err(chg->dev, "Couldn't set dc voltage to 2*vph rc=%d\n", + rc); + + rc = smblib_select_sec_charger(chg, POWER_SUPPLY_CHARGER_SEC_CP, + POWER_SUPPLY_CP_WIRELESS, false); + if (rc < 0) + dev_err(chg->dev, + "Couldn't enable secondary chargers rc=%d\n", rc); + smblib_dbg(chg, PR_OEM, "enable cp by wireless\n"); + } else + smblib_dbg(chg, PR_WLS, "dcin_not present or usbin_present\n"); + + return 0; +} + +int smblib_set_wirless_power_good_enable(struct smb_charger *chg, + const union power_supply_propval *val) +{ + int rc = 0; + + chg->power_good_en = val->intval; + if (chg->power_good_en) { + chg->fake_dc_on = 1; + chg->fake_dc_flag = 0; + chg->last_batt_stat = 0; + cancel_delayed_work(&chg->dc_plug_out_delay_work); + vote(chg->awake_votable, DC_UV_AWAKE_VOTER, false, 0); + + } + else { + if (!chg->fake_dc_flag) + chg->fake_dc_on = 0; + /* step1: enter dc suspend */ + rc = vote(chg->dc_suspend_votable, OTG_VOTER, + true, 0); + + /* step2: force dcin_en low */ + rc = smblib_masked_write(chg, DCIN_CMD_IL_REG, + DCIN_EN_OVERRIDE_BIT | DCIN_EN_BIT, + DCIN_EN_OVERRIDE_BIT); + if (rc < 0) { + dev_err(chg->dev, "Couldn't configure dc_en override rc=%d\n", rc); + return rc; + } + + /* step3: enable pull-down on dcin_pon and mid_chg */ + rc = smblib_masked_write(chg, DCIN_CMD_PULLDOWN_REG, + DCIN_PULLDOWN_EN_BIT | DCIN_MID_PULLDOWN_BIT, + DCIN_PULLDOWN_EN_BIT | DCIN_MID_PULLDOWN_BIT); + if (rc < 0) { + dev_err(chg->dev, "Couldn't enable dcin_pulldown rc=%d\n", rc); + return rc; + } + /* wait 10ms to pull mid_chg lower than Vsys+Vrevi */ + msleep(10); + + /* step4: disable pull-down on dcin_pon and mid_chg */ + rc = smblib_masked_write(chg, DCIN_CMD_PULLDOWN_REG, + DCIN_PULLDOWN_EN_BIT | DCIN_MID_PULLDOWN_BIT, + 0); + if (rc < 0) { + dev_err(chg->dev, "Couldn't disable dcin_pulldown rc=%d\n", rc); + return rc; + } + + /* step5: remove dcin_en low */ + rc = smblib_masked_write(chg, DCIN_CMD_IL_REG, + DCIN_EN_OVERRIDE_BIT | DCIN_EN_BIT, + 0); + if (rc < 0) { + dev_err(chg->dev, "Couldn't configure dc_en override rc=%d\n", rc); + return rc; + } + + /* step6: exit dc suspend */ + rc = vote(chg->dc_suspend_votable, OTG_VOTER, + false, 0); + } + power_supply_changed(chg->dc_psy); + power_supply_changed(chg->batt_psy); + if (chg->wls_psy) + power_supply_changed(chg->wls_psy); + + return 0; +} + +static void smblib_dc_plug_out_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + dc_plug_out_delay_work.work); + + chg->fake_dc_on = 0; /*use for delay 1.8s*/ + chg->fake_dc_flag = 0; + chg->last_batt_stat = 0; + power_supply_changed(chg->dc_psy); + vote(chg->awake_votable, DC_UV_AWAKE_VOTER, false, 0); +} + irqreturn_t dc_plugin_irq_handler(int irq, void *data) { struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; - union power_supply_propval pval; + union power_supply_propval pval = {0, }; int input_present; bool dcin_present, vbus_present; - int rc, wireless_vout = 0; + //int rc, wireless_vout = 0; + int rc; int sec_charger; rc = smblib_get_prop_vph_voltage_now(chg, &pval); @@ -6275,15 +7845,26 @@ irqreturn_t dc_plugin_irq_handler(int irq, void *data) return IRQ_HANDLED; /* 2*VPH, with a granularity of 100mV */ - wireless_vout = ((pval.intval * 2) / 100000) * 100000; + //wireless_vout = ((pval.intval * 2) / 100000) * 100000; rc = smblib_is_input_present(chg, &input_present); if (rc < 0) return IRQ_HANDLED; + chg->idtp_psy = power_supply_get_by_name("idt"); + if (!chg->idtp_psy) + dev_err(chg->dev, "Could not get idtp psy\n"); + dcin_present = input_present & INPUT_PRESENT_DC; vbus_present = input_present & INPUT_PRESENT_USB; + /* when dcin present and in otg mode, set vbus present to 0*/ + if (smblib_get_prop_dfp_mode(chg) != POWER_SUPPLY_TYPEC_NONE + && smblib_get_prop_dfp_mode(chg) + != POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER + && dcin_present) + vbus_present = 0; + if (dcin_present && !vbus_present) { cancel_work_sync(&chg->dcin_aicl_work); @@ -6298,18 +7879,13 @@ irqreturn_t dc_plugin_irq_handler(int irq, void *data) smblib_dbg(chg, (PR_WLS | PR_INTERRUPT), "reset: icl: 100 mA\n"); if (chg->sec_cp_present) { - pval.intval = wireless_vout; - rc = smblib_set_prop_voltage_wls_output(chg, &pval); - if (rc < 0) - dev_err(chg->dev, "Couldn't set dc voltage to 2*vph rc=%d\n", - rc); - - rc = smblib_select_sec_charger(chg, - POWER_SUPPLY_CHARGER_SEC_CP, - POWER_SUPPLY_CP_WIRELESS, false); - if (rc < 0) - dev_err(chg->dev, "Couldn't enable secondary chargers rc=%d\n", - rc); + chg->flag_dc_present = 1; + vote(chg->awake_votable, DC_AWAKE_VOTER, true, 0); + if (chg->idtp_psy) { + pval.intval = true; + power_supply_set_property(chg->idtp_psy, + POWER_SUPPLY_PROP_PRESENT, &pval); + } } else { /* * If no secondary charger is present, commence @@ -6324,6 +7900,27 @@ irqreturn_t dc_plugin_irq_handler(int irq, void *data) schedule_work(&chg->dcin_aicl_work); } else { + vote(chg->awake_votable, DC_AWAKE_VOTER, false, 0); + vote(chg->dc_icl_votable, DCIN_ADAPTER_VOTER, true, 100000); + chg->flag_dc_present = 0; + chg->cp_reason = POWER_SUPPLY_CP_NONE; + sec_charger = chg->sec_pl_present ? + POWER_SUPPLY_CHARGER_SEC_PL : + POWER_SUPPLY_CHARGER_SEC_NONE; + rc = smblib_select_sec_charger(chg, sec_charger, + POWER_SUPPLY_CP_NONE, false); + + if (rc < 0) + dev_err(chg->dev, + "Couldn't disable secondary charger rc=%d\n", + rc); + + if (chg->idtp_psy) { + pval.intval = false; + power_supply_set_property(chg->idtp_psy, + POWER_SUPPLY_PROP_PRESENT, &pval); + } + if (chg->cp_reason == POWER_SUPPLY_CP_WIRELESS) { sec_charger = chg->sec_pl_present ? POWER_SUPPLY_CHARGER_SEC_PL : @@ -6341,6 +7938,8 @@ irqreturn_t dc_plugin_irq_handler(int irq, void *data) } power_supply_changed(chg->dc_psy); + if (chg->wls_psy) + power_supply_changed(chg->wls_psy); smblib_dbg(chg, (PR_WLS | PR_INTERRUPT), "dcin_present= %d, usbin_present= %d, cp_reason = %d\n", dcin_present, vbus_present, chg->cp_reason); @@ -6459,7 +8058,7 @@ irqreturn_t wdog_bark_irq_handler(int irq, void *data) struct smb_charger *chg = irq_data->parent_data; int rc; - smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); + smblib_dbg(chg, PR_OEM, "IRQ: %s\n", irq_data->name); rc = smblib_write(chg, BARK_BITE_WDOG_PET_REG, BARK_BITE_WDOG_PET_BIT); if (rc < 0) @@ -7161,6 +8760,9 @@ static void jeita_update_work(struct work_struct *work) chg->jeita_soft_thlds[1] + SOFT_JEITA_HYSTERESIS_OFFSET; } + chg->jeita_configured = JEITA_CFG_COMPLETE; + return; + chg->jeita_soft_fcc[0] = chg->jeita_soft_fcc[1] = -EINVAL; chg->jeita_soft_fv[0] = chg->jeita_soft_fv[1] = -EINVAL; max_fcc_ma = max_fv_uv = -EINVAL; @@ -7247,6 +8849,13 @@ static void smblib_lpd_ra_open_work(struct work_struct *work) if (!(stat & TYPEC_WATER_DETECTION_STATUS_BIT) || (stat & TYPEC_TCCDEBOUNCE_DONE_STATUS_BIT)) { chg->lpd_stage = LPD_STAGE_NONE; + chg->lpd_status = false; + + if (chg->support_liquid == true) { + vote(chg->usb_icl_votable, LIQUID_DETECTION_VOTER, false, 0); + if (chg->batt_psy) + power_supply_changed(chg->batt_psy); + } goto out; } @@ -7275,8 +8884,16 @@ static void smblib_lpd_ra_open_work(struct work_struct *work) } chg->lpd_reason = LPD_MOISTURE_DETECTED; + + chg->lpd_status = true; + chg->moisture_present = true; + if (chg->support_liquid == true) { + vote(chg->usb_icl_votable, LIQUID_DETECTION_VOTER, true, 0); + if (chg->batt_psy) + power_supply_changed(chg->batt_psy); + } } else { /* Floating cable, disable water detection irq temporarily */ rc = smblib_masked_write(chg, TYPE_C_INTERRUPT_EN_CFG_2_REG, @@ -7299,12 +8916,22 @@ static void smblib_lpd_ra_open_work(struct work_struct *work) chg->lpd_reason = LPD_FLOATING_CABLE; } - /* recheck in 60 seconds */ - alarm_start_relative(&chg->lpd_recheck_timer, ms_to_ktime(60000)); + /* recheck in 15 seconds */ + alarm_start_relative(&chg->lpd_recheck_timer, ms_to_ktime(15000)); out: vote(chg->awake_votable, LPD_VOTER, false, 0); } +static void smblib_lpd_disable_chg_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + lpd_disable_chg_work); + + vote(chg->usb_icl_votable, LIQUID_DETECTION_VOTER, false, 0); + if (chg->batt_psy) + power_supply_changed(chg->batt_psy); +} + static void smblib_lpd_detach_work(struct work_struct *work) { struct smb_charger *chg = container_of(work, struct smb_charger, @@ -7314,6 +8941,54 @@ static void smblib_lpd_detach_work(struct work_struct *work) chg->lpd_stage = LPD_STAGE_NONE; } +static void smblib_charger_type_recheck(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + charger_type_recheck.work); + int recheck_time = TYPE_RECHECK_TIME_5S; + static int last_charger_type, check_count; + int rc; + smblib_update_usb_type(chg); + smblib_dbg(chg, PR_OEM, "typec_mode:%d,last:%d: real charger type:%d\n", + chg->typec_mode, last_charger_type, chg->real_charger_type); + + if (last_charger_type != chg->real_charger_type) + check_count--; + last_charger_type = chg->real_charger_type; + + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3 || + chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP || + chg->pd_active || (check_count >= TYPE_RECHECK_COUNT) || + ((chg->real_charger_type == POWER_SUPPLY_TYPE_USB_FLOAT) && + (chg->typec_mode == POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER))) { + smblib_dbg(chg, PR_OEM, "hvdcp detect or check_count = %d break\n", + check_count); + check_count = 0; + return; + } + + if (smblib_get_prop_dfp_mode(chg) != POWER_SUPPLY_TYPEC_NONE) + goto check_next; + + if (!chg->recheck_charger) + chg->precheck_charger_type = chg->real_charger_type; + chg->recheck_charger = true; + + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_FLOAT) { + rc = smblib_request_dpdm(chg, false); + if (rc < 0) + smblib_err(chg, "Couldn't disable DPDM rc=%d\n", rc); + + msleep(500); + } + smblib_rerun_apsd_if_required(chg); + +check_next: + check_count++; + schedule_delayed_work(&chg->charger_type_recheck, + msecs_to_jiffies(recheck_time)); +} + static char *dr_mode_text[] = { "ufp", "dfp", "none" }; @@ -7477,6 +9152,14 @@ static int smblib_create_votables(struct smb_charger *chg) return rc; } + chg->dc_icl_votable = create_votable("DC_ICL", VOTE_MIN, + smblib_dc_icl_vote_callback, + chg); + if (IS_ERR(chg->dc_icl_votable)) { + rc = PTR_ERR(chg->dc_icl_votable); + return rc; + } + chg->awake_votable = create_votable("AWAKE", VOTE_SET_ANY, smblib_awake_vote_callback, chg); @@ -7586,6 +9269,7 @@ int smblib_init(struct smb_charger *chg) INIT_WORK(&chg->pl_update_work, pl_update_work); INIT_WORK(&chg->jeita_update_work, jeita_update_work); INIT_WORK(&chg->dcin_aicl_work, dcin_aicl_work); + INIT_WORK(&chg->lpd_disable_chg_work, smblib_lpd_disable_chg_work); INIT_DELAYED_WORK(&chg->clear_hdc_work, clear_hdc_work); INIT_DELAYED_WORK(&chg->icl_change_work, smblib_icl_change_work); INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work); @@ -7593,8 +9277,14 @@ int smblib_init(struct smb_charger *chg) INIT_DELAYED_WORK(&chg->bb_removal_work, smblib_bb_removal_work); INIT_DELAYED_WORK(&chg->lpd_ra_open_work, smblib_lpd_ra_open_work); INIT_DELAYED_WORK(&chg->lpd_detach_work, smblib_lpd_detach_work); + INIT_DELAYED_WORK(&chg->raise_qc3_vbus_work, smblib_raise_qc3_vbus_work); + INIT_DELAYED_WORK(&chg->charger_type_recheck, smblib_charger_type_recheck); + INIT_DELAYED_WORK(&chg->cc_un_compliant_charge_work, + smblib_cc_un_compliant_charge_work); + INIT_DELAYED_WORK(&chg->reg_work, smblib_reg_work); INIT_DELAYED_WORK(&chg->thermal_regulation_work, smblib_thermal_regulation_work); + INIT_DELAYED_WORK(&chg->dc_plug_out_delay_work, smblib_dc_plug_out_work); INIT_DELAYED_WORK(&chg->usbov_dbc_work, smblib_usbov_dbc_work); INIT_DELAYED_WORK(&chg->role_reversal_check, smblib_dual_role_check_work); @@ -7646,6 +9336,7 @@ int smblib_init(struct smb_charger *chg) chg->sec_chg_selected = POWER_SUPPLY_CHARGER_SEC_NONE; chg->cp_reason = POWER_SUPPLY_CP_NONE; chg->thermal_status = TEMP_BELOW_RANGE; + chg->batt_temp_irq_enabled = false; chg->dr_mode = DUAL_ROLE_PROP_MODE_NONE; chg->typec_irq_en = true; @@ -7674,6 +9365,7 @@ int smblib_init(struct smb_charger *chg) } chg->bms_psy = power_supply_get_by_name("bms"); + //chg->idtp_psy = power_supply_get_by_name("idt"); if (chg->sec_pl_present) { chg->pl.psy = power_supply_get_by_name("parallel"); @@ -7752,6 +9444,10 @@ int smblib_deinit(struct smb_charger *chg) cancel_delayed_work_sync(&chg->bb_removal_work); cancel_delayed_work_sync(&chg->lpd_ra_open_work); cancel_delayed_work_sync(&chg->lpd_detach_work); + cancel_delayed_work_sync(&chg->raise_qc3_vbus_work); + cancel_delayed_work_sync(&chg->charger_type_recheck); + cancel_delayed_work_sync(&chg->cc_un_compliant_charge_work); + cancel_delayed_work_sync(&chg->reg_work); cancel_delayed_work_sync(&chg->thermal_regulation_work); cancel_delayed_work_sync(&chg->usbov_dbc_work); cancel_delayed_work_sync(&chg->role_reversal_check); @@ -7772,3 +9468,14 @@ int smblib_deinit(struct smb_charger *chg) return 0; } + +static int __init early_parse_off_charge_flag(char *p) +{ + if (p) { + if (!strcmp(p, "charger")) + off_charge_flag = true; + } + + return 0; +} +early_param("androidboot.mode", early_parse_off_charge_flag); diff --git a/drivers/power/supply/qcom/smb5-lib.h b/drivers/power/supply/qcom/smb5-lib.h index a9dbc9af5e55..d5a5889abcaf 100644 --- a/drivers/power/supply/qcom/smb5-lib.h +++ b/drivers/power/supply/qcom/smb5-lib.h @@ -1,4 +1,5 @@ /* Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + * Copyright (C) 2019 XiaoMi, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -32,6 +33,7 @@ enum print_reason { PR_PARALLEL = BIT(3), PR_OTG = BIT(4), PR_WLS = BIT(5), + PR_OEM = BIT(6), }; #define DEFAULT_VOTER "DEFAULT_VOTER" @@ -61,14 +63,25 @@ enum print_reason { #define OTG_DELAY_VOTER "OTG_DELAY_VOTER" #define USBIN_I_VOTER "USBIN_I_VOTER" #define WEAK_CHARGER_VOTER "WEAK_CHARGER_VOTER" +#define OTG_VOTER "OTG_VOTER" #define PL_FCC_LOW_VOTER "PL_FCC_LOW_VOTER" #define WBC_VOTER "WBC_VOTER" #define HW_LIMIT_VOTER "HW_LIMIT_VOTER" +#define CHG_AWAKE_VOTER "CHG_AWAKE_VOTER" +#define DCIN_ADAPTER_VOTER "DCIN_ADAPTER_VOTER" +#define DCIN_LIMIT_VOTER "DCIN_LIMIT_VOTER" #define PL_SMB_EN_VOTER "PL_SMB_EN_VOTER" #define FORCE_RECHARGE_VOTER "FORCE_RECHARGE_VOTER" #define LPD_VOTER "LPD_VOTER" +#define DC_AWAKE_VOTER "DC_AWAKE_VOTER" +#define DC_UV_AWAKE_VOTER "DC_UV_AWAKE_VOTER" +#define CLASSA_QC_FCC_VOTER "CLASSA_QC_FCC_VOTER" +#define QC_A_CP_ICL_MAX_VOTER "QC_A_CP_ICL_MAX_VOTER" +#define JEITA_VOTER "JEITA_VOTER" #define FCC_STEPPER_VOTER "FCC_STEPPER_VOTER" #define SW_THERM_REGULATION_VOTER "SW_THERM_REGULATION_VOTER" +#define LIQUID_DETECTION_VOTER "LIQUID_DETECTION_VOTER" +#define QC2_UNSUPPORTED_VOTER "QC2_UNSUPPORTED_VOTER" #define JEITA_ARB_VOTER "JEITA_ARB_VOTER" #define MOISTURE_VOTER "MOISTURE_VOTER" #define HVDCP2_ICL_VOTER "HVDCP2_ICL_VOTER" @@ -86,10 +99,48 @@ enum print_reason { #define MAIN_FCC_VOTER "MAIN_FCC_VOTER" #define DCIN_AICL_VOTER "DCIN_AICL_VOTER" #define OVERHEAT_LIMIT_VOTER "OVERHEAT_LIMIT_VOTER" +#define PD_VERIFED_VOTER "PD_VERIFED_VOTER" #define BOOST_BACK_STORM_COUNT 3 #define WEAK_CHG_STORM_COUNT 8 +#define VOL_THR_FOR_QC_CLASS_AB 12300000 +#define COMP_FOR_LOW_RESISTANCE_CABLE 100000 +#define QC_CLASS_A_CURRENT_UA 3600000 +#define HVDCP_CLASS_A_MAX_UA 2500000 +#define HVDCP_CLASS_A_FOR_CP_UA 2000000 +#define MAX_PULSE 38 +#define MAX_PLUSE_COUNT_ALLOWED 30 +#define HIGH_NUM_PULSE_THR 12 +#define PD_UNVERIFED_CURRENT 3000000 + +/* thermal micros */ +#define MAX_TEMP_LEVEL 16 +/* percent of ICL compared to base 5V for different PD voltage_min voltage */ +#define PD_6P5V_PERCENT 85 +#define PD_7P5V_PERCENT 75 +#define PD_8P5V_PERCENT 70 +#define PD_9V_PERCENT 65 +#define PD_MICRO_5V 5000000 +#define PD_MICRO_5P9V 5900000 +#define PD_MICRO_6P5V 6500000 +#define PD_MICRO_7P5V 7500000 +#define PD_MICRO_8P5V 8500000 +#define PD_MICRO_9V 9000000 +#define ICL_LIMIT_LEVEL_THR 8 + +#define QC2_UNSUPPORTED_UA 1800000 +/* defined for HVDCP2 */ +#define HVDCP2_CURRENT_UA 1500000 + +/* defined for charger type recheck */ +#define CHARGER_RECHECK_DELAY_MS 30000 +#define TYPE_RECHECK_TIME_5S 5000 +#define TYPE_RECHECK_COUNT 3 + +/* defined for un_compliant Type-C cable */ +#define CC_UN_COMPLIANT_START_DELAY_MS 700 + #define VBAT_TO_VRAW_ADC(v) div_u64((u64)v * 1000000UL, 194637UL) #define ITERM_LIMITS_PMI632_MA 5000 @@ -99,8 +150,8 @@ enum print_reason { #define SDP_100_MA 100000 #define SDP_CURRENT_UA 500000 #define CDP_CURRENT_UA 1500000 -#define DCP_CURRENT_UA 1500000 -#define HVDCP_CURRENT_UA 3000000 +#define DCP_CURRENT_UA 1600000 +#define HVDCP_CURRENT_UA 2800000 #define TYPEC_DEFAULT_CURRENT_UA 900000 #define TYPEC_MEDIUM_CURRENT_UA 1500000 #define TYPEC_HIGH_CURRENT_UA 3000000 @@ -108,6 +159,24 @@ enum print_reason { #define DCIN_ICL_MAX_UA 1500000 #define DCIN_ICL_STEP_UA 100000 +/*DCIN ICL*/ +#define PSNS_CURRENT_SAMPLE_RATE 1053 +#define PSNS_CURRENT_SAMPLE_RESIS 392 +#define PSNS_COMP_UV_FOR_HIGH_THERMAL 40000 + +/* cutoff voltage threshold */ +#define CUTOFF_VOL_THR 3400000 + +#define RSBU_K_300K_UV 3000000 + +#define RECHARGE_SOC_THR 99 + +enum hvdcp3_type { + HVDCP3_NONE = 0, + HVDCP3_CLASSA_18W, + HVDCP3_CLASSB_27W, +}; + #define ROLE_REVERSAL_DELAY_MS 2000 enum smb_mode { @@ -376,6 +445,8 @@ struct smb_iio { struct iio_channel *die_temp_chan; struct iio_channel *skin_temp_chan; struct iio_channel *smb_temp_chan; + struct iio_channel *hw_version_gpio5; + struct iio_channel *project_gpio6; }; struct smb_charger { @@ -392,6 +463,9 @@ struct smb_charger { int otg_delay_ms; int *weak_chg_icl_ua; bool pd_not_supported; + bool init_once; + bool support_liquid; + bool dynamic_fv_enabled; /* locks */ struct mutex smb_lock; @@ -408,11 +482,17 @@ struct smb_charger { struct power_supply *usb_psy; struct power_supply *dc_psy; struct power_supply *bms_psy; + struct power_supply_desc usb_psy_desc; struct power_supply *usb_main_psy; struct power_supply *usb_port_psy; struct power_supply *wls_psy; + struct power_supply *idtp_psy; + struct power_supply *wip_psy; + struct power_supply *wireless_psy; + struct power_supply *wls_chip_psy; struct power_supply *cp_psy; enum power_supply_type real_charger_type; + enum power_supply_type wireless_charger_type; /* dual role class */ struct dual_role_phy_instance *dual_role; @@ -438,6 +518,7 @@ struct smb_charger { struct votable *fcc_main_votable; struct votable *fv_votable; struct votable *usb_icl_votable; + struct votable *dc_icl_votable; struct votable *awake_votable; struct votable *pl_disable_votable; struct votable *chg_disable_votable; @@ -456,16 +537,22 @@ struct smb_charger { struct work_struct moisture_protection_work; struct work_struct chg_termination_work; struct work_struct dcin_aicl_work; + struct work_struct lpd_disable_chg_work; struct delayed_work ps_change_timeout_work; struct delayed_work clear_hdc_work; struct delayed_work icl_change_work; struct delayed_work pl_enable_work; struct delayed_work uusb_otg_work; struct delayed_work bb_removal_work; + struct delayed_work raise_qc3_vbus_work; struct delayed_work lpd_ra_open_work; struct delayed_work lpd_detach_work; + struct delayed_work charger_type_recheck; + struct delayed_work cc_un_compliant_charge_work; + struct delayed_work reg_work; struct delayed_work thermal_regulation_work; struct delayed_work usbov_dbc_work; + struct delayed_work dc_plug_out_delay_work; struct delayed_work role_reversal_check; struct delayed_work pr_swap_detach_work; struct delayed_work pr_lock_clear_work; @@ -488,10 +575,13 @@ struct smb_charger { int voltage_min_uv; int voltage_max_uv; int pd_active; + int pd_verifed; bool pd_hard_reset; bool pr_lock_in_progress; bool pr_swap_in_progress; bool early_usb_attach; + bool early_dc_attach; + bool batt_temp_irq_enabled; bool ok_to_pd; bool typec_legacy; bool typec_irq_en; @@ -502,13 +592,34 @@ struct smb_charger { int boost_threshold_ua; int system_temp_level; int thermal_levels; + int lpd_levels; + int dc_temp_level; + int dc_thermal_levels; +#ifdef CONFIG_THERMAL + int *thermal_mitigation_dcp; + int *thermal_mitigation_qc2; + int *thermal_mitigation_pd_base; + int *thermal_mitigation_icl; + int *thermal_fcc_qc3_normal; + int *thermal_fcc_qc3_cp; + int *thermal_fcc_qc3_classb_cp; + int *thermal_fcc_pps_cp; + int *thermal_mitigation_dc; + int *lpd_hwversion; + int *thermal_mitigation_epp; + int *thermal_mitigation_bpp_qc3; + int *thermal_mitigation_bpp_qc2; + int *thermal_mitigation_bpp; +#else int *thermal_mitigation; +#endif int dcp_icl_ua; int fake_capacity; int fake_batt_status; bool step_chg_enabled; bool sw_jeita_enabled; bool typec_legacy_use_rp_icl; + bool lpd_enabled; bool is_hdc; bool chg_done; int connector_type; @@ -538,6 +649,7 @@ struct smb_charger { enum lpd_stage lpd_stage; bool lpd_disabled; enum lpd_reason lpd_reason; + bool lpd_status; bool fcc_stepper_enable; int die_temp; int smb_temp; @@ -545,6 +657,7 @@ struct smb_charger { int connector_temp; int thermal_status; int main_fcc_max; + bool report_usb_absent; u32 jeita_soft_thlds[2]; u32 jeita_soft_hys_thlds[2]; int jeita_soft_fcc[2]; @@ -582,6 +695,8 @@ struct smb_charger { int qc2_max_pulses; enum qc2_non_comp_voltage qc2_unsupported_voltage; bool dbc_usbov; + bool fake_usb_insertion; + bool qc2_unsupported; /* extcon for VBUS / ID notification to USB for uUSB */ struct extcon_dev *extcon; @@ -595,6 +710,13 @@ struct smb_charger { int die_health; int connector_health; + /* raise qc3 vbus flag */ + bool qc_class_ab; + bool is_qc_class_a; + bool is_qc_class_b; + bool raise_vbus_to_detect; + bool detect_low_power_qc3_charger; + bool high_vbus_detected; /* flash */ u32 flash_derating_soc; @@ -608,6 +730,31 @@ struct smb_charger { int dcin_uv_count; ktime_t dcin_uv_last_time; int last_wls_vout; + int flag_dc_present; + int power_good_en; + int fake_dc_on; + int fake_dc_flag; + int last_batt_stat; + /* charger type recheck */ + int recheck_charger; + int precheck_charger_type; + /* workarounds */ + bool cc_un_compliant_detected; + bool snk_debug_acc_detected; + bool support_wireless; +}; + +enum quick_charge_type { + QUICK_CHARGE_NORMAL = 0, + QUICK_CHARGE_FAST, + QUICK_CHARGE_FLASH, + QUICK_CHARGE_TURBE, + QUICK_CHARGE_MAX, +}; + +struct quick_charge { + enum power_supply_type adap_type; + enum quick_charge_type adap_cap; }; int smblib_read(struct smb_charger *chg, u16 addr, u8 *val); @@ -626,6 +773,8 @@ int smblib_set_charge_param(struct smb_charger *chg, int smblib_set_usb_suspend(struct smb_charger *chg, bool suspend); int smblib_set_dc_suspend(struct smb_charger *chg, bool suspend); +int smblib_change_psns_to_curr(struct smb_charger *chg, int uv); + int smblib_mapping_soc_from_field_value(struct smb_chg_param *param, int val_u, u8 *val_raw); int smblib_mapping_cc_delta_to_field_value(struct smb_chg_param *param, @@ -647,6 +796,7 @@ int smblib_vconn_regulator_disable(struct regulator_dev *rdev); int smblib_vconn_regulator_is_enabled(struct regulator_dev *rdev); irqreturn_t default_irq_handler(int irq, void *data); +irqreturn_t dcin_uv_handler(int irq, void *data); irqreturn_t smb_en_irq_handler(int irq, void *data); irqreturn_t chg_state_change_irq_handler(int irq, void *data); irqreturn_t batt_temp_changed_irq_handler(int irq, void *data); @@ -685,6 +835,8 @@ int smblib_get_prop_system_temp_level(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_system_temp_level_max(struct smb_charger *chg, union power_supply_propval *val); +int smblib_get_prop_dc_temp_level(struct smb_charger *chg, + union power_supply_propval *val); int smblib_get_prop_input_current_limited(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_batt_iterm(struct smb_charger *chg, @@ -697,6 +849,8 @@ int smblib_set_prop_batt_status(struct smb_charger *chg, const union power_supply_propval *val); int smblib_set_prop_system_temp_level(struct smb_charger *chg, const union power_supply_propval *val); +int smblib_set_prop_dc_temp_level(struct smb_charger *chg, + const union power_supply_propval *val); int smblib_set_prop_input_current_limited(struct smb_charger *chg, const union power_supply_propval *val); @@ -716,6 +870,8 @@ int smblib_get_prop_voltage_wls_output(struct smb_charger *chg, union power_supply_propval *val); int smblib_set_prop_voltage_wls_output(struct smb_charger *chg, const union power_supply_propval *val); +int smblib_get_prop_wireless_version(struct smb_charger *chg, + union power_supply_propval *val); int smblib_set_prop_dc_reset(struct smb_charger *chg); int smblib_get_prop_usb_present(struct smb_charger *chg, union power_supply_propval *val); @@ -807,6 +963,23 @@ int smblib_configure_hvdcp_apsd(struct smb_charger *chg, bool enable); int smblib_icl_override(struct smb_charger *chg, enum icl_override_mode mode); enum alarmtimer_restart smblib_lpd_recheck_timer(struct alarm *alarm, ktime_t time); +int smblib_set_prop_wireless_wakelock(struct smb_charger *chg, + const union power_supply_propval *val); + +int smblib_set_prop_type_recheck(struct smb_charger *chg, + const union power_supply_propval *val); +int smblib_get_prop_type_recheck(struct smb_charger *chg, + union power_supply_propval *val); +int smblib_get_quick_charge_type(struct smb_charger *chg); +int smblib_set_wirless_cp_enable(struct smb_charger *chg, + const union power_supply_propval *val); +int smblib_set_wirless_power_good_enable(struct smb_charger *chg, + const union power_supply_propval *val); +int smblib_get_prop_liquid_status(struct smb_charger *chg, + union power_supply_propval *val); + +bool smblib_support_liquid_feature(struct smb_charger *chg); + int smblib_toggle_smb_en(struct smb_charger *chg, int toggle); void smblib_hvdcp_detect_enable(struct smb_charger *chg, bool enable); void smblib_hvdcp_exit_config(struct smb_charger *chg); diff --git a/drivers/power/supply/qcom/smb5-reg.h b/drivers/power/supply/qcom/smb5-reg.h index 444841d67c82..b4d2c96dc83e 100644 --- a/drivers/power/supply/qcom/smb5-reg.h +++ b/drivers/power/supply/qcom/smb5-reg.h @@ -1,4 +1,5 @@ /* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * Copyright (C) 2019 XiaoMi, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -317,6 +318,10 @@ enum { #define USBIN_5V_AICL_THRESHOLD_REG (USBIN_BASE + 0x81) #define USBIN_CONT_AICL_THRESHOLD_REG (USBIN_BASE + 0x84) + +#define TYPE_C_CFG_REG (USBIN_BASE + 0x58) +#define APSD_START_ON_CC_BIT BIT(7) + /******************************** * DCIN Peripheral Registers * ********************************/ @@ -327,6 +332,12 @@ enum { #define DCIN_CMD_IL_REG (DCIN_BASE + 0x40) #define DCIN_SUSPEND_BIT BIT(0) #define DCIN_EN_OVERRIDE_BIT BIT(1) +#define DCIN_EN_BIT BIT(2) + +#define DCIN_CMD_PULLDOWN_REG (DCIN_BASE + 0x45) +#define DCIN_PULLDOWN_EN_BIT BIT(0) +#define DCIN_MID_PULLDOWN_BIT BIT(1) + #define DCIN_EN_MASK GENMASK(2, 1) #define DCIN_CMD_PON_REG (DCIN_BASE + 0x45) @@ -349,6 +360,9 @@ enum { #define SNK_RP_3P0_BIT BIT(1) #define SNK_RP_SHORT_BIT BIT(0) +#define TYPE_C_SNK_DEBUG_ACC_STATUS_REG (TYPEC_BASE + 0x07) +#define SNK_DEBUG_ACC_RPSTD_PRSTD_BIT BIT(0) + #define TYPE_C_SRC_STATUS_REG (TYPEC_BASE + 0x08) #define DETECTED_SNK_TYPE_MASK GENMASK(4, 0) #define SRC_HIGH_BATT_BIT BIT(5) @@ -400,6 +414,10 @@ enum { #define TYPEC_CCOUT_VALUE_BIT BIT(1) #define TYPEC_CCOUT_SRC_BIT BIT(0) +#define TYPE_C_DEBUG_ACC_SNK_CFG (TYPEC_BASE + 0x4A) +#define TYPEC_DEBUG_ACC_SNK_SEL_ICL BIT(2) +#define TYPEC_DEBUG_ACC_SNK_DIS_AICL BIT(3) + #define DEBUG_ACCESS_SRC_CFG_REG (TYPEC_BASE + 0x4C) #define EN_UNORIENTED_DEBUG_ACCESS_SRC_BIT BIT(0) @@ -515,6 +533,7 @@ enum { #define WATCHDOG_TRIGGER_AFP_EN_BIT BIT(7) #define BARK_WDOG_INT_EN_BIT BIT(6) #define WDOG_TIMER_EN_ON_PLUGIN_BIT BIT(1) +#define WDOG_TIMER_EN_BIT BIT(0) #define SNARL_BARK_BITE_WD_CFG_REG (MISC_BASE + 0x53) #define BITE_WDOG_DISABLE_CHARGING_CFG_BIT BIT(7) diff --git a/drivers/power/supply/qcom/step-chg-jeita.c b/drivers/power/supply/qcom/step-chg-jeita.c index 72191eca56bb..faec5e154d30 100644 --- a/drivers/power/supply/qcom/step-chg-jeita.c +++ b/drivers/power/supply/qcom/step-chg-jeita.c @@ -1,4 +1,5 @@ /* Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * Copyright (C) 2019 XiaoMi, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -22,6 +23,7 @@ #define STEP_CHG_VOTER "STEP_CHG_VOTER" #define JEITA_VOTER "JEITA_VOTER" +#define DYNAMIC_FV_VOTER "DYNAMIC_FV_VOTER" #define is_between(left, right, value) \ (((left) >= (right) && (left) >= (value) \ @@ -44,16 +46,24 @@ struct jeita_fv_cfg { struct range_data fv_cfg[MAX_STEP_CHG_ENTRIES]; }; +struct dynamic_fv_cfg { + char *prop_name; + struct range_data fv_cfg[MAX_STEP_CHG_ENTRIES]; +}; + struct step_chg_info { struct device *dev; ktime_t step_last_update_time; ktime_t jeita_last_update_time; + ktime_t dynamic_fv_last_update_time; bool step_chg_enable; bool sw_jeita_enable; + bool dynamic_fv_enable; bool jeita_arb_en; bool config_is_read; bool step_chg_cfg_valid; bool sw_jeita_cfg_valid; + bool dynamic_fv_cfg_valid; bool soc_based_step_chg; bool ocv_based_step_chg; bool vbat_avg_based_step_chg; @@ -61,16 +71,19 @@ struct step_chg_info { bool taper_fcc; int jeita_fcc_index; int jeita_fv_index; + int dynamic_fv_index; int step_index; int get_config_retry_count; struct step_chg_cfg *step_chg_config; struct jeita_fcc_cfg *jeita_fcc_config; struct jeita_fv_cfg *jeita_fv_config; + struct dynamic_fv_cfg *dynamic_fv_config; struct votable *fcc_votable; struct votable *fv_votable; struct votable *usb_icl_votable; + struct votable *dc_suspend_votable; struct wakeup_source *step_chg_ws; struct power_supply *batt_psy; struct power_supply *bms_psy; @@ -360,6 +373,17 @@ static int get_step_chg_jeita_setting_from_profile(struct step_chg_info *chip) chip->sw_jeita_cfg_valid = false; } + chip->dynamic_fv_cfg_valid = true; + rc = read_range_data_from_node(profile_node, + "qcom,dynamic-fv-ranges", + chip->dynamic_fv_config->fv_cfg, + BATT_HOT_DECIDEGREE_MAX, max_fv_uv); + if (rc < 0) { + pr_debug("Read qcom,dynamic-fv-ranges failed from battery profile, rc=%d\n", + rc); + chip->dynamic_fv_cfg_valid = false; + } + return rc; } @@ -391,16 +415,20 @@ static void get_config_work(struct work_struct *work) chip->step_chg_config->fcc_cfg[i].high_threshold, chip->step_chg_config->fcc_cfg[i].value); for (i = 0; i < MAX_STEP_CHG_ENTRIES; i++) - pr_debug("jeita-fcc-cfg: %ddecidegree ~ %ddecidegre, %duA\n", + pr_info("jeita-fcc-cfg: %ddecidegree ~ %ddecidegre, %duA\n", chip->jeita_fcc_config->fcc_cfg[i].low_threshold, chip->jeita_fcc_config->fcc_cfg[i].high_threshold, chip->jeita_fcc_config->fcc_cfg[i].value); for (i = 0; i < MAX_STEP_CHG_ENTRIES; i++) - pr_debug("jeita-fv-cfg: %ddecidegree ~ %ddecidegre, %duV\n", + pr_info("jeita-fv-cfg: %ddecidegree ~ %ddecidegre, %duV\n", chip->jeita_fv_config->fv_cfg[i].low_threshold, chip->jeita_fv_config->fv_cfg[i].high_threshold, chip->jeita_fv_config->fv_cfg[i].value); - + for (i = 0; i < MAX_STEP_CHG_ENTRIES; i++) + pr_info("dynamic-fv-cfg: %d(count) ~ %d(count), %duV\n", + chip->dynamic_fv_config->fv_cfg[i].low_threshold, + chip->dynamic_fv_config->fv_cfg[i].high_threshold, + chip->dynamic_fv_config->fv_cfg[i].value); return; reschedule: @@ -417,6 +445,14 @@ static int get_val(struct range_data *range, int hysteresis, int current_index, *new_index = -EINVAL; + /* + * As battery temperature may be below 0, range.xxx is a unsigned int, but battery + * temperature is a signed int, so cannot compare them when battery temp is below 0, + * we treat it as 0 degree when the parameter threshold(battery temp) is below 0. + */ + if (threshold < 0) + threshold = 0; + /* * If the threshold is lesser than the minimum allowed range, * return -ENODATA. @@ -456,6 +492,14 @@ static int get_val(struct range_data *range, int hysteresis, int current_index, *val = range[*new_index].value; } + if (threshold < range[0].low_threshold) { + *new_index = 0; + *val = range[*new_index].value; + } else if (threshold > range[MAX_STEP_CHG_ENTRIES - 1].low_threshold) { + *new_index = MAX_STEP_CHG_ENTRIES - 1; + *val = range[*new_index].value; + } + /* * If we don't have a current_index return this * newfound value. There is no hysterisis from out of range @@ -614,7 +658,86 @@ update_time: return 0; } -#define JEITA_SUSPEND_HYST_UV 50000 +static int handle_dynamic_fv(struct step_chg_info *chip) +{ + union power_supply_propval pval = {0, }; + int rc = 0, fv_uv, cycle_count; + u64 elapsed_us; + int batt_vol = 0; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_DYNAMIC_FV_ENABLED, &pval); + if (rc < 0) + chip->dynamic_fv_enable = 0; + else + chip->dynamic_fv_enable = pval.intval; + + if (!chip->dynamic_fv_enable || !chip->dynamic_fv_cfg_valid) { + /*need recovery some setting*/ + if (chip->fv_votable) + vote(chip->fv_votable, DYNAMIC_FV_VOTER, false, 0); + return 0; + } + + elapsed_us = ktime_us_delta(ktime_get(), chip->dynamic_fv_last_update_time); + /* skip processing, event too early */ + if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US) + return 0; + + rc = power_supply_get_property(chip->bms_psy, + POWER_SUPPLY_PROP_CYCLE_COUNT, &pval); + if (rc < 0) { + pr_err("Couldn't read %s property rc=%d\n", + chip->dynamic_fv_config->prop_name, rc); + return rc; + } + cycle_count = pval.intval; + + rc = get_val(chip->dynamic_fv_config->fv_cfg, + 0, + chip->dynamic_fv_index, + cycle_count, + &chip->dynamic_fv_index, + &fv_uv); + if (rc < 0) { + /* remove the vote if no step-based fv is found */ + if (chip->fv_votable) + vote(chip->fv_votable, DYNAMIC_FV_VOTER, false, 0); + goto update_time; + } + + power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &pval); + batt_vol = pval.intval; + if (batt_vol >= fv_uv) { + goto update_time; + } + + chip->fv_votable = find_votable("FV"); + if (!chip->fv_votable) + goto update_time; + + vote(chip->fv_votable, DYNAMIC_FV_VOTER, true, fv_uv); + + /*set battery full voltage to FLOAT VOLTAGE - 10mV*/ + pval.intval = fv_uv - 10000; + rc = power_supply_set_property(chip->bms_psy, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, &pval); + if (rc < 0) { + pr_err("Couldn't set CONSTANT VOLTAGE property rc=%d\n", rc); + return rc; + } + + pr_debug("%s:cycle_count:%d,Batt_full:%d,fv:%d,\n", + __func__, cycle_count, pval.intval, fv_uv); + +update_time: + chip->dynamic_fv_last_update_time = ktime_get(); + return 0; +} + +/* set JEITA_SUSPEND_HYST_UV to 70mV to avoid recharge frequently when jeita warm */ +#define JEITA_SUSPEND_HYST_UV 70000 static int handle_jeita(struct step_chg_info *chip) { union power_supply_propval pval = {0, }; @@ -635,6 +758,8 @@ static int handle_jeita(struct step_chg_info *chip) vote(chip->fv_votable, JEITA_VOTER, false, 0); if (chip->usb_icl_votable) vote(chip->usb_icl_votable, JEITA_VOTER, false, 0); + if (chip->dc_suspend_votable) + vote(chip->dc_suspend_votable, JEITA_VOTER, 0, 0); return 0; } @@ -692,6 +817,12 @@ static int handle_jeita(struct step_chg_info *chip) if (!chip->usb_icl_votable) goto set_jeita_fv; + if (!chip->dc_suspend_votable) + chip->dc_suspend_votable = find_votable("DC_SUSPEND"); + + if (!chip->dc_suspend_votable) + goto set_jeita_fv; + /* * If JEITA float voltage is same as max-vfloat of battery then * skip any further VBAT specific checks. @@ -700,20 +831,27 @@ static int handle_jeita(struct step_chg_info *chip) POWER_SUPPLY_PROP_VOLTAGE_MAX, &pval); if (rc || (pval.intval == fv_uv)) { vote(chip->usb_icl_votable, JEITA_VOTER, false, 0); + vote(chip->dc_suspend_votable, JEITA_VOTER, 0, 0); goto set_jeita_fv; } + pr_info("%s = %d FCC = %duA FV = %duV\n", + chip->jeita_fcc_config->param.prop_name, pval.intval, fcc_ua, fv_uv); /* * Suspend USB input path if battery voltage is above * JEITA VFLOAT threshold. */ - if (chip->jeita_arb_en && fv_uv > 0) { + /* if (chip->jeita_arb_en && fv_uv > 0) { */ + if (fv_uv > 0) { rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &pval); - if (!rc && (pval.intval > fv_uv)) + if (!rc && (pval.intval > fv_uv)) { vote(chip->usb_icl_votable, JEITA_VOTER, true, 0); - else if (pval.intval < (fv_uv - JEITA_SUSPEND_HYST_UV)) + vote(chip->dc_suspend_votable, JEITA_VOTER, 1, 0); + } else if (pval.intval < (fv_uv - JEITA_SUSPEND_HYST_UV)) { vote(chip->usb_icl_votable, JEITA_VOTER, false, 0); + vote(chip->dc_suspend_votable, JEITA_VOTER, 0, 0); + } } set_jeita_fv: @@ -744,6 +882,7 @@ static int handle_battery_insertion(struct step_chg_info *chip) if (chip->batt_missing) { chip->step_chg_cfg_valid = false; chip->sw_jeita_cfg_valid = false; + chip->dynamic_fv_cfg_valid = false; chip->get_config_retry_count = 0; } else { /* @@ -775,6 +914,10 @@ static void status_change_work(struct work_struct *work) if (rc < 0) pr_err("Couldn't handle sw jeita rc = %d\n", rc); + rc = handle_dynamic_fv(chip); + if (rc < 0) + pr_err("Couldn't handle sw dynamic fv rc = %d\n", rc); + rc = handle_step_chg_config(chip); if (rc < 0) pr_err("Couldn't handle step rc = %d\n", rc); @@ -860,6 +1003,7 @@ int qcom_step_chg_init(struct device *dev, chip->step_index = -EINVAL; chip->jeita_fcc_index = -EINVAL; chip->jeita_fv_index = -EINVAL; + chip->dynamic_fv_index = -EINVAL; chip->step_chg_config = devm_kzalloc(dev, sizeof(struct step_chg_cfg), GFP_KERNEL); @@ -874,16 +1018,18 @@ int qcom_step_chg_init(struct device *dev, sizeof(struct jeita_fcc_cfg), GFP_KERNEL); chip->jeita_fv_config = devm_kzalloc(dev, sizeof(struct jeita_fv_cfg), GFP_KERNEL); - if (!chip->jeita_fcc_config || !chip->jeita_fv_config) + chip->dynamic_fv_config = devm_kzalloc(dev, + sizeof(struct dynamic_fv_cfg), GFP_KERNEL); + if (!chip->jeita_fcc_config || !chip->jeita_fv_config || !chip->dynamic_fv_config) return -ENOMEM; chip->jeita_fcc_config->param.psy_prop = POWER_SUPPLY_PROP_TEMP; chip->jeita_fcc_config->param.prop_name = "BATT_TEMP"; - chip->jeita_fcc_config->param.hysteresis = 10; + chip->jeita_fcc_config->param.hysteresis = 5; chip->jeita_fv_config->param.psy_prop = POWER_SUPPLY_PROP_TEMP; chip->jeita_fv_config->param.prop_name = "BATT_TEMP"; - chip->jeita_fv_config->param.hysteresis = 10; - + chip->jeita_fv_config->param.hysteresis = 5; + chip->dynamic_fv_config->prop_name = "BATT_CYCLE_COUNT"; INIT_DELAYED_WORK(&chip->status_change_work, status_change_work); INIT_DELAYED_WORK(&chip->get_config_work, get_config_work); diff --git a/drivers/power/supply/qcom/step-chg-jeita.h b/drivers/power/supply/qcom/step-chg-jeita.h index 26e74ed484e9..8032c93d5de6 100644 --- a/drivers/power/supply/qcom/step-chg-jeita.h +++ b/drivers/power/supply/qcom/step-chg-jeita.h @@ -1,4 +1,5 @@ /* Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * Copyright (C) 2019 XiaoMi, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -13,7 +14,7 @@ #ifndef __STEP_CHG_H__ #define __STEP_CHG_H__ -#define MAX_STEP_CHG_ENTRIES 8 +#define MAX_STEP_CHG_ENTRIES 5 struct step_chg_jeita_param { u32 psy_prop; diff --git a/drivers/power/supply/rx1618.c b/drivers/power/supply/rx1618.c new file mode 100644 index 000000000000..c31e0b8c4680 --- /dev/null +++ b/drivers/power/supply/rx1618.c @@ -0,0 +1,1550 @@ +/** + * @file rx1618.c + * @author + * @date Tuesday, January 9th 2018 + * + * @brief + * + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rx1618.h" + +#define RX1618_DELAY_MS 30000 +#define EPP_MODE 1 +#define BPP_MODE 0 +#define MIN_VBUCK 4000 +#define MAX_VBUCK 11000 +#define AP_REV_DATA_OK 0xaa +#define AP_SENT_DATA_OK 0x55 +#define PRIVATE_VBUCK_SET_CMD 0x80 +#define PRIVATE_ID_CMD 0x86 +#define PRIVATE_USB_TYPE_CMD 0x87 +#define PRIVATE_FAST_CHG_CMD 0x88 +#define PRIVATE_PRODUCT_TEST_CMD 0x89 +#define PRIVATE_FOD_TEST_CMD 0x8A +#define PRIVATE_TX_HW_ID_CMD 0x8b + +/* used registers define */ +#define REG_RX_SENT_CMD 0x0000 +#define REG_RX_SENT_DATA1 0x0001 +#define REG_RX_SENT_DATA2 0x0002 +#define REG_RX_SENT_DATA3 0x0003 +#define REG_RX_SENT_DATA4 0x0004 +#define REG_RX_SENT_DATA5 0x0005 +#define REG_RX_REV_CMD 0x0020 +#define REG_RX_REV_DATA1 0x0021 +#define REG_RX_REV_DATA2 0x0022 +#define REG_RX_REV_DATA3 0x0023 +#define REG_RX_REV_DATA4 0x0024 +#define REG_RX_REV_DATA5 0x0025 +#define REG_RX_VRECT 0x000a +#define REG_RX_IRECT 0x000b +#define REG_RX_VBUCK_1 0x0008 +#define REG_RX_VBUCK_2 0x0009 +#define REG_AP_RX_COMM 0x000C + +static struct rx1618_chg *g_chip; +static int g_Delta = 0; +static bool int_done_flag = false; +static u8 g_light_screen_flag = 0; +static u8 g_sar_done_flag = 0; +static u8 g_fast_chg_flag = 0; +static u8 g_id_done_flag = 0; +static u8 g_cali_done_flag = 0; +static u8 g_usb_type_flag = 0; +static u8 g_fw_id = 0; +static u8 g_hw_id_h,g_hw_id_l; +static u8 g_epp_or_bpp = BPP_MODE; + +struct rx1618_chg { + char *name; + struct i2c_client *client; + struct device *dev; + struct regmap *regmap; + int irq_gpio; + int enable_gpio; + int chip_enable; + int online; + struct delayed_work wireless_work; + struct delayed_work wireless_int_work; + struct mutex irq_complete; + struct mutex wireless_chg_lock; + struct mutex wireless_chg_int_lock; + + struct power_supply *wip_psy; + struct power_supply *dc_psy; + struct power_supply_desc wip_psy_d; + struct power_supply *wireless_psy; + int epp; + int auth; +}; + +static int rx1618_read(struct rx1618_chg *chip, u8 *val, u16 addr) +{ + unsigned int temp; + int rc; + + rc = regmap_read(chip->regmap, addr, &temp); + if (rc >= 0) { + *val = (u8)temp; + //dev_err(chip->dev, "[rx1618] [%s] [0x%04x] = [0x%x] \n", __func__, addr, *val); + } + + return rc; +} + + +static int rx1618_write(struct rx1618_chg *chip, u8 val, u16 addr) +{ + int rc = 0; + + rc = regmap_write(chip->regmap, addr, val); + if (rc >= 0) + { + //dev_err(chip->dev, "[rx1618] [%s] [0x%04x] = [0x%x] \n", __func__, addr, *val); + } + + return rc; +} + + +unsigned int rx1618_get_rx_vrect(struct rx1618_chg *chip) +{ + u8 data=0; + u16 vrect=0; + + //if(!chip->online) + // return -EINVAL; + + rx1618_read(chip, &data, REG_RX_VRECT); + vrect = (22500*data) >> 8; + + dev_err(chip->dev, "[rx1618] [%s] data = 0x%x, vrect=%d mV\n",__func__,data, vrect); + + return vrect; +} + +unsigned int rx1618_get_rx_irect(struct rx1618_chg *chip) +{ + u8 data=0; + u16 irect=0; + + //if(!chip->online) + // return -EINVAL; + + rx1618_read(chip, &data, REG_RX_IRECT); + irect = (2500*data) >> 8; + + dev_err(chip->dev, "[rx1618] [%s] data = 0x%x, irect=%d mA \n",__func__,data,irect); + + return irect; +} + + +unsigned int rx1618_get_rx_vbuck(struct rx1618_chg *chip) +{ + u16 vbuck = 0; + u8 data1 = 0; + u8 data2 = 0; + u16 data = 0; + + // if(!chip->online) + // return -EINVAL; + + rx1618_read(chip, &data1, REG_RX_VBUCK_1); + rx1618_read(chip, &data2, REG_RX_VBUCK_2); + + data=(data1<<2)+(data2 & 0x03); + vbuck = (2500*590/120*data) >> 10; + + dev_err(chip->dev, "[rx1618] [%s] Vbuck: data=0x%x, vbuck=%d mV ,data1 = 0x%x, data2= 0x%x \n",__func__, data, vbuck, data1, data2); + + return vbuck; +} + +unsigned int rx1618_get_rx_ibuck(struct rx1618_chg *chip) +{ + u16 ibuck=0; + + //if(!chip->online) + // return -EINVAL; + + ibuck = (rx1618_get_rx_vrect(chip) * rx1618_get_rx_irect(chip) * 95) / (rx1618_get_rx_vbuck(chip)*100); + + dev_err(chip->dev, "[rx1618] [%s] Ibuck = %d mA\n",__func__,ibuck); + + return ibuck; +} + + +bool rx1618_is_vbuck_on(struct rx1618_chg *chip) +{ + bool vbuck_status=false; + unsigned int voltage = 0; + + voltage = rx1618_get_rx_vbuck(chip); + + dev_err(chip->dev, "[rx1618] [%s] Vbuck = %d \n",__func__, voltage); + + if((voltage > MIN_VBUCK) && (voltage < MAX_VBUCK))//4V~11V + { + vbuck_status=true; + } + else + { + vbuck_status=false; + } + + return vbuck_status; +} + + +int rx1618_set_vbuck(struct rx1618_chg *chip, int volt) +{ + u8 data = 0; + + if((volt < 5166) && (volt > 9609)) //V bus_set =5166000uV+21224uV*[Vbus_set(0:7)] DEC + { + data = 0; + } + else + { + data = (volt-5166)*1000/21224; + } + + data = data + g_Delta; + dev_err(chip->dev, "[rx1618] [%s] data = 0x%x, g_Delta=%d \n",__func__, data, g_Delta); + + rx1618_write(chip, PRIVATE_VBUCK_SET_CMD, REG_RX_SENT_CMD); //sent set vbuck cmd + rx1618_write(chip, data, REG_RX_SENT_DATA1); //sent data0 + + rx1618_write(chip, AP_SENT_DATA_OK, REG_AP_RX_COMM); + + return 0; +} + + +bool rx1618_check_firmware(struct rx1618_chg *chip) +{ + u8 *read_buffer=NULL; + int i=0; + + read_buffer = (u8*)vmalloc(20000); + + rx1618_write(chip, 0xB2, 0x0017); + rx1618_write(chip, 0xB3, 0x0017); + + dev_err(chip->dev, "[rx1618] [%s] enter \n",__func__); + + for(i=0;i<(sizeof(fw_data)/4);i++) + { + rx1618_write(chip, ((0+i*4)/256), 0x0010);//address_H + rx1618_write(chip, ((0+i*4)%256), 0x0011);//address_L + + rx1618_read(chip, (read_buffer+4*i+0), 0x0013); + rx1618_read(chip, (read_buffer+4*i+1), 0x0014); + rx1618_read(chip, (read_buffer+4*i+2), 0x0015); + rx1618_read(chip, (read_buffer+4*i+3), 0x0016); + } + + rx1618_write(chip, 0xB2, 0x0017); + rx1618_write(chip, 0x00, 0x0017); + rx1618_write(chip, 0x55, 0x2017); + + for(i=0;idev, "[rx1618] [%s] fw download Fail! read_buffer[i]=0x%x, ~fw_data[i]=0x%x, i=%d, sizeof(fw_data)=%ld\n",__func__,read_buffer[i], (~fw_data[i])&0xff,i,sizeof(fw_data)); + + vfree(read_buffer); + return false; + } + } + + dev_err(chip->dev, "[rx1618] [%s] i=%d, sizeof(fw_data)=%ld\n",__func__,i,sizeof(fw_data)); + + if(i == sizeof(fw_data)) + { + dev_err(chip->dev, "[rx1618] [%s] fw download Success! \n",__func__); + vfree(read_buffer); + return true; + } + else + { + vfree(read_buffer); + return false; + } + +} + +bool rx1618_download_firmware(struct rx1618_chg *chip) +{ + bool check_status=false; + int i=0; + + cancel_delayed_work_sync(&chip->wireless_work); + cancel_delayed_work_sync(&chip->wireless_int_work); + + rx1618_write(chip, 0x55, 0x2017); + rx1618_write(chip, 0x69, 0x2017); + rx1618_write(chip, 0x96, 0x2017); + rx1618_write(chip, 0x66, 0x2017); + rx1618_write(chip, 0x99, 0x2017); + rx1618_write(chip, 0x00, 0x2018); + rx1618_write(chip, 0x00, 0x2019); + rx1618_write(chip, 0x5A, 0x0001); + rx1618_write(chip, 0x3C, 0x0002); + rx1618_write(chip, 0xA5, 0x0003); + rx1618_write(chip, 0xC3, 0x0004); + rx1618_write(chip, 0x60, 0x1000); + rx1618_write(chip, 0x9C, 0x0017); + rx1618_write(chip, 0x9D, 0x0017); + rx1618_write(chip, 0x00, 0x0010); + rx1618_write(chip, 0x00, 0x0011); + rx1618_write(chip, 0x01, 0x0019); + mdelay(180); + rx1618_write(chip, 0x00, 0x0019); + rx1618_write(chip, 0x9C, 0x0017); + rx1618_write(chip, 0xA2, 0x0017); + rx1618_write(chip, 0xA3, 0x0017); + rx1618_write(chip, 0x01, 0x0019); + mdelay(20); + rx1618_write(chip, 0x00, 0x0019); + rx1618_write(chip, 0xA2, 0x0017); + rx1618_write(chip, 0x80, 0x0017); + mdelay(20); + rx1618_write(chip, 0x5A, 0x0001); + rx1618_write(chip, 0x3C, 0x0002); + rx1618_write(chip, 0xA5, 0x0003); + rx1618_write(chip, 0xC3, 0x0004); + rx1618_write(chip, 0x60, 0x1000); + rx1618_write(chip, 0x9C, 0x0017); + rx1618_write(chip, 0x9D, 0x0017); + rx1618_write(chip, 0x00, 0x0010); + rx1618_write(chip, 0x00, 0x0011); + rx1618_write(chip, 0x01, 0x0019); + mdelay(180); + rx1618_write(chip, 0x00, 0x0019); + rx1618_write(chip, 0x9C, 0x0017); + rx1618_write(chip, 0xA2, 0x0017); + rx1618_write(chip, 0xA3, 0x0017); + rx1618_write(chip, 0x01, 0x0019); + mdelay(20); + rx1618_write(chip, 0x00, 0x0019); + rx1618_write(chip, 0xA2, 0x0017); + rx1618_write(chip, 0x80, 0x0017); + rx1618_write(chip, 0x9C, 0x0017); + rx1618_write(chip, 0x9D, 0x0017); + rx1618_write(chip, 0x20, 0x0010); + rx1618_write(chip, 0x00, 0x0011); + rx1618_write(chip, 0x01, 0x0019); + rx1618_write(chip, 0x00, 0x0019); + rx1618_write(chip, 0x9C, 0x0017); + rx1618_write(chip, 0xA2, 0x0017); + rx1618_write(chip, 0xA3, 0x0017); + rx1618_write(chip, 0x01, 0x0019); + mdelay(20); + rx1618_write(chip, 0x00, 0x0019); + rx1618_write(chip, 0xA2, 0x0017); + rx1618_write(chip, 0x80, 0x0017); + rx1618_write(chip, 0x00, 0x0010); + rx1618_write(chip, 0x00, 0x0011); + + rx1618_write(chip, (~fw_data[0]), 0x0012); + rx1618_write(chip, 0x92, 0x0017); + rx1618_write(chip, 0x93, 0x0017); + rx1618_write(chip, 0x69, 0x001A); + + for(i=1;i<(sizeof(fw_data));i++) + { + rx1618_write(chip, (~fw_data[i]), 0x0012); + udelay(20); + } + + dev_err(chip->dev, "[rx1618] [%s] fw download Success! Firmware total data: %d bytes \n",__func__,i); + + rx1618_write(chip, 0x92, 0x0017); + rx1618_write(chip, 0x80, 0x0017); + rx1618_write(chip, 0x77, 0x001A); + + mdelay(500); + check_status = rx1618_check_firmware(chip); + + if(check_status) + { + dev_err(chip->dev, "[rx1618] [%s] RX1618 download firmware and check firmware Success! \n",__func__); + return true; + } + else + { + dev_err(chip->dev, "[rx1618] [%s] RX1618 download firmware Fail, Please try again !!! \n",__func__); + return false; + } +} + +static void determine_initial_status(struct rx1618_chg *chip) +{ + bool vbuck_on = false; + //union power_supply_propval prop = {0, }; + + vbuck_on = rx1618_is_vbuck_on(chip); + if(vbuck_on && !chip->online) { + chip->online = 1; + //prop.intval = vbuck_on; + //power_supply_set_property(chip->batt_psy, POWER_SUPPLY_PROP_WIRELESS_ONLINE, &prop); + } + + dev_err(chip->dev, "[rx1618] [%s] initial vbuck_on = %d, online = %d\n",__func__,vbuck_on,chip->online); +} + + +int rx1618_chip_init(struct rx1618_chg *chip) +{ + return 0; +} + + +void rx1618_dump_reg(void) +{ + u8 data[32] = {0}; + + rx1618_read(g_chip, &data[0], 0x0000); + rx1618_read(g_chip, &data[1], 0x0001); + rx1618_read(g_chip, &data[2], 0x0002); + rx1618_read(g_chip, &data[3], 0x0003); + rx1618_read(g_chip, &data[4], 0x0004); + rx1618_read(g_chip, &data[5], 0x0005); + rx1618_read(g_chip, &data[6], 0x0008); + rx1618_read(g_chip, &data[7], 0x0009); + rx1618_read(g_chip, &data[8], 0x000A); + rx1618_read(g_chip, &data[9], 0x000B); + rx1618_read(g_chip, &data[10], 0x000C); + rx1618_read(g_chip, &data[11], 0x0020); + rx1618_read(g_chip, &data[12], 0x0021); + rx1618_read(g_chip, &data[13], 0x0022); + rx1618_read(g_chip, &data[14], 0x0023); + rx1618_read(g_chip, &data[15], 0x0024); + + dev_err(g_chip->dev, "[rx1618] [%s] REG:0x0000=0x%x\n",__func__,data[0]); + dev_err(g_chip->dev, "[rx1618] [%s] REG:0x0001=0x%x\n",__func__,data[1]); + dev_err(g_chip->dev, "[rx1618] [%s] REG:0x0002=0x%x\n",__func__,data[2]); + dev_err(g_chip->dev, "[rx1618] [%s] REG:0x0003=0x%x\n",__func__,data[3]); + dev_err(g_chip->dev, "[rx1618] [%s] REG:0x0004=0x%x\n",__func__,data[4]); + dev_err(g_chip->dev, "[rx1618] [%s] REG:0x0005=0x%x\n",__func__,data[5]); + dev_err(g_chip->dev, "[rx1618] [%s] REG:0x0008=0x%x\n",__func__,data[6]); + dev_err(g_chip->dev, "[rx1618] [%s] REG:0x0009=0x%x\n",__func__,data[7]); + dev_err(g_chip->dev, "[rx1618] [%s] REG:0x000A=0x%x\n",__func__,data[8]); + dev_err(g_chip->dev, "[rx1618] [%s] REG:0x000B=0x%x\n",__func__,data[9]); + dev_err(g_chip->dev, "[rx1618] [%s] REG:0x000C=0x%x\n",__func__,data[10]); + dev_err(g_chip->dev, "[rx1618] [%s] REG:0x0020=0x%x\n",__func__,data[11]); + dev_err(g_chip->dev, "[rx1618] [%s] REG:0x0021=0x%x\n",__func__,data[12]); + dev_err(g_chip->dev, "[rx1618] [%s] REG:0x0022=0x%x\n",__func__,data[13]); + dev_err(g_chip->dev, "[rx1618] [%s] REG:0x0023=0x%x\n",__func__,data[14]); + dev_err(g_chip->dev, "[rx1618] [%s] REG:0x0024=0x%x\n",__func__,data[15]); +} + +#define N 2 +//0x000b[0:3] 0000:no charger, 0001:SDP, 0010:CDP, 0011:DCP, 0101:QC2-other, +//0110:QC3-other, 0111:PD, 1000:fail charger, 1001:QC3-27W, 1010:PD-27W +#define EPP_MODE_CURRENT 300000*N +#define DC_OTHER_CURRENT 400000*N +#define DC_LOW_CURRENT 100000*N //200mA +#define DC_SDP_CURRENT 400000*N +#define DC_DCP_CURRENT 400000*N +#define DC_CDP_CURRENT 400000*N +#define DC_QC2_CURRENT 500000*N +#define DC_QC3_CURRENT 500000*N +#define DC_QC3_20W_CURRENT 1000000*N //2A +#define DC_PD_CURRENT 500000*N +#define DC_PD_20W_CURRENT 1000000*N //2A +void rx1618_set_pmi_icl(struct rx1618_chg *chip, int mA) +{ + union power_supply_propval val = {0, }; + + if (!chip->dc_psy) { + chip->dc_psy = power_supply_get_by_name("dc"); + if (!chip->dc_psy) { + dev_err(chip->dev, "[rx1618] [%s] no dc_psy,return\n",__func__); + return; + } + } + val.intval = mA; + power_supply_set_property(chip->dc_psy, POWER_SUPPLY_PROP_CURRENT_MAX, &val); + dev_err(chip->dev, "[rx1618] [%s] [rx1618] set icl: %d\n",__func__,val.intval); +} + + +static void rx1618_wireless_work(struct work_struct *work) +{ + struct rx1618_chg *chip = container_of(work, struct rx1618_chg, wireless_work.work); + + mutex_lock(&chip->wireless_chg_lock); + schedule_delayed_work(&chip->wireless_work, msecs_to_jiffies(RX1618_DELAY_MS)); + mutex_unlock(&chip->wireless_chg_lock); + + dev_err(chip->dev, "[rx1618] [%s] enter \n",__func__); + + return; +} + + +static void rx1618_wireless_int_work(struct work_struct *work) +{ + int i = 0; + int uA = 0; + u16 irect = 0; + u16 vrect = 0; + u8 data1,data2,data3,data4,privite_cmd,tx_cmd,usb_type; + u8 data_h,data_l,header,header_length; + union power_supply_propval val = {0, }; + + struct rx1618_chg *chip = container_of(work, struct rx1618_chg, wireless_int_work.work); + + chip->wireless_psy = power_supply_get_by_name("wireless"); + if (!chip->wireless_psy) { + dev_err(chip->dev, "[rx1618] no wireless_psy,return\n"); + } + + mutex_lock(&chip->wireless_chg_int_lock); + dev_err(chip->dev, "[rx1618] [%s] enter \n",__func__); + + //read data + rx1618_read(chip, &privite_cmd, REG_RX_REV_CMD); + rx1618_read(chip, &data1, REG_RX_REV_DATA1); + rx1618_read(chip, &data2, REG_RX_REV_DATA2); + rx1618_read(chip, &data3, REG_RX_REV_DATA3); + rx1618_read(chip, &data4, REG_RX_REV_DATA4); + + dev_err(chip->dev, "[rx1618] [%s] privite_cmd,data=0x%x,0x%x,0x%x,0x%x,0x%x\n",__func__,privite_cmd, data1,data2,data3,data4); + + switch (privite_cmd) { + + case 0x01: //Vrect10 + rx1618_write(chip, AP_REV_DATA_OK, REG_AP_RX_COMM); + msleep(10); + break; + case 0x02: //Duty L + rx1618_write(chip, AP_REV_DATA_OK, REG_AP_RX_COMM); + msleep(10); + break; + case 0x03: //Duty H + rx1618_write(chip, AP_REV_DATA_OK, REG_AP_RX_COMM); + msleep(10); + break; + case 0x04: //OVPH + rx1618_write(chip, AP_REV_DATA_OK, REG_AP_RX_COMM); + msleep(10); + break; + case 0x05: //OCPH + rx1618_write(chip, AP_REV_DATA_OK, REG_AP_RX_COMM); + msleep(10); + break; + case 0x06: //Plimit + rx1618_write(chip, AP_REV_DATA_OK, REG_AP_RX_COMM); + msleep(10); + break; + case 0x07: //FCM20 + rx1618_write(chip, AP_REV_DATA_OK, REG_AP_RX_COMM); + msleep(10); + break; + + case 0x08: //calibration + if(g_cali_done_flag == 0) + { + rx1618_write(chip, AP_REV_DATA_OK, REG_AP_RX_COMM); + chip->epp = 1; + g_cali_done_flag = 1; + dev_err(chip->dev, "[rx1618] [%s] Calibration OK! \n",__func__); + + for(i = 0; i <= 2; i++) { + uA = (EPP_MODE_CURRENT + N*50000*i); + rx1618_set_pmi_icl(chip, uA); + msleep(100); + } + } + break; + + case 0x09: //ID ok + if(g_id_done_flag == 0) + { + rx1618_write(chip, AP_REV_DATA_OK, REG_AP_RX_COMM); //receive ok + msleep(10); + + rx1618_write(chip, PRIVATE_ID_CMD, REG_RX_SENT_CMD); //sent sar + rx1618_write(chip, AP_SENT_DATA_OK, REG_AP_RX_COMM); + g_id_done_flag = 1; + dev_err(chip->dev, "[rx1618] [%s] ID OK! \n",__func__); + } + break; + + case 0x0A: //SAR ok + if(g_sar_done_flag == 0) + { + rx1618_write(chip, AP_REV_DATA_OK, REG_AP_RX_COMM); //receive ok + msleep(10); + + rx1618_write(chip, PRIVATE_USB_TYPE_CMD, REG_RX_SENT_CMD); //sent usb type + rx1618_write(chip, AP_SENT_DATA_OK, REG_AP_RX_COMM); + g_sar_done_flag = 1; + dev_err(chip->dev, "[rx1618] [%s] SAR OK! \n",__func__); + chip->auth = 1; + } + break; + + case 0x12: //fast charge ok + if(g_fast_chg_flag == 0) + { + rx1618_write(chip, AP_REV_DATA_OK, REG_AP_RX_COMM); //receive ok + msleep(10); + + g_fast_chg_flag = 1; + dev_err(chip->dev, "[rx1618] [%s] Fast charge ok! \n",__func__); + + rx1618_write(chip, 0x18, REG_RX_SENT_DATA1); + rx1618_write(chip, 0x4c, REG_RX_SENT_DATA2); + rx1618_write(chip, PRIVATE_TX_HW_ID_CMD, REG_RX_SENT_CMD); //sent tx hw id req + rx1618_write(chip, AP_SENT_DATA_OK, REG_AP_RX_COMM); + dev_err(chip->dev, "[rx1618] [%s] sent tx hw id req\n",__func__); + } + + + break; + + + case 0x0B: //USB type + if(g_usb_type_flag == 0) + { + rx1618_write(chip, AP_REV_DATA_OK, REG_AP_RX_COMM); //receive ok + msleep(10); + + rx1618_read(chip, &usb_type, 0x0021); + + rx1618_write(chip, PRIVATE_FAST_CHG_CMD, REG_RX_SENT_CMD); //sent fast charge + rx1618_write(chip, AP_SENT_DATA_OK, REG_AP_RX_COMM); + g_usb_type_flag = 1; + dev_err(chip->dev, "[rx1618] [%s] usb_type=0x%x \n",__func__,usb_type); + + switch (usb_type) { + case 0: //other charger + if((g_id_done_flag == 0) && (g_epp_or_bpp == BPP_MODE))//bpp and no id + { + rx1618_set_pmi_icl(chip, DC_OTHER_CURRENT); + dev_err(chip->dev, "[rx1618] [%s] bpp and no id---800mA \n",__func__); + } + else//ID ok + { + rx1618_set_pmi_icl(chip, DC_LOW_CURRENT); + dev_err(chip->dev, "[rx1618] [%s] bpp and id ok---200mA \n",__func__); + } + break; + + case 1: //SDP--1W + rx1618_set_pmi_icl(chip, DC_SDP_CURRENT); + dev_err(chip->dev, "[rx1618] [%s] SDP--1W \n",__func__); + break; + + case 2: //CDP--1W + rx1618_set_pmi_icl(chip, DC_CDP_CURRENT); + dev_err(chip->dev, "[rx1618] [%s] CDP--1W \n",__func__); + break; + + case 3: //DCP--1W + rx1618_set_pmi_icl(chip, DC_DCP_CURRENT); + dev_err(chip->dev, "[rx1618] [%s] DCP--1W \n",__func__); + break; + + case 5: //QC2-other -- 7.5W + for(i = 0; i <= 8; i++) { + uA = (DC_LOW_CURRENT + N*50000*i); + rx1618_set_pmi_icl(chip, uA); + msleep(100); + } + dev_err(chip->dev, "[rx1618] [%s] QC2 other--7.5W \n",__func__); + break; + + case 6: //QC3-other -- 10W + for(i = 0; i <= 8; i++) { + uA = (DC_LOW_CURRENT + N*50000*i); + rx1618_set_pmi_icl(chip, uA); + msleep(100); + } + dev_err(chip->dev, "[rx1618] [%s] QC3 other--10W \n",__func__); + break; + + case 7: //PD-other -- 10W + for(i = 0; i <= 8; i++) { + uA = (DC_LOW_CURRENT + N*50000*i); + rx1618_set_pmi_icl(chip, uA); + msleep(100); + } + dev_err(chip->dev, "[rx1618] [%s] PD other--7.5W \n",__func__); + break; + + case 8: //fail charger + break; + + case 9: //QC3-27W(20W) + for(i = 0; i <= 11; i++) { + uA = (DC_QC3_CURRENT + N*50000*i); + rx1618_set_pmi_icl(chip, uA); + msleep(100); + } + if(chip->epp && chip->auth && chip->wireless_psy) { + val.intval = 1; + power_supply_set_property(chip->wireless_psy, POWER_SUPPLY_PROP_WIRELESS_CP_EN, &val); + } + dev_err(chip->dev, "[rx1618] [%s] QC3-27W(20W) \n",__func__); + break; + + case 10: //PD-27W(20W) + for(i = 0; i <= 11; i++) { + uA = (DC_QC3_CURRENT + N*50000*i); + rx1618_set_pmi_icl(chip, uA); + msleep(100); + } + if(chip->epp && chip->auth && chip->wireless_psy) { + val.intval = 1; + power_supply_set_property(chip->wireless_psy, POWER_SUPPLY_PROP_WIRELESS_CP_EN, &val); + } + dev_err(chip->dev, "[rx1618] [%s] PD-27W(20W) \n",__func__); + break; + + default: + dev_err(chip->dev, "[rx1618] [%s] other Usb_type\n",__func__); + break; + } + + } + break; + + case 0x0C: //Light screen + if(g_light_screen_flag == 0) + { + rx1618_write(chip, AP_REV_DATA_OK, REG_AP_RX_COMM); //receive ok + msleep(10); + + rx1618_read(chip, &g_hw_id_h, REG_RX_REV_DATA1); + rx1618_read(chip, &g_hw_id_l, REG_RX_REV_DATA2); + rx1618_read(chip, &g_fw_id, REG_RX_REV_DATA3); + rx1618_read(chip, &g_epp_or_bpp, REG_RX_REV_DATA4); + + if(g_epp_or_bpp==EPP_MODE)//EPP + { + rx1618_set_pmi_icl(chip, EPP_MODE_CURRENT); + dev_err(chip->dev, "[rx1618] [%s] EPP--600mA \n",__func__); + } + else //BPP mode + { + rx1618_set_pmi_icl(chip, DC_LOW_CURRENT); + dev_err(chip->dev, "[rx1618] [%s] BPP--200mA \n",__func__); + } + + g_light_screen_flag = 1; + + dev_err(chip->dev, "[rx1618] [%s] Light screen,fw_id,hw_id_h,hw_id_l,g_epp_or_bpp=0x%x, 0x%x, 0x%x, 0x%x\n",__func__,g_fw_id,g_hw_id_h,g_hw_id_l,g_epp_or_bpp); + } + break; + + case 0x4C: //tx hw id + rx1618_write(chip, AP_REV_DATA_OK, REG_AP_RX_COMM); + msleep(10); + + rx1618_read(chip, &data1, REG_RX_REV_DATA1); + rx1618_read(chip, &data2, REG_RX_REV_DATA2); + rx1618_read(chip, &data3, REG_RX_REV_DATA3); + + dev_err(chip->dev, "[rx1618] [%s] tx hw id,data1-3=0x%x, 0x%x, 0x%x, 0x%x\n",__func__,data1,data2,data3,data4); + break; + + case 0x0D: //TX test request + rx1618_write(chip, AP_REV_DATA_OK, REG_AP_RX_COMM); + msleep(10); + + rx1618_read(chip, &header, REG_RX_REV_DATA1); + header_length = ((header & 0xf0) >> 4); + + dev_err(chip->dev, "[rx1618] [%s] product Test mode, header=0x%x, header_length=0x%x\n",__func__,header,header_length); + + switch (header_length) { + case 1: + rx1618_read(chip, &tx_cmd, REG_RX_REV_DATA2); + break; + + case 2: + rx1618_read(chip, &tx_cmd, REG_RX_REV_DATA2); + rx1618_read(chip, &data1, REG_RX_REV_DATA3); + break; + + case 3: + rx1618_read(chip, &tx_cmd, REG_RX_REV_DATA2); + rx1618_read(chip, &data1, REG_RX_REV_DATA3); + rx1618_read(chip, &data2, REG_RX_REV_DATA4); + break; + + case 4: + rx1618_read(chip, &tx_cmd, REG_RX_REV_DATA2); + rx1618_read(chip, &data1, REG_RX_REV_DATA3); + rx1618_read(chip, &data2, REG_RX_REV_DATA4); + rx1618_read(chip, &data3, REG_RX_REV_DATA5); + break; + + default: + break; + } + + dev_err(chip->dev, "[rx1618] [%s] tx_cmd,data=0x%x, 0x%x, 0x%x, 0x%x\n",__func__,tx_cmd,data1,data2,data3); + + if(tx_cmd==0x12) //irect + { + rx1618_write(chip, 0x38, REG_RX_SENT_DATA1); //sent header + rx1618_write(chip, 0x12, REG_RX_SENT_DATA2); //sent cmd + + irect = rx1618_get_rx_irect(chip); + data_h = (irect & 0xff00) >> 8; + rx1618_write(chip, data_h, 0x0003); + data_l = (irect & 0x00ff); + rx1618_write(chip, data_l, 0x0004); + dev_err(chip->dev, "[rx1618] [%s] product test--0x12--irect=%d \n",__func__,irect); + } + else if(tx_cmd==0x13) //vrect + { + rx1618_write(chip, 0x38, REG_RX_SENT_DATA1); //sent header + rx1618_write(chip, 0x13, REG_RX_SENT_DATA2); //sent cmd + + vrect = rx1618_get_rx_vrect(chip); + data_h = (vrect & 0xff00) >> 8; + rx1618_write(chip, data_h, 0x0003); + data_l = (vrect & 0x00ff); + rx1618_write(chip, data_l, 0x0004); + dev_err(chip->dev, "[rx1618] [%s] product test--0x13--vrect=%d \n",__func__,vrect); + } + else if(tx_cmd==0x24) //fw id + { + rx1618_write(chip, 0x28, REG_RX_SENT_DATA1); //sent header + rx1618_write(chip, 0x24, REG_RX_SENT_DATA2); //sent cmd + rx1618_write(chip, g_fw_id, 0x0003); + dev_err(chip->dev, "[rx1618] [%s] product test--0x24--g_fw_id=0x%x \n",__func__,g_fw_id); + } + else if(tx_cmd==0x25) //hw id + { + rx1618_write(chip, 0x38, REG_RX_SENT_DATA1); //sent header + rx1618_write(chip, 0x25, REG_RX_SENT_DATA2); //sent cmd + rx1618_write(chip, g_hw_id_h, 0x0003); + rx1618_write(chip, g_hw_id_l, 0x0004); + dev_err(chip->dev, "[rx1618] [%s] product test--0x25--g_hw_id=0x%x, 0x%x \n",__func__,g_hw_id_h,g_hw_id_l); + } + else + { + dev_err(chip->dev, "[rx1618] [%s] product test--other cmd \n",__func__); + break; + } + + dev_err(chip->dev, "[rx1618] [%s] header,tx_cmd,data=0x%x, 0x%x, 0x%x, 0x%x, 0x%x \n",__func__,header,tx_cmd,data1,data2,data3); + + rx1618_write(chip, PRIVATE_PRODUCT_TEST_CMD, REG_RX_SENT_CMD); //sent test cmd + rx1618_write(chip, AP_SENT_DATA_OK, REG_AP_RX_COMM); + break; + + default: + rx1618_write(chip, AP_REV_DATA_OK, REG_AP_RX_COMM); //receive ok + msleep(10); + dev_err(chip->dev, "[rx1618] [%s] other private cmd \n",__func__); + break; + } + + int_done_flag = false; + mutex_unlock(&chip->wireless_chg_int_lock); + + dev_err(chip->dev, "[rx1618] [%s] exit \n",__func__); + + return; +} + + +static irqreturn_t rx1618_chg_stat_handler(int irq, void *dev_id) +{ + struct rx1618_chg *chip = dev_id; + u8 rev_cmd=0; + + mutex_lock(&chip->irq_complete); + + if(!int_done_flag) + { + rx1618_read(chip, &rev_cmd, REG_RX_REV_CMD); + if(rev_cmd==0) + { + mdelay(100); + int_done_flag = false; + //chip->online = 0; + g_light_screen_flag = 0; + g_cali_done_flag = 0; + g_id_done_flag = 0; + g_sar_done_flag = 0; + g_fast_chg_flag = 0; + g_usb_type_flag = 0; + g_epp_or_bpp = BPP_MODE; + chip->epp = 0; + chip->auth = 0; + cancel_delayed_work_sync(&chip->wireless_work); + cancel_delayed_work_sync(&chip->wireless_int_work); + dev_err(chip->dev, "[rx1618] [%s] Wireless Offline \n",__func__); + } + else + { + int_done_flag = true; + //chip->online = 1; + + schedule_delayed_work(&chip->wireless_work, 0); + schedule_delayed_work(&chip->wireless_int_work, 0); + dev_err(chip->dev, "[rx1618] [%s] Wireless Online \n",__func__); + } + } + + mutex_unlock(&chip->irq_complete); + + dev_err(chip->dev, "[rx1618] [%s] enter \n",__func__); + + return IRQ_HANDLED; +} + + +static int rx1618_parse_dt(struct rx1618_chg *chip) +{ + int ret = 0; + + struct device_node *node = chip->dev->of_node; + if (!node) { + dev_err(chip->dev, "[rx1618] [%s] No DT data Failing Probe\n",__func__); + return -EINVAL; + } + + //Get the Interrupt GPIO resource + chip->irq_gpio = of_get_named_gpio(node, "rx1618_irq_gpio", 0); + if(gpio_is_valid(chip->irq_gpio)) { + ret = gpio_request(chip->irq_gpio, "rx1618_irq_gpio"); + if(ret) { + dev_err(chip->dev, "[rx1618] [%s] irq gpio request failed, ret = %d\n",__func__,ret); + return -EPROBE_DEFER; + } + ret = gpio_direction_input(chip->irq_gpio); + if(ret) { + dev_err(chip->dev, "[rx1618] [%s] set direction failed, ret = %d\n",__func__,ret); + goto fail_irq_gpio; + } + + chip->client->irq=gpio_to_irq(chip->irq_gpio); + if (chip->client->irq) + { + dev_err(chip->dev, "[rx1618] [%s] gpio_to_irq Success! \n",__func__); + } + else + { + dev_err(chip->dev, "[rx1618] [%s] gpio_to_irq Fail! \n",__func__); + goto fail_irq_gpio; + } + } else { + goto fail_irq_gpio; + } + + return ret; + +fail_irq_gpio: + dev_err(chip->dev, "[rx1618] [%s] fail_irq_gpio \n",__func__); + if(gpio_is_valid(chip->irq_gpio)) + gpio_free(chip->irq_gpio); + + return ret; +} + + +/*************FOD**************/ +//CMD : 0x8A +//DATA0 : Power Level, (2W, 4W, 8W, 6W, 10W… 20W) +//DATA1 : gain * 1024 hight 8bits +//DATA2 : gain * 1024 low 8bits +//DATA3: offset(mW) hight 8bits +//DATA4: offset(mW) low 8bits +/*************FOD**************/ +u8 g_power_level=0; +u8 g_gain_h=0; +u8 g_gain_l=0; +u8 g_offset_h=0; +u8 g_offset_l=0; +void FODturring(void) +{ + rx1618_write(g_chip, g_power_level, REG_RX_SENT_DATA1); + rx1618_write(g_chip, g_gain_h, REG_RX_SENT_DATA2); + rx1618_write(g_chip, g_gain_l, REG_RX_SENT_DATA3); + rx1618_write(g_chip, g_offset_h, REG_RX_SENT_DATA4); + rx1618_write(g_chip, g_offset_l, REG_RX_SENT_DATA5); + + dev_err(g_chip->dev, "[rx1618] [%s] FODturring:g_power_level,g_gain_h,g_gain_l,g_offset_h,g_offset_l %d,%d,%d,%d,%d\n",__func__,g_power_level,g_gain_h,g_gain_l,g_offset_h,g_offset_l); + + msleep(5); + + rx1618_write(g_chip, PRIVATE_FOD_TEST_CMD, REG_RX_SENT_CMD); + rx1618_write(g_chip, AP_SENT_DATA_OK, REG_AP_RX_COMM); +} + +static ssize_t chip_fod_power_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int index; + index = (int)simple_strtoul(buf, NULL, 10); + + g_power_level = (index & 0xff); + dev_err(g_chip->dev, "[rx1618] [%s] g_power_level=0x%x \n",__func__,g_power_level); + + return count; +} + +static ssize_t chip_fod_gain_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + u16 index; + index = (u16)simple_strtoul(buf, NULL, 10); + + g_gain_l = (index & 0x00ff); + g_gain_h = (index & 0xff00) >> 8; + + dev_err(g_chip->dev, "[rx1618] [%s] g_gain_l=0x%x, g_gain_h=0x%x \n",__func__,g_gain_l,g_gain_h); + + return count; +} + +static ssize_t chip_fod_offset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + u16 index; + index = (u16)simple_strtoul(buf, NULL, 10); + + g_offset_l = (index & 0x00ff); + g_offset_h = (index & 0xff00) >> 8; + + dev_err(g_chip->dev, "[rx1618] [%s] g_offset_l=0x%x, g_offset_h=0x%x \n",__func__,g_offset_l,g_offset_h); + + FODturring(); + + return count; +} + + +static ssize_t chip_ibuck_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned int ibuck = 0; + + ibuck = rx1618_get_rx_ibuck(g_chip); + + return sprintf(buf, "rx1618 Ibuck: %d mA\n", ibuck); +} + + +static ssize_t chip_vrect_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned int vrect = 0; + + vrect = rx1618_get_rx_vrect(g_chip); + + return sprintf(buf, "rx1618 Vrect : %d mV\n", vrect); +} + +static ssize_t chip_irect_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned int irect = 0; + + irect = rx1618_get_rx_irect(g_chip); + + return sprintf(buf, "rx1618 Irect: %d mA\n", irect); +} + + +static ssize_t chip_vbuck_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int index; + + index = (int)simple_strtoul(buf, NULL, 10); + dev_err(g_chip->dev, "[rx1618] [%s] --Store output_voltage = %d\n",__func__,index); + if ((index < 5166) || (index > 9609)) { + dev_err(g_chip->dev, "[rx1618] [%s] Store Voltage %s is invalid\n",__func__,buf); + rx1618_set_vbuck(g_chip, 0); + return count; + } + + rx1618_set_vbuck(g_chip, index); + + return count; +} + +static ssize_t chip_vbuck_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned int vbuck = 0; + + vbuck = rx1618_get_rx_vbuck(g_chip); + + return sprintf(buf, "rx1618 Vbuck : %d mV\n", vbuck); +} + +static ssize_t chip_ap_req_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + //int index; + //index = (int)simple_strtoul(buf, NULL, 10); + return count; +} + +static ssize_t chip_ap_req_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 data = 0; + return sprintf(buf, "AP REQ DATA : 0x%x \n", data); +} + +static ssize_t chip_firmware_update_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + bool enable = strncmp(buf, "update", 6) ? false : true; + + dev_err(g_chip->dev, "[rx1618] [%s] Firmware Update enable = %d\n",__func__,enable); + if(enable) { + int ret = rx1618_download_firmware(g_chip); + if (!ret) { + dev_err(g_chip->dev, "[rx1618] [%s] Firmware Update failed! Please try again! \n",__func__); + } + dev_err(g_chip->dev, "[rx1618] [%s] Firmware Update Success!!! \n",__func__); + } + + return count; +} + + +static ssize_t chip_firmware_update_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + bool ret = rx1618_check_firmware(g_chip); + if(ret) + { + return sprintf(buf, "rx1618_download_firmware check Success!!!\n"); + } + else + { + return sprintf(buf, "rx1618_download_firmware check Fail!!!\n"); + } +} + + +static ssize_t chip_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + //bool enable = strncmp(buf, "1", 1) ? false : true; + + //rx1618_chip_enable(g_chip, enable); + //dev_err(g_chip->dev, "[rx1618] [%s] chip enable:%d\n",__func__,enable); + + return count; +} + +static ssize_t chip_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "rx1618 chip enable status: %d \n",g_chip->chip_enable); +} + +static ssize_t chip_fw_version_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "Firmware ID: 0x%x \t\n", g_fw_id); +} + +static ssize_t chip_hw_version_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "Hardware ID: 0x%x, 0x%x \t\n", g_hw_id_h, g_hw_id_l); +} + + +static ssize_t chip_vbuck_calibration_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int index = 0; + + index = (int)simple_strtoul(buf, NULL, 10); + + g_Delta = index; + dev_err(g_chip->dev, "[rx1618] [%s] g_Delta = %d \n",__func__, g_Delta); + + return count; +} + +static DEVICE_ATTR(chip_fod_power, S_IWUSR, NULL, chip_fod_power_store); +static DEVICE_ATTR(chip_fod_gain, S_IWUSR, NULL, chip_fod_gain_store); +static DEVICE_ATTR(chip_fod_offset, S_IWUSR, NULL, chip_fod_offset_store); +static DEVICE_ATTR(chip_vrect, S_IRUGO, chip_vrect_show, NULL); +static DEVICE_ATTR(chip_irect, S_IRUGO, chip_irect_show, NULL); +static DEVICE_ATTR(chip_vbuck_calibration, S_IWUSR, NULL, chip_vbuck_calibration_store); +static DEVICE_ATTR(chip_firmware_update, S_IWUSR | S_IRUGO, chip_firmware_update_show, chip_firmware_update_store); +static DEVICE_ATTR(chip_fw_version, S_IRUGO, chip_fw_version_show, NULL); +static DEVICE_ATTR(chip_hw_version, S_IRUGO, chip_hw_version_show, NULL); +static DEVICE_ATTR(chip_enable, S_IWUSR | S_IRUGO, chip_enable_show, chip_enable_store); +static DEVICE_ATTR(chip_vbuck, S_IWUSR | S_IRUGO, chip_vbuck_show, chip_vbuck_store); +static DEVICE_ATTR(chip_ibuck, S_IRUGO, chip_ibuck_show, NULL); +static DEVICE_ATTR(chip_ap_req, S_IWUSR | S_IRUGO, chip_ap_req_show, chip_ap_req_store); + +static struct attribute *rx1618_sysfs_attrs[] = { + &dev_attr_chip_vrect.attr, + &dev_attr_chip_irect.attr, + &dev_attr_chip_fw_version.attr, + &dev_attr_chip_hw_version.attr, + &dev_attr_chip_enable.attr, + &dev_attr_chip_vbuck.attr, + &dev_attr_chip_ibuck.attr, + &dev_attr_chip_ap_req.attr, + &dev_attr_chip_firmware_update.attr, + &dev_attr_chip_vbuck_calibration.attr, + &dev_attr_chip_fod_power.attr, + &dev_attr_chip_fod_gain.attr, + &dev_attr_chip_fod_offset.attr, + NULL, +}; + +static const struct attribute_group rx1618_sysfs_group_attrs = { + .attrs = rx1618_sysfs_attrs, +}; + +#if 1 +static enum power_supply_property rx1618_wireless_properties[] = { + /* + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_CHARGING_ENABLED, + POWER_SUPPLY_PROP_RX_CHIP_ID, //RX chip id + POWER_SUPPLY_PROP_RX_VRECT, //RX vrect + POWER_SUPPLY_PROP_RX_IOUT, //RX output current + POWER_SUPPLY_PROP_RX_VOUT, //RX output voltage + POWER_SUPPLY_PROP_RX_ILIMIT, //RX Main LDO output current limit + POWER_SUPPLY_PROP_VOUT_SET, //Vout voltage set + */ + POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, +}; + + +static int rx1618_wireless_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + int ret; + struct rx1618_chg *chip = power_supply_get_drvdata(psy); + int data; + + switch (prop) { + /* + case POWER_SUPPLY_PROP_PRESENT: + break; + case POWER_SUPPLY_PROP_CHARGING_ENABLED: + rx1618_chip_enable(chip, val->intval); + break; + case POWER_SUPPLY_PROP_VOUT_SET: + ret = rx1618_set_vbuck(chip, val->intval); + if(ret < 0) + return ret; + break; + */ + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION: + data = val->intval/1000; + ret = rx1618_set_vbuck(chip, data); + break; + default: + return -EINVAL; + } + + return 0; +} + + +static int rx1618_wireless_get_property(struct power_supply *psy, + enum power_supply_property prop, + union power_supply_propval *val) +{ + struct rx1618_chg *chip = power_supply_get_drvdata(psy); + + switch (prop) { + /* + case POWER_SUPPLY_PROP_PRESENT: + val->intval = rx1618_is_rx_present(chip); + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = chip->online; + break; + case POWER_SUPPLY_PROP_CHARGING_ENABLED: + val->intval = chip->chip_enable; + break; + case POWER_SUPPLY_PROP_RX_CHIP_ID: + val->intval = rx1618_get_rx_chip_id(chip); + break; + case POWER_SUPPLY_PROP_RX_VRECT: + val->intval = rx1618_get_rx_vrect(chip); + break; + case POWER_SUPPLY_PROP_RX_IOUT: + val->intval = rx1618_get_rx_ibuck(chip); + break; + case POWER_SUPPLY_PROP_RX_VOUT: + val->intval = rx1618_get_rx_vbuck(chip); + break; + case POWER_SUPPLY_PROP_VOUT_SET: + val->intval = 0; + break; + */ + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION: + val->intval = rx1618_get_rx_vbuck(chip); + break; + default: + return -EINVAL; + } + return 0; +} +#endif + +// first step: define regmap_config +static const struct regmap_config rx1618_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = 0xFFFF, +}; + +static int rx1618_prop_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + int rc; + + switch (psp) { + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION: + return 1; + default: + rc = 0; + break; + } + + return rc; +} + +static int rx1618_probe(struct i2c_client *client,const struct i2c_device_id *id) +{ + int ret = 0; + struct rx1618_chg *chip; + struct kobject *rx1618_kobj; + + struct power_supply_config wip_psy_cfg = {}; + int hw_id; + /* + struct power_supply *batt_psy; + + batt_psy = power_supply_get_by_name("battery"); + if (!batt_psy) { + dev_err(&client->dev, "Battery supply not found; defer probe\n"); + return -EPROBE_DEFER; + } + */ + + hw_id = get_hw_country_version(); + dev_info(&client->dev, "[rx1618] %s: hw_id is %d\n", __func__, hw_id); + if (hw_id) /*hw_id=1 is idt */ + return 0; + + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) { + dev_err(&client->dev, "i2c allocated device info data failed!\n"); + return -ENOMEM; + } + + chip->regmap = regmap_init_i2c(client, &rx1618_regmap_config); + if (!chip->regmap) { + dev_err(&client->dev, "parent regmap is missing\n"); + return -EINVAL; + } + + chip->client = client; + chip->dev = &client->dev; + + //chip->batt_psy = batt_psy; + //chip->online = 0; + chip->chip_enable = false; + + device_init_wakeup(&client->dev, true); + i2c_set_clientdata(client, chip); + + rx1618_parse_dt(chip); + + mutex_init(&chip->irq_complete); + mutex_init(&chip->wireless_chg_lock); + mutex_init(&chip->wireless_chg_int_lock); + INIT_DELAYED_WORK(&chip->wireless_work, rx1618_wireless_work); + INIT_DELAYED_WORK(&chip->wireless_int_work, rx1618_wireless_int_work); + + chip->wip_psy_d.name = "rx1618"; + chip->wip_psy_d.type = POWER_SUPPLY_TYPE_WIRELESS; + chip->wip_psy_d.get_property = rx1618_wireless_get_property; + chip->wip_psy_d.set_property = rx1618_wireless_set_property; + chip->wip_psy_d.properties = rx1618_wireless_properties; + chip->wip_psy_d.num_properties = ARRAY_SIZE(rx1618_wireless_properties); + chip->wip_psy_d.property_is_writeable = rx1618_prop_is_writeable, + + wip_psy_cfg.drv_data = chip; + + chip->wip_psy = devm_power_supply_register(chip->dev, &chip->wip_psy_d, &wip_psy_cfg); + if (IS_ERR(chip->wip_psy)) { + dev_err(chip->dev, "Couldn't register wip psy rc=%ld\n", PTR_ERR(chip->wip_psy)); + return ret; + } + + if(chip->client->irq) { + ret = devm_request_threaded_irq(&chip->client->dev, chip->client->irq, NULL, + rx1618_chg_stat_handler, + (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT), + "rx1618_chg_stat_irq", chip); + if (ret) { + dev_err(chip->dev, "Failed irq = %d ret = %d\n", chip->client->irq, ret); + } + } + + enable_irq_wake(chip->client->irq); + + rx1618_kobj = kobject_create_and_add("rx1618", NULL); + if (!rx1618_kobj) { + dev_err(chip->dev, "sysfs_create_group fail"); + goto error_sysfs; + } + ret = sysfs_create_group(rx1618_kobj,&rx1618_sysfs_group_attrs); + if (ret < 0) + { + dev_err(chip->dev, "sysfs_create_group fail %d\n", ret); + goto error_sysfs; + } + + g_chip = chip; + + determine_initial_status(chip); + rx1618_chip_init(chip); + + rx1618_dump_reg(); + + dev_err(chip->dev, "[rx1618] [%s] success! \n",__func__); + + return 0; + + +error_sysfs: + sysfs_remove_group(rx1618_kobj, &rx1618_sysfs_group_attrs); + dev_err(chip->dev, "[rx1618] [%s] rx1618 probe error_sysfs! \n",__func__); + + mutex_destroy(&chip->irq_complete); + mutex_destroy(&chip->wireless_chg_lock); + mutex_destroy(&chip->wireless_chg_int_lock); + if (chip->irq_gpio > 0) + gpio_free(chip->irq_gpio); + + return 0; +} + +static int rx1618_remove(struct i2c_client *client) +{ + struct rx1618_chg *chip = i2c_get_clientdata(client); + + mutex_destroy(&chip->irq_complete); + cancel_delayed_work_sync(&chip->wireless_work); + cancel_delayed_work_sync(&chip->wireless_int_work); + + return 0; +} + +static const struct i2c_device_id rx1618_id[] = { + {rx1618_DRIVER_NAME, 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, rx1618_id); + +static struct of_device_id rx1618_match_table[] = { + { .compatible = "nuvolta,wl_charger_rx1618",}, + {} +}; + +static struct i2c_driver rx1618_driver = { + .driver = { + .name = rx1618_DRIVER_NAME, + .of_match_table = rx1618_match_table, + }, + .probe = rx1618_probe, + .remove = rx1618_remove, + .id_table = rx1618_id, +}; + +static int __init rx1618_init(void) +{ + int ret; + + ret = i2c_add_driver(&rx1618_driver); + if (ret) + printk(KERN_ERR "rx1618 i2c driver init failed!\n"); + + return ret; +} + +static void __exit rx1618_exit(void) +{ + i2c_del_driver(&rx1618_driver); +} + +module_init(rx1618_init); +module_exit(rx1618_exit); + +MODULE_AUTHOR("colin"); +MODULE_DESCRIPTION("NUVOLTA Wireless Power Charger Monitor driver"); +MODULE_LICENSE("GPL/BSD"); diff --git a/drivers/power/supply/rx1618.h b/drivers/power/supply/rx1618.h new file mode 100644 index 000000000000..91aa5f8c21e3 --- /dev/null +++ b/drivers/power/supply/rx1618.h @@ -0,0 +1,31 @@ +/** + * @file rx1618.h + * @author + * @date Tuesday, January 9th 2018 + * + * @brief + * + * + */ +#ifndef __RX1618_H__ +#define __RX1618_H__ + +#define rx1618_DRIVER_NAME "rx1618" +#define rx1618_I2C_ADDR 0x60 + +/* bits mask */ +#define BIT0 (1 << 0) +#define BIT1 (1 << 1) +#define BIT2 (1 << 2) +#define BIT3 (1 << 3) +#define BIT4 (1 << 4) +#define BIT5 (1 << 5) +#define BIT6 (1 << 6) +#define BIT7 (1 << 7) + + +const u8 fw_data[]={ +0x60, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0x84, 0x32, 0x00, 0x00, 0x1C, 0x2D, 0x00, 0x00, 0x9C, 0x32, 0x00, 0x00, 0xA4, 0x32, 0x00, 0x00, 0xAC, 0x32, 0x00, 0x00, 0xB4, 0x33, 0x00, 0x00, 0x1C, 0x2B, 0x00, 0x00, 0x3C, 0x2D, 0x00, 0x00, 0xB4, 0x32, 0x00, 0x00, 0xBC, 0x32, 0x00, 0x00, 0xD0, 0x32, 0x00, 0x00, 0x58, 0x33, 0x00, 0x00, 0x94, 0x33, 0x00, 0x00, 0x9C, 0x33, 0x00, 0x00, 0xA4, 0x33, 0x00, 0x00, 0xAC, 0x33, 0x00, 0x00, 0xB4, 0x33, 0x00, 0x00, 0xB4, 0x33, 0x00, 0x00, 0xB4, 0x33, 0x00, 0x00, 0xB4, 0x33, 0x00, 0x00, 0xB4, 0x33, 0x00, 0x00, 0xB4, 0x33, 0x00, 0x00, 0xB4, 0x33, 0x00, 0x00, 0xB4, 0x33, 0x00, 0x00, 0xB4, 0x33, 0x00, 0x00, 0xB4, 0x33, 0x00, 0x00, 0xB4, 0x33, 0x00, 0x00, 0xB4, 0x33, 0x00, 0x00, 0xB4, 0x33, 0x00, 0x00, 0xB4, 0x33, 0x00, 0x00, 0xB4, 0x33, 0x00, 0x00, 0xB4, 0x33, 0x00, 0x00, 0x44, 0x74, 0x89, 0x74, 0x40, 0x3A, 0x0A, 0x0C, 0xC3, 0x6C, 0x00, 0x20, 0x00, 0x2A, 0x89, 0x74, 0x08, 0x58, 0x20, 0xA3, 0x00, 0x23, 0x0E, 0x64, 0xFD, 0x0B, 0x3C, 0x78, 0xC2, 0x14, 0x89, 0x74, 0x40, 0x3A, 0x0C, 0x0C, 0xA3, 0x5A, 0x55, 0x75, 0x00, 0x25, 0x00, 0x33, 0x4C, 0x58, 0x8C, 0x59, 0x80, 0x84, 0x80, 0xA2, 0x00, 0x23, 0x4E, 0x65, 0xFA, 0x0B, 0x82, 0x14, 0x67, 0x10, 0x60, 0x83, 0x41, 0x3B, 0x03, 0x08, 0x00, 0x30, 0x02, 0x04, 0x05, 0x10, 0x3C, 0x78, 0x04, 0x10, 0x3C, 0x78, 0x00, 0x32, 0x62, 0x10, 0x40, 0xA3, 0x3C, 0x78, 0x14, 0x00, 0x00, 0x20, 0x15, 0x00, 0x00, 0x20, 0x72, 0x10, 0x03, 0xC0, 0x20, 0x64, 0x52, 0x10, 0x02, 0xC0, 0x21, 0x64, 0x31, 0x10, 0x87, 0x6F, 0xF1, 0x10, 0xD2, 0x10, 0xB2, 0x10, 0x9C, 0x65, 0x07, 0x08, 0x80, 0x95, 0x80, 0xB7, 0x03, 0x25, 0x03, 0x27, 0x9C, 0x65, 0xFB, 0x0F, 0x6F, 0x10, 0x4F, 0x10, 0x8E, 0x60, 0x42, 0x4A, 0x40, 0x3A, 0x07, 0x0C, 0x00, 0x31, 0x20, 0xB3, 0x03, 0x23, 0x00, 0x2A, 0x40, 0x3A, 0xFC, 0x0B, 0x00, 0xE0, 0x01, 0x13, 0x00, 0xE0, 0xD3, 0x1A, 0x00, 0x04, 0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x14, 0x00, 0x00, 0x20, 0xC4, 0x39, 0x00, 0x00, 0x14, 0x00, 0x00, 0x20, 0x28, 0x01, 0x00, 0x20, 0x00, 0xE0, 0x16, 0x18, 0x00, 0xC0, 0x20, 0x40, 0x3C, 0x78, 0x00, 0x00, 0x3C, 0x78, 0x00, 0x00, 0x03, 0x32, 0x62, 0x10, 0x40, 0xA3, 0x3C, 0x78, 0x2B, 0x00, 0x00, 0x20, 0xD1, 0x14, 0x01, 0x75, 0x00, 0xE0, 0x56, 0x0F, 0x40, 0x38, 0x04, 0x0C, 0x00, 0x32, 0x6A, 0x11, 0x40, 0xA3, 0x40, 0x33, 0xD2, 0x64, 0x24, 0x0C, 0x0C, 0x65, 0x06, 0x0C, 0x41, 0x3C, 0x16, 0x0C, 0x44, 0x3C, 0x2E, 0x08, 0x1D, 0x04, 0x80, 0x33, 0x61, 0x43, 0xD2, 0x64, 0x19, 0x0C, 0x80, 0x33, 0x62, 0x43, 0xD2, 0x64, 0x15, 0x0C, 0x80, 0x33, 0xD2, 0x64, 0x22, 0x08, 0x03, 0x32, 0x7E, 0x10, 0x40, 0xA3, 0x03, 0x30, 0x00, 0xE0, 0x28, 0x0F, 0x1B, 0x04, 0x7B, 0x10, 0x60, 0x83, 0x42, 0x3B, 0x17, 0x08, 0x00, 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0xE0, 0x6E, 0x17, 0x11, 0x04, 0x76, 0x10, 0x60, 0x83, 0x42, 0x3B, 0x0D, 0x08, 0x03, 0x32, 0x73, 0x10, 0x40, 0xA3, 0x0C, 0x30, 0x00, 0xE0, 0xAD, 0x0A, 0x06, 0x30, 0x00, 0xE0, 0x10, 0x0F, 0x00, 0x30, 0x00, 0xE0, 0x73, 0x03, 0x6E, 0x10, 0x60, 0x83, 0x40, 0x3B, 0x04, 0x0C, 0x41, 0x3B, 0x15, 0x08, 0x0D, 0x04, 0x00, 0xE0, 0x3A, 0x10, 0x40, 0x38, 0x10, 0x0C, 0x69, 0x10, 0x41, 0x93, 0x86, 0x3A, 0x41, 0xB3, 0x01, 0x32, 0x66, 0x10, 0x40, 0xA3, 0x08, 0x04, 0x00, 0xE0, 0x42, 0x10, 0x40, 0x38, 0x04, 0x0C, 0x02, 0x32, 0x62, 0x10, 0x40, 0xA3, 0x91, 0x14, 0x2B, 0x00, 0x00, 0x20, 0x00, 0xA0, 0x00, 0x40, 0x46, 0x10, 0x00, 0x33, 0x60, 0xA2, 0x61, 0xA2, 0x62, 0xA2, 0x63, 0xA2, 0x03, 0x22, 0x60, 0xA2, 0x61, 0xA2, 0x00, 0x33, 0x61, 0xAA, 0x3C, 0x78, 0x2C, 0x00, 0x00, 0x20, 0xD1, 0x14, 0x01, 0x75, 0x00, 0xE0, 0xEA, 0x0E, 0x40, 0x38, 0x0B, 0x0C, 0x7E, 0x12, 0x00, 0x32, 0x41, 0xA3, 0x0A, 0x32, 0x40, 0xA3, 0x02, 0x30, 0x00, 0x31, 0x04, 0x32, 0x00, 0xE0, 0x3A, 0x0F, 0x40, 0x33, 0xD2, 0x64, 0x16, 0x0C, 0x0C, 0x65, 0x08, 0x0C, 0x42, 0x3C, 0x44, 0x0C, 0x44, 0x3C, 0x10, 0x0C, 0x41, 0x3C, 0x9E, 0x08, 0x23, 0x04, 0x80, 0x33, 0x61, 0x43, 0xD2, 0x64, 0x84, 0x0C, 0x80, 0x33, 0x62, 0x43, 0xD2, 0x64, 0x80, 0x0C, 0x80, 0x33, 0xD2, 0x64, 0x92, 0x08, 0x51, 0x04, 0xFF, 0xE3, 0x22, 0xFF, 0x6C, 0x12, 0x63, 0x83, 0x02, 0x3B, 0x0A, 0x08, 0x00, 0x23, 0x4A, 0x12, 0x63, 0xA2, 0x02, 0x30, 0x00, 0x31, 0x04, 0x32, 0x00, 0xE0, 0x14, 0x0F, 0x82, 0x04, 0x08, 0x30, 0x00, 0xE0, 0xD6, 0x0E, 0x0A, 0x32, 0x64, 0x12, 0x41, 0xA3, 0x7B, 0x04, 0x62, 0x12, 0x61, 0x83, 0x43, 0x3B, 0x09, 0x08, 0xFF, 0xE3, 0xFE, 0xFE, 0x83, 0x6C, 0x01, 0x30, 0x05, 0x31, 0x00, 0xE0, 0xE7, 0x16, 0x6F, 0x04, 0x44, 0x3B, 0x09, 0x08, 0xFF, 0xE3, 0xF4, 0xFE, 0x83, 0x6C, 0x01, 0x30, 0x05, 0x31, 0x00, 0xE0, 0xDD, 0x16, 0x65, 0x04, 0x00, 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0xE0, 0xD7, 0x16, 0x5F, 0x04, 0x74, 0x11, 0x61, 0x83, 0x43, 0x3B, 0x0C, 0x08, 0xFF, 0xE3, 0xEA, 0xFE, 0x42, 0x80, 0x48, 0x42, 0x63, 0x80, 0x8C, 0x6C, 0x70, 0x11, 0x41, 0xAB, 0xFF, 0xE3, 0xE4, 0xFE, 0x10, 0x04, 0x44, 0x3B, 0x0E, 0x08, 0xFF, 0xE3, 0xDD, 0xFE, 0x6C, 0x11, 0x41, 0x80, 0x5A, 0x42, 0x5A, 0x4A, 0x40, 0xA3, 0x42, 0x80, 0x5A, 0x42, 0x5A, 0x4A, 0x41, 0xA3, 0xFF, 0xE3, 0xD4, 0xFE, 0x65, 0x11, 0x00, 0x32, 0x43, 0xA3, 0x61, 0x83, 0x40, 0x3B, 0x06, 0x08, 0x00, 0xE0, 0x32, 0x0A, 0x0C, 0x30, 0x00, 0xE0, 0xF7, 0x09, 0x60, 0x11, 0x61, 0x83, 0x06, 0x3B, 0x16, 0x08, 0x00, 0x23, 0xCC, 0x74, 0x5D, 0x10, 0x61, 0xA2, 0x42, 0x3B, 0x04, 0x08, 0x03, 0x32, 0x7A, 0x10, 0x41, 0xA3, 0x42, 0x3C, 0x05, 0x08, 0x01, 0x32, 0x78, 0x10, 0x42, 0xA3, 0x23, 0x04, 0x02, 0x30, 0x00, 0x31, 0x04, 0x32, 0x00, 0xE0, 0xAF, 0x0E, 0x1D, 0x04, 0x05, 0x30, 0x00, 0xE0, 0x41, 0x0E, 0x0A, 0x32, 0x71, 0x10, 0x41, 0xA3, 0x16, 0x04, 0x70, 0x10, 0x00, 0x32, 0x43, 0xA3, 0x61, 0x83, 0x01, 0x32, 0xC8, 0x64, 0x03, 0x08, 0x47, 0x3B, 0x05, 0x08, 0x08, 0x30, 0x00, 0xE0, 0x61, 0x0E, 0x09, 0x04, 0x00, 0x23, 0x49, 0x10, 0x61, 0xA2, 0x02, 0x30, 0x00, 0x31, 0x04, 0x32, 0x00, 0xE0, 0x92, 0x0E, 0x00, 0xE0, 0x40, 0x0F, 0x40, 0x38, 0x42, 0x0C, 0x63, 0x10, 0x62, 0x83, 0x41, 0x3B, 0x3E, 0x08, 0x06, 0x04, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x20, 0x30, 0x00, 0x00, 0x20, 0x66, 0x11, 0x00, 0x32, 0x42, 0xA3, 0x41, 0x83, 0x07, 0x3A, 0x32, 0x08, 0x64, 0x11, 0x43, 0xC4, 0x43, 0x08, 0x60, 0x93, 0x0C, 0x78, 0x23, 0x30, 0x00, 0xE0, 0xF6, 0x0F, 0x29, 0x04, 0x31, 0x30, 0x00, 0xE0, 0x00, 0x10, 0x25, 0x04, 0x00, 0xE0, 0x0B, 0x10, 0x22, 0x04, 0x00, 0xE0, 0x14, 0x10, 0x1F, 0x04, 0x7C, 0x10, 0x00, 0x83, 0x80, 0x74, 0x14, 0x33, 0x8C, 0x64, 0x02, 0x08, 0x14, 0x30, 0x00, 0x74, 0x00, 0xE0, 0x15, 0x10, 0x14, 0x04, 0x76, 0x10, 0x01, 0x83, 0x80, 0x74, 0x14, 0x33, 0x8C, 0x64, 0x02, 0x08, 0x14, 0x30, 0x00, 0x74, 0x00, 0xE0, 0x1A, 0x10, 0x09, 0x04, 0x71, 0x10, 0x60, 0x83, 0x0A, 0x3B, 0x00, 0xC4, 0x00, 0x05, 0x01, 0x20, 0x00, 0xE0, 0x21, 0x10, 0x91, 0x14, 0x6C, 0x10, 0x01, 0x83, 0x3C, 0x78, 0x00, 0x00, 0x6A, 0x10, 0x61, 0x8B, 0x40, 0x3B, 0x03, 0x64, 0x3C, 0x78, 0x00, 0x00, 0x67, 0x10, 0x61, 0x8B, 0x40, 0x3B, 0x03, 0x64, 0x3C, 0x78, 0x00, 0x00, 0x01, 0x32, 0x62, 0x10, 0x42, 0xA3, 0x3C, 0x78, 0x2C, 0x00, 0x00, 0x20, 0xB0, 0x38, 0x00, 0x00, 0x30, 0x00, 0x00, 0x20, 0x00, 0x32, 0x62, 0x10, 0x40, 0xA3, 0x3C, 0x78, 0x34, 0x00, 0x00, 0x20, 0xD2, 0x14, 0x01, 0x75, 0x00, 0xE0, 0xCC, 0x0D, 0x40, 0x38, 0x10, 0x0C, 0x00, 0x30, 0x00, 0xE0, 0xF3, 0x0B, 0xAA, 0x10, 0x03, 0x48, 0x00, 0xA5, 0x00, 0xE0, 0xC2, 0x0E, 0x68, 0x10, 0x41, 0x93, 0xA6, 0x3A, 0x41, 0xB3, 0x00, 0x85, 0x00, 0xE0, 0xCD, 0x0E, 0x41, 0x3C, 0x04, 0x08, 0x02, 0x30, 0x00, 0xE0, 0xA8, 0x0D, 0x92, 0x14, 0x00, 0x00, 0x34, 0x00, 0x00, 0x20, 0x00, 0xA0, 0x00, 0x40, 0xC1, 0x14, 0x73, 0x10, 0x40, 0x93, 0x33, 0x10, 0x84, 0x68, 0x40, 0xB3, 0x06, 0x31, 0x24, 0xA3, 0x03, 0x31, 0x25, 0xA3, 0x30, 0x4A, 0x21, 0xE4, 0xFC, 0x20, 0x8F, 0x10, 0x82, 0xC4, 0x20, 0x20, 0x00, 0xE4, 0x07, 0x30, 0x48, 0x4A, 0x42, 0xE4, 0x3C, 0x30, 0x41, 0xA3, 0x00, 0xE4, 0x38, 0x30, 0x00, 0xA3, 0x40, 0x8B, 0x42, 0xE4, 0x80, 0x33, 0x40, 0xAB, 0x21, 0xE4, 0x3C, 0x30, 0x22, 0xA3, 0x41, 0x8B, 0x42, 0xE4, 0x3F, 0x20, 0x41, 0xAB, 0x81, 0x14, 0x00, 0x00, 0x38, 0x00, 0x00, 0x20, 0xFF, 0x3F, 0xFE, 0xFF, 0xBF, 0x3F, 0xFE, 0xFF, 0xD2, 0x14, 0x01, 0x75, 0x00, 0xE0, 0x80, 0x0D, 0x40, 0x38, 0x23, 0x0C, 0x62, 0x13, 0x03, 0x32, 0x45, 0xA3, 0x42, 0x83, 0x81, 0x3A, 0x42, 0xA3, 0x41, 0x83, 0x42, 0xE4, 0x3C, 0x30, 0x41, 0xA3, 0x40, 0x93, 0x1E, 0x12, 0x80, 0x68, 0xAE, 0x3A, 0x40, 0xB3, 0x3D, 0x12, 0x84, 0x68, 0x42, 0xE4, 0x07, 0x30, 0x42, 0xEC, 0x06, 0x00, 0x40, 0xA3, 0x60, 0x93, 0x63, 0x4B, 0x63, 0xE4, 0x07, 0x20, 0x20, 0x3B, 0x04, 0x08, 0x06, 0x32, 0x74, 0x12, 0x45, 0xA3, 0x08, 0x30, 0x00, 0xE0, 0x64, 0x0D, 0x50, 0x3C, 0x21, 0x0C, 0x10, 0x3C, 0x0D, 0x08, 0x42, 0x3C, 0xDE, 0x0C, 0x02, 0x3C, 0x04, 0x08, 0x41, 0x3C, 0x5D, 0x09, 0x82, 0x04, 0x44, 0x3C, 0x37, 0x0C, 0x48, 0x3C, 0x58, 0x09, 0x13, 0x04, 0x80, 0x33, 0xD2, 0x64, 0xC1, 0x0C, 0x0C, 0x65, 0x05, 0x0C, 0x40, 0x33, 0xD2, 0x64, 0x4F, 0x09, 0x2B, 0x04, 0x80, 0x33, 0x61, 0x43, 0xD2, 0x64, 0x27, 0x0C, 0x80, 0x33, 0x62, 0x43, 0xD2, 0x64, 0x46, 0x09, 0x22, 0x04, 0x00, 0x30, 0x00, 0xE0, 0x87, 0x0E, 0x41, 0x38, 0x40, 0x09, 0x5E, 0x11, 0x20, 0x92, 0x6E, 0x49, 0x06, 0x23, 0x63, 0xE4, 0x07, 0x20, 0x6E, 0x43, 0x1B, 0x11, 0x40, 0x68, 0xC4, 0x6C, 0x60, 0xB2, 0xE0, 0x31, 0x29, 0x41, 0xC4, 0x68, 0x40, 0x3B, 0x07, 0x0C, 0x07, 0x30, 0x00, 0x31, 0x64, 0x32, 0x00, 0xE0, 0x7C, 0x0D, 0x2A, 0x05, 0x02, 0x30, 0x00, 0x31, 0x64, 0x32, 0x00, 0xE0, 0x76, 0x0D, 0x24, 0x05, 0x70, 0x11, 0x40, 0x93, 0x4A, 0x4A, 0x00, 0x22, 0x42, 0xE4, 0x0F, 0x20, 0x02, 0x42, 0x21, 0x83, 0x21, 0xE4, 0x3C, 0x30, 0x40, 0x6C, 0x21, 0xA3, 0x20, 0x3A, 0x34, 0x08, 0x8F, 0x6C, 0x65, 0x83, 0x00, 0x2B, 0xCC, 0x74, 0x65, 0xA2, 0x40, 0x3B, 0x1F, 0x0C, 0xCB, 0x6C, 0x41, 0x82, 0x42, 0xE4, 0x3C, 0x30, 0x41, 0xA3, 0x40, 0x83, 0x86, 0x3A, 0x4B, 0x6C, 0x40, 0xA3, 0x40, 0x93, 0x42, 0xE4, 0x07, 0x20, 0x43, 0x42, 0x21, 0xE4, 0x38, 0x30, 0x84, 0x6C, 0x42, 0xE4, 0x07, 0x30, 0x42, 0xEC, 0x06, 0x00, 0x40, 0xA3, 0x40, 0x93, 0x1B, 0x10, 0x80, 0x68, 0xAE, 0x3A, 0x40, 0xB3, 0x08, 0x30, 0x00, 0xE0, 0xEF, 0x0C, 0x14, 0x04, 0x76, 0x10, 0x42, 0x83, 0xA1, 0x3A, 0x42, 0xA3, 0x06, 0x30, 0x00, 0xE0, 0xFF, 0x09, 0x11, 0x30, 0x00, 0xE0, 0x68, 0x08, 0x05, 0x30, 0x00, 0xE0, 0xCB, 0x0C, 0x06, 0x04, 0x02, 0x30, 0x00, 0x31, 0x64, 0x32, 0x00, 0xE0, 0x2F, 0x0D, 0xFF, 0xE3, 0x2F, 0xFD, 0xDB, 0x04, 0x6B, 0x10, 0x60, 0x93, 0x63, 0xE4, 0x07, 0x20, 0x06, 0x3B, 0xD5, 0x08, 0x4B, 0x10, 0x62, 0xC4, 0x42, 0x08, 0x40, 0x92, 0x08, 0x78, 0x4A, 0x10, 0x6C, 0x5A, 0x80, 0x83, 0xFF, 0xE3, 0x14, 0xFD, 0x83, 0x6C, 0x01, 0x30, 0x53, 0x6C, 0x00, 0xE0, 0xFD, 0x14, 0xC5, 0x04, 0x38, 0x00, 0x00, 0x20, 0xFF, 0x3F, 0xFE, 0xFF, 0xBF, 0x7F, 0xFE, 0xFF, 0xD0, 0x38, 0x00, 0x00, 0x04, 0x39, 0x00, 0x00, 0x00, 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0xE0, 0xED, 0x14, 0xB5, 0x04, 0x04, 0x30, 0x00, 0xE0, 0xC7, 0x09, 0x05, 0x30, 0x00, 0xE0, 0x96, 0x0C, 0xAE, 0x04, 0xFF, 0xE3, 0xF5, 0xFC, 0x83, 0x6C, 0x01, 0x30, 0x07, 0x31, 0x00, 0xE0, 0xDE, 0x14, 0xA6, 0x04, 0x69, 0x13, 0x60, 0x93, 0xE0, 0x31, 0x29, 0x41, 0xC4, 0x68, 0x40, 0x3B, 0x9F, 0x08, 0x65, 0x13, 0x20, 0x93, 0x23, 0x49, 0x21, 0xE4, 0x07, 0x20, 0x40, 0x83, 0x42, 0xE4, 0x07, 0x30, 0x84, 0x6C, 0x40, 0xA3, 0x94, 0x04, 0x12, 0x30, 0x00, 0xE0, 0x12, 0x08, 0x03, 0x30, 0x00, 0xE0, 0xA3, 0x09, 0x05, 0x30, 0x00, 0xE0, 0x72, 0x0C, 0x7B, 0x12, 0x04, 0x83, 0x05, 0x38, 0x87, 0x08, 0x00, 0xE0, 0x6C, 0x07, 0x84, 0x04, 0x78, 0x12, 0x41, 0x83, 0x42, 0xE4, 0x3C, 0x30, 0x41, 0xA3, 0x60, 0x83, 0x63, 0xE4, 0x07, 0x20, 0x40, 0x3B, 0x13, 0x08, 0xFF, 0xE3, 0xC9, 0xFC, 0x03, 0x6D, 0x09, 0x30, 0x00, 0xE0, 0xF5, 0x07, 0x70, 0x12, 0x22, 0x84, 0x26, 0x41, 0x41, 0x8B, 0x42, 0xE4, 0x3F, 0x20, 0x84, 0x6C, 0x41, 0xAB, 0x00, 0x30, 0x00, 0xE0, 0x7E, 0x09, 0x63, 0x04, 0x41, 0x3B, 0x0A, 0x08, 0xFF, 0xE3, 0xB5, 0xFC, 0x0A, 0x30, 0x00, 0xE0, 0xE2, 0x07, 0x01, 0x30, 0x00, 0xE0, 0x73, 0x09, 0x58, 0x04, 0x42, 0x3B, 0x4E, 0x08, 0xFF, 0xE3, 0xAA, 0xFC, 0x03, 0x6D, 0x62, 0x80, 0x42, 0x12, 0x6C, 0x5A, 0xA0, 0x83, 0x00, 0xE0, 0x81, 0x04, 0x41, 0x38, 0x19, 0x08, 0x28, 0x33, 0xD6, 0x64, 0x0B, 0x08, 0x7C, 0x11, 0x05, 0x32, 0x44, 0xA3, 0x40, 0x8B, 0x42, 0xE4, 0x80, 0x33, 0x42, 0xEC, 0x80, 0x02, 0x40, 0xAB, 0x25, 0x04, 0x54, 0x3D, 0x23, 0x08, 0x76, 0x11, 0x04, 0x32, 0x44, 0xA3, 0x40, 0x8B, 0x42, 0xE4, 0x80, 0x33, 0xA9, 0x3A, 0x40, 0xAB, 0x1A, 0x04, 0x4F, 0x3D, 0x0A, 0x08, 0x71, 0x11, 0x01, 0x32, 0x44, 0xA3, 0x40, 0x8B, 0x42, 0xE4, 0x80, 0x33, 0xA8, 0x3A, 0x40, 0xAB, 0x0F, 0x04, 0x54, 0x3D, 0x04, 0x0C, 0x28, 0x33, 0xD6, 0x64, 0x0A, 0x08, 0x6A, 0x11, 0x02, 0x32, 0x44, 0xA3, 0x40, 0x8B, 0x42, 0xE4, 0x80, 0x33, 0x42, 0xEC, 0x80, 0x01, 0x40, 0xAB, 0x65, 0x11, 0x22, 0x84, 0x21, 0xE4, 0x0F, 0x20, 0x22, 0x41, 0x42, 0x83, 0x42, 0xE4, 0x3C, 0x30, 0x84, 0x6C, 0x42, 0xA3, 0x0B, 0x30, 0x00, 0xE0, 0x93, 0x07, 0x02, 0x30, 0x00, 0xE0, 0x24, 0x09, 0x09, 0x04, 0x45, 0x3B, 0x07, 0x08, 0x4C, 0x30, 0x00, 0xE0, 0x8A, 0x07, 0x05, 0x30, 0x00, 0xE0, 0x1B, 0x09, 0x05, 0x30, 0x00, 0xE0, 0xEA, 0x0B, 0xFF, 0xE3, 0x54, 0xFC, 0x76, 0x10, 0x60, 0x83, 0x63, 0xE4, 0x40, 0x20, 0xCC, 0x74, 0x40, 0x3B, 0x40, 0x0C, 0x73, 0x10, 0x60, 0x93, 0x63, 0xE4, 0x07, 0x20, 0x05, 0x3B, 0x3A, 0x08, 0x52, 0x10, 0x62, 0xC4, 0x43, 0x08, 0x60, 0x93, 0x0C, 0x78, 0x00, 0xE0, 0x20, 0x0E, 0x41, 0x38, 0x31, 0x08, 0x6B, 0x10, 0x40, 0x83, 0x86, 0x3A, 0x40, 0xA3, 0x2C, 0x04, 0x00, 0xE0, 0x29, 0x0E, 0x41, 0x38, 0x28, 0x08, 0x67, 0x10, 0x40, 0x83, 0x86, 0x3A, 0x40, 0xA3, 0x23, 0x04, 0x00, 0xE0, 0x32, 0x0E, 0x41, 0x38, 0x1F, 0x08, 0x62, 0x10, 0x40, 0x83, 0x08, 0x04, 0x00, 0x00, 0x38, 0x00, 0x00, 0x20, 0x08, 0x39, 0x00, 0x00, 0xEC, 0x38, 0x00, 0x00, 0x86, 0x3A, 0x40, 0xA3, 0x12, 0x04, 0x00, 0xE0, 0x33, 0x0E, 0x41, 0x38, 0x0E, 0x08, 0x76, 0x10, 0x40, 0x83, 0x86, 0x3A, 0x40, 0xA3, 0x09, 0x04, 0x00, 0xE0, 0x3C, 0x0E, 0x41, 0x38, 0x05, 0x08, 0x71, 0x10, 0x40, 0x83, 0x86, 0x3A, 0x40, 0xA3, 0x92, 0x14, 0x00, 0x00, 0x6E, 0x10, 0x00, 0x93, 0x3C, 0x78, 0x00, 0x00, 0x6C, 0x10, 0x00, 0xE4, 0x07, 0x20, 0x03, 0x40, 0x40, 0x83, 0x42, 0xE4, 0x38, 0x30, 0x80, 0x6C, 0x40, 0xA3, 0x3C, 0x78, 0x67, 0x10, 0x62, 0x83, 0x63, 0xE4, 0x02, 0x20, 0xCC, 0x74, 0x40, 0x3B, 0x03, 0x64, 0x3C, 0x78, 0x63, 0x10, 0x40, 0x83, 0xA6, 0x3A, 0x40, 0xA3, 0x3C, 0x78, 0x00, 0x00, 0x38, 0x00, 0x00, 0x20, 0x00, 0x32, 0x62, 0x10, 0x40, 0xA3, 0x3C, 0x78, 0x3E, 0x00, 0x00, 0x20, 0xD0, 0x14, 0x00, 0x30, 0x00, 0xE0, 0xB2, 0x09, 0xBB, 0x33, 0x0C, 0x64, 0x0D, 0x08, 0x6A, 0x10, 0x60, 0x83, 0x40, 0x3B, 0x09, 0x08, 0x01, 0x32, 0x67, 0x10, 0x40, 0xA3, 0x02, 0x30, 0x00, 0x31, 0x08, 0x32, 0x00, 0xE0, 0xD3, 0x0B, 0x90, 0x14, 0xD0, 0x14, 0x01, 0x30, 0x00, 0xE0, 0x64, 0x0B, 0x90, 0x14, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x20, 0xD0, 0x14, 0x00, 0x74, 0x05, 0x38, 0x59, 0x08, 0x6D, 0x11, 0x03, 0xC4, 0x40, 0x08, 0x60, 0x90, 0x0C, 0x78, 0x6C, 0x11, 0x41, 0x8B, 0x42, 0xE4, 0x3F, 0x20, 0x42, 0xEC, 0x00, 0x39, 0x41, 0xAB, 0xBC, 0x30, 0x00, 0xE0, 0x23, 0x07, 0x49, 0x04, 0x66, 0x11, 0x41, 0x8B, 0x42, 0xE4, 0x3F, 0x20, 0x42, 0xEC, 0xC0, 0x4F, 0x41, 0xAB, 0xBC, 0x30, 0x00, 0xE0, 0x18, 0x07, 0x3E, 0x04, 0x61, 0x11, 0x41, 0x8B, 0x42, 0xE4, 0x3F, 0x20, 0x42, 0xEC, 0x80, 0x66, 0x41, 0xAB, 0x79, 0x30, 0x00, 0xE0, 0x0D, 0x07, 0x33, 0x04, 0x7B, 0x10, 0x41, 0x8B, 0x42, 0xE4, 0x3F, 0x20, 0x3A, 0x10, 0x84, 0x6C, 0x41, 0xAB, 0x19, 0x30, 0x00, 0xE0, 0x02, 0x07, 0x28, 0x04, 0xFF, 0xE3, 0x49, 0xFD, 0x50, 0x38, 0x0F, 0x08, 0x03, 0x30, 0x00, 0xE0, 0x24, 0x06, 0x72, 0x10, 0x41, 0x8B, 0x42, 0xE4, 0x3F, 0x20, 0x42, 0xEC, 0x80, 0x66, 0x41, 0xAB, 0x79, 0x30, 0x00, 0xE0, 0xF0, 0x06, 0x16, 0x04, 0x04, 0x30, 0x00, 0xE0, 0x16, 0x06, 0x6B, 0x10, 0x41, 0x8B, 0x42, 0xE4, 0x3F, 0x20, 0x2A, 0x10, 0x84, 0x6C, 0x41, 0xAB, 0x19, 0x30, 0x00, 0xE0, 0xE2, 0x06, 0x08, 0x04, 0x66, 0x10, 0x41, 0x8B, 0x42, 0xE4, 0x3F, 0x20, 0x26, 0x10, 0x84, 0x6C, 0x41, 0xAB, 0x90, 0x14, 0x00, 0x00, 0x14, 0x39, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, 0x80, 0x88, 0xFF, 0xFF, 0xC0, 0xBB, 0xFF, 0xFF, 0xD0, 0x14, 0x00, 0x74, 0x69, 0x13, 0x64, 0x83, 0x40, 0x3B, 0x2B, 0x08, 0x01, 0x32, 0x67, 0x13, 0x44, 0xA3, 0x40, 0x38, 0x14, 0x08, 0x00, 0xE0, 0xED, 0x05, 0x64, 0x13, 0x42, 0x83, 0x80, 0x3A, 0x81, 0x3A, 0x42, 0xA3, 0x41, 0x83, 0x20, 0x83, 0x21, 0xE4, 0x0F, 0x20, 0x00, 0x30, 0x5F, 0x28, 0x40, 0x6C, 0x20, 0xA3, 0x42, 0xE4, 0x1F, 0x20, 0x41, 0xA3, 0x13, 0x04, 0x41, 0x38, 0x11, 0x08, 0x7A, 0x12, 0x41, 0x83, 0x22, 0x83, 0x80, 0x39, 0x81, 0x39, 0xA0, 0x39, 0x22, 0xA3, 0x42, 0xE4, 0x07, 0x20, 0x41, 0xA3, 0x40, 0x83, 0x42, 0xE4, 0x0F, 0x20, 0x42, 0xEC, 0x30, 0x00, 0x40, 0xA3, 0x90, 0x14, 0x00, 0x00, 0xD0, 0x14, 0x00, 0xE0, 0xF1, 0x08, 0xFA, 0x33, 0x63, 0x43, 0x0C, 0x64, 0x08, 0x0C, 0x6E, 0x12, 0x42, 0x83, 0x82, 0x3A, 0x83, 0x3A, 0xA2, 0x3A, 0x42, 0xA3, 0x0A, 0x04, 0x03, 0xEA, 0xC4, 0x09, 0x0C, 0x64, 0x06, 0x08, 0x68, 0x12, 0x42, 0x83, 0x82, 0x3A, 0x83, 0x3A, 0x42, 0xA3, 0x66, 0x12, 0x62, 0x83, 0x63, 0xE4, 0x0C, 0x20, 0x44, 0x3B, 0x30, 0x08, 0x64, 0x12, 0x61, 0x93, 0x63, 0xE4, 0x0F, 0x20, 0x21, 0x3B, 0x20, 0x08, 0x00, 0xE0, 0x3D, 0x10, 0x40, 0x38, 0x1C, 0x08, 0x7E, 0x11, 0x40, 0x93, 0x48, 0x4A, 0x00, 0x22, 0x42, 0xE4, 0x07, 0x20, 0x21, 0x83, 0x21, 0xE4, 0x07, 0x30, 0x48, 0x6C, 0x21, 0xA3, 0x24, 0x3A, 0x1E, 0x08, 0x78, 0x11, 0x01, 0x93, 0x00, 0xE4, 0x0F, 0x20, 0x00, 0x28, 0x00, 0x74, 0x00, 0xE0, 0x72, 0x0F, 0x73, 0x11, 0x41, 0x83, 0x42, 0xE4, 0x07, 0x30, 0x41, 0xA3, 0x10, 0x04, 0x71, 0x11, 0x61, 0x93, 0x63, 0xE4, 0x0F, 0x20, 0x20, 0x3B, 0x0A, 0x0C, 0x01, 0x30, 0x00, 0xE0, 0x63, 0x0F, 0x06, 0x04, 0x6C, 0x11, 0x41, 0x93, 0x42, 0xE4, 0x0F, 0x30, 0x41, 0xB3, 0x90, 0x14, 0xD0, 0x14, 0x01, 0x74, 0x67, 0x11, 0x60, 0x93, 0x6E, 0x4B, 0x63, 0xE4, 0x03, 0x20, 0x40, 0x3B, 0x04, 0x0C, 0x42, 0x3B, 0x28, 0x08, 0x25, 0x04, 0x62, 0x11, 0x60, 0x93, 0x6B, 0x4B, 0x63, 0xE4, 0x03, 0x20, 0x20, 0x3B, 0x20, 0x08, 0x7F, 0x10, 0x60, 0x93, 0x76, 0x4B, 0x2D, 0x2B, 0x0D, 0x64, 0x1A, 0x0C, 0x00, 0x30, 0x00, 0xE0, 0x22, 0x10, 0x7B, 0x10, 0x40, 0x93, 0x42, 0xEC, 0x0C, 0x00, 0x40, 0xB3, 0x78, 0x10, 0x41, 0x83, 0x42, 0xE4, 0x3F, 0x20, 0x00, 0x31, 0x7F, 0x29, 0x84, 0x6C, 0x41, 0xA3, 0x02, 0x30, 0x00, 0x31, 0xFA, 0x32, 0x42, 0x42, 0x00, 0xE0, 0xB1, 0x0A, 0x03, 0x04, 0xFF, 0xE3, 0x7E, 0xFF, 0x90, 0x14, 0x00, 0x00, 0xD0, 0x14, 0x01, 0x74, 0x6D, 0x10, 0x60, 0x93, 0x6E, 0x4B, 0x63, 0xE4, 0x03, 0x20, 0x40, 0x3B, 0x04, 0x0C, 0x42, 0x3B, 0x2D, 0x08, 0x2A, 0x04, 0x03, 0xEA, 0x11, 0x01, 0x0C, 0x64, 0x28, 0x08, 0xFF, 0xE3, 0xA0, 0xFE, 0x40, 0x38, 0x11, 0x0C, 0x64, 0x10, 0x61, 0x83, 0x63, 0xE4, 0x20, 0x20, 0xCC, 0x74, 0x40, 0x3B, 0x0A, 0x08, 0x05, 0x04, 0x40, 0x00, 0x00, 0x20, 0x00, 0xA0, 0x00, 0x40, 0x64, 0x13, 0x65, 0x83, 0x41, 0x3B, 0x14, 0x08, 0x00, 0x30, 0x00, 0xE0, 0xE3, 0x0F, 0x62, 0x13, 0x40, 0x93, 0x42, 0xEC, 0x0C, 0x00, 0x40, 0xB3, 0x7E, 0x12, 0x41, 0x83, 0x42, 0xE4, 0x3F, 0x20, 0x00, 0x31, 0x7F, 0x29, 0x84, 0x6C, 0x41, 0xA3, 0x03, 0x04, 0xFF, 0xE3, 0x45, 0xFF, 0x90, 0x14, 0x78, 0x12, 0x00, 0x32, 0x46, 0xA3, 0x44, 0xA3, 0x45, 0xA3, 0x02, 0x32, 0x47, 0xA3, 0x22, 0x83, 0x80, 0x39, 0x81, 0x39, 0x84, 0x6C, 0x42, 0xA3, 0x41, 0x83, 0x85, 0x3A, 0x00, 0x31, 0x3F, 0x29, 0x84, 0x6C, 0x41, 0xA3, 0x4B, 0x83, 0x42, 0xE4, 0x01, 0x20, 0x42, 0xEC, 0x1C, 0x00, 0x4B, 0xA3, 0x45, 0x8B, 0x42, 0xE4, 0xFC, 0x31, 0x42, 0xEC, 0x38, 0x00, 0x45, 0xAB, 0x42, 0x93, 0x2B, 0x12, 0x84, 0x68, 0x42, 0xB3, 0x42, 0xE4, 0xC0, 0x3F, 0x44, 0xAB, 0x42, 0xE4, 0x3F, 0x30, 0x48, 0xA3, 0x3C, 0x78, 0x00, 0x00, 0xD4, 0x14, 0x01, 0x75, 0x00, 0xE0, 0xEA, 0x09, 0x41, 0x38, 0x67, 0x08, 0x00, 0xE0, 0xDE, 0x09, 0x43, 0x38, 0x0C, 0x08, 0x01, 0x30, 0xFF, 0xE3, 0xDB, 0xFE, 0x04, 0x30, 0xFF, 0xE3, 0x72, 0xFE, 0x7B, 0x11, 0x41, 0x83, 0xA5, 0x3A, 0x41, 0xA3, 0x4F, 0x04, 0x46, 0x38, 0x4D, 0x08, 0xFF, 0xE3, 0x29, 0xFE, 0x43, 0x6D, 0xC0, 0xE4, 0x07, 0x20, 0xE7, 0x48, 0xE7, 0xE4, 0x07, 0x20, 0xFF, 0xE3, 0x2F, 0xFE, 0x41, 0x38, 0x2C, 0x08, 0x42, 0x3E, 0x12, 0x0C, 0x43, 0x3E, 0x1A, 0x0C, 0x40, 0x3E, 0x3B, 0x08, 0x6F, 0x11, 0x62, 0x83, 0x63, 0xE4, 0x03, 0x20, 0x42, 0x3B, 0x35, 0x08, 0x00, 0x30, 0xFF, 0xE3, 0xB6, 0xFE, 0x01, 0x30, 0xFF, 0xE3, 0x4D, 0xFE, 0x2E, 0x04, 0x68, 0x11, 0x62, 0x83, 0x63, 0xE4, 0x03, 0x20, 0x41, 0x3B, 0x28, 0x08, 0x1F, 0x6C, 0xFF, 0xE3, 0x43, 0xFE, 0x24, 0x04, 0x63, 0x11, 0x62, 0x83, 0x63, 0xE4, 0x03, 0x20, 0x40, 0x3B, 0x1E, 0x08, 0x1F, 0x6C, 0xFF, 0xE3, 0x39, 0xFE, 0x7F, 0x10, 0x41, 0x83, 0xA5, 0x3A, 0x41, 0xA3, 0x16, 0x04, 0xA5, 0xE4, 0x07, 0x30, 0x58, 0x6D, 0x65, 0xE4, 0x80, 0x33, 0x63, 0xE4, 0x07, 0x20, 0x40, 0x3B, 0x04, 0x08, 0x00, 0x30, 0xFF, 0xE3, 0x8E, 0xFE, 0x76, 0x10, 0x62, 0x83, 0x63, 0xE4, 0x03, 0x20, 0x40, 0x3B, 0x04, 0x08, 0x00, 0x30, 0xFF, 0xE3, 0x1F, 0xFE, 0x72, 0x10, 0x42, 0x83, 0x84, 0x3A, 0x85, 0x3A, 0xA5, 0x3A, 0x42, 0xA3, 0x08, 0x30, 0x00, 0xE0, 0x8A, 0x09, 0x50, 0x3C, 0x01, 0x0D, 0x10, 0x3C, 0x0D, 0x08, 0x42, 0x3C, 0x43, 0x0D, 0x02, 0x3C, 0x04, 0x08, 0x41, 0x3C, 0x7D, 0x09, 0x16, 0x05, 0x44, 0x3C, 0x70, 0x0D, 0x48, 0x3C, 0x78, 0x09, 0x1A, 0x04, 0x80, 0x33, 0xD2, 0x64, 0x44, 0x0D, 0x0C, 0x65, 0x0C, 0x0C, 0x40, 0x33, 0xD2, 0x64, 0x6F, 0x09, 0x64, 0x05, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, 0x00, 0xA0, 0x00, 0x40, 0xFF, 0x0F, 0xFC, 0xFF, 0x80, 0x33, 0x61, 0x43, 0xD2, 0x64, 0x59, 0x0D, 0x80, 0x33, 0x62, 0x43, 0xD2, 0x64, 0x5F, 0x09, 0x54, 0x05, 0x00, 0xE0, 0xC7, 0x06, 0x41, 0x38, 0x04, 0x08, 0x00, 0xE0, 0x83, 0x06, 0x57, 0x05, 0x00, 0x30, 0x00, 0xE0, 0x43, 0x07, 0x03, 0x6D, 0x64, 0x13, 0x60, 0x93, 0x43, 0xE4, 0x0F, 0x20, 0x64, 0x4B, 0x63, 0xE4, 0x0F, 0x20, 0x00, 0x2B, 0xCA, 0x64, 0x0A, 0x0C, 0x7F, 0x12, 0x00, 0x93, 0x16, 0x48, 0x11, 0x58, 0x07, 0x40, 0x53, 0x6C, 0x00, 0xE0, 0xA0, 0x14, 0x02, 0x04, 0x00, 0x30, 0xC3, 0x6C, 0x00, 0x32, 0x30, 0x2A, 0x81, 0x64, 0x02, 0x0C, 0xCB, 0x6C, 0x0F, 0x6C, 0x31, 0x32, 0xC9, 0x64, 0x02, 0x0C, 0x31, 0x30, 0x00, 0x74, 0x00, 0xE0, 0x7C, 0x0A, 0x41, 0x38, 0x2F, 0x09, 0x72, 0x12, 0x20, 0x93, 0x00, 0x21, 0x21, 0xE4, 0x0F, 0x20, 0x40, 0x83, 0x42, 0xE4, 0x0F, 0x30, 0x84, 0x6C, 0x40, 0xA3, 0x6E, 0x12, 0x61, 0x93, 0xC0, 0x3B, 0x7E, 0x0C, 0x6B, 0x12, 0x60, 0x93, 0x76, 0x4B, 0x43, 0xE4, 0x16, 0x10, 0x09, 0x65, 0x14, 0x0C, 0x68, 0x12, 0x22, 0x93, 0x4E, 0x41, 0x5A, 0x4A, 0x00, 0x22, 0x42, 0xE4, 0x3F, 0x20, 0x4C, 0x42, 0x06, 0x12, 0x40, 0x68, 0x84, 0x6C, 0x42, 0xB3, 0x42, 0xE4, 0xC0, 0x3F, 0x44, 0xAB, 0x42, 0xE4, 0x3F, 0x30, 0x48, 0xA3, 0x2A, 0x04, 0x43, 0x2B, 0x0D, 0x65, 0x14, 0x0C, 0x7D, 0x11, 0x42, 0x93, 0x3E, 0x11, 0x84, 0x68, 0x42, 0xB3, 0x34, 0x42, 0x3A, 0x49, 0x00, 0x21, 0x21, 0xE4, 0x3F, 0x20, 0x26, 0x41, 0x42, 0xE4, 0xC0, 0x3F, 0x84, 0x6C, 0x44, 0xAB, 0x42, 0xE4, 0x3F, 0x30, 0x48, 0xA3, 0x14, 0x04, 0x73, 0x11, 0x42, 0x93, 0x14, 0x11, 0x80, 0x68, 0x42, 0xB3, 0x42, 0xE4, 0xC0, 0x3F, 0x44, 0xAB, 0x22, 0x93, 0x3A, 0x41, 0x3A, 0x49, 0x00, 0x21, 0x21, 0xE4, 0x3F, 0x20, 0x48, 0x83, 0x42, 0xE4, 0x3F, 0x30, 0x84, 0x6C, 0x48, 0xA3, 0x6A, 0x11, 0x62, 0x93, 0x6E, 0x43, 0x7A, 0x4B, 0x2F, 0x3B, 0x09, 0x08, 0x67, 0x11, 0x4B, 0x83, 0x42, 0xE4, 0x01, 0x20, 0x42, 0xEC, 0x1C, 0x00, 0x4B, 0xA3, 0x1B, 0x04, 0x63, 0x11, 0x62, 0x93, 0x74, 0x43, 0x7A, 0x4B, 0x2F, 0x3B, 0x09, 0x08, 0x60, 0x11, 0x4B, 0x83, 0x42, 0xE4, 0x01, 0x20, 0x42, 0xEC, 0x1E, 0x00, 0x4B, 0xA3, 0x0D, 0x04, 0x7C, 0x10, 0x62, 0x93, 0x7A, 0x43, 0x7A, 0x4B, 0x2F, 0x3B, 0x07, 0x08, 0x79, 0x10, 0x4B, 0x83, 0x42, 0xE4, 0x01, 0x20, 0xA5, 0x3A, 0x4B, 0xA3, 0x76, 0x10, 0x62, 0x93, 0x19, 0x4B, 0x67, 0x43, 0x79, 0x4B, 0x0E, 0x64, 0x03, 0x0C, 0x00, 0xE0, 0x34, 0x04, 0x71, 0x10, 0x22, 0x93, 0x39, 0x49, 0x22, 0x41, 0x45, 0x8B, 0x42, 0xE4, 0xFC, 0x31, 0x84, 0x6C, 0x45, 0xAB, 0x08, 0x30, 0x00, 0xE0, 0x18, 0x09, 0x6B, 0x10, 0x67, 0x83, 0x41, 0x3B, 0x0D, 0x08, 0x69, 0x10, 0x42, 0x83, 0x84, 0x3A, 0x85, 0x3A, 0xA4, 0x3A, 0x42, 0xA3, 0x07, 0x30, 0x00, 0x31, 0xB4, 0x32, 0x00, 0xE0, 0xE9, 0x08, 0x91, 0x04, 0x07, 0x30, 0x00, 0x31, 0x96, 0x32, 0x00, 0xE0, 0xE3, 0x08, 0x8B, 0x04, 0x40, 0x00, 0x00, 0x20, 0x00, 0x50, 0x00, 0x40, 0xFF, 0x0F, 0xFC, 0xFF, 0x7F, 0x12, 0x00, 0x93, 0x10, 0x48, 0x00, 0xE4, 0x03, 0x20, 0x00, 0xE0, 0xE1, 0x09, 0x40, 0x38, 0x7C, 0x0C, 0x08, 0x30, 0x00, 0xE0, 0xF0, 0x08, 0x79, 0x12, 0x62, 0x83, 0x63, 0xE4, 0x03, 0x20, 0x40, 0x3B, 0x07, 0x08, 0x07, 0x30, 0x00, 0x31, 0x96, 0x32, 0x00, 0xE0, 0xC5, 0x08, 0x6D, 0x04, 0x73, 0x12, 0x42, 0x83, 0x84, 0x3A, 0x85, 0x3A, 0x42, 0xA3, 0x67, 0x04, 0x70, 0x12, 0x62, 0x83, 0x63, 0xE4, 0x30, 0x20, 0x40, 0x3B, 0x0D, 0x08, 0x00, 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0xE0, 0x99, 0x10, 0x6B, 0x12, 0x42, 0x83, 0x84, 0x3A, 0x85, 0x3A, 0xA5, 0x3A, 0x42, 0xA3, 0x55, 0x04, 0x50, 0x3B, 0x0F, 0x08, 0xFF, 0xE3, 0xA0, 0xF8, 0x83, 0x6C, 0x01, 0x30, 0x1F, 0x31, 0x00, 0xE0, 0x89, 0x10, 0x63, 0x12, 0x42, 0x83, 0x84, 0x3A, 0x85, 0x3A, 0xA5, 0x3A, 0x42, 0xA3, 0x45, 0x04, 0x04, 0x30, 0x00, 0x31, 0x14, 0x32, 0x00, 0xE0, 0x97, 0x08, 0x3F, 0x04, 0x0D, 0x30, 0x00, 0xE0, 0xC3, 0x03, 0x07, 0x30, 0x00, 0x31, 0x4B, 0x32, 0x00, 0xE0, 0x8E, 0x08, 0x04, 0x30, 0x00, 0x31, 0x05, 0x32, 0x00, 0xE0, 0x89, 0x08, 0x31, 0x04, 0x75, 0x11, 0x61, 0x83, 0x63, 0xE4, 0x18, 0x20, 0x40, 0x3B, 0x0F, 0x08, 0x72, 0x11, 0x41, 0x83, 0x83, 0x3A, 0x84, 0x3A, 0xA3, 0x3A, 0x41, 0xA3, 0x40, 0x83, 0x42, 0xE4, 0x0F, 0x20, 0x00, 0x31, 0x5F, 0x29, 0x84, 0x6C, 0x40, 0xA3, 0x13, 0x04, 0x48, 0x3B, 0x11, 0x08, 0x6A, 0x11, 0x41, 0x83, 0x83, 0x3A, 0x84, 0x3A, 0xA4, 0x3A, 0x41, 0xA3, 0x40, 0x83, 0x42, 0xE4, 0x0F, 0x20, 0x00, 0x30, 0x5F, 0x28, 0x80, 0x6C, 0x40, 0xA3, 0x00, 0x30, 0x00, 0xE0, 0xD4, 0x03, 0x07, 0x30, 0x00, 0x31, 0x4B, 0x32, 0x00, 0xE0, 0x5D, 0x08, 0x04, 0x30, 0x00, 0x31, 0x05, 0x32, 0x00, 0xE0, 0x58, 0x08, 0x94, 0x14, 0x00, 0x00, 0xD0, 0x14, 0x7C, 0x10, 0x60, 0x93, 0x43, 0xE4, 0x0F, 0x20, 0x64, 0x4B, 0x63, 0xE4, 0x0F, 0x20, 0xC9, 0x64, 0x0A, 0x08, 0x77, 0x10, 0x40, 0x83, 0x42, 0xE4, 0x0F, 0x30, 0x40, 0xA3, 0x10, 0x30, 0x00, 0xE0, 0xF0, 0x07, 0x04, 0x04, 0x08, 0x30, 0x00, 0xE0, 0xEC, 0x07, 0x90, 0x14, 0x00, 0x00, 0xD1, 0x14, 0x70, 0x10, 0x60, 0x93, 0x6E, 0x4B, 0x63, 0xE4, 0x0C, 0x20, 0x4E, 0x10, 0x6C, 0x5A, 0x80, 0x93, 0x00, 0x30, 0x00, 0xE0, 0xCA, 0x05, 0xD1, 0x7B, 0x08, 0x30, 0x01, 0x31, 0x05, 0x32, 0x00, 0xE0, 0x2C, 0x08, 0x91, 0x14, 0x00, 0x00, 0x66, 0x10, 0x00, 0x93, 0x12, 0x48, 0x00, 0xE4, 0x03, 0x20, 0x3C, 0x78, 0x63, 0x10, 0x00, 0x93, 0x10, 0x48, 0x00, 0xE4, 0x03, 0x20, 0x3C, 0x78, 0x40, 0x00, 0x00, 0x20, 0x2C, 0x39, 0x00, 0x00, 0x7C, 0x10, 0x00, 0x93, 0x0E, 0x48, 0x00, 0xE4, 0x03, 0x20, 0x3C, 0x78, 0x79, 0x10, 0x00, 0x93, 0x0B, 0x48, 0x00, 0xE4, 0x03, 0x20, 0x3C, 0x78, 0xD1, 0x14, 0x76, 0x10, 0x60, 0x93, 0x6E, 0x4B, 0x63, 0xE4, 0x0C, 0x20, 0x54, 0x10, 0x6C, 0x5A, 0x80, 0x93, 0x00, 0x30, 0x00, 0xE0, 0x9A, 0x05, 0xD1, 0x7B, 0x91, 0x14, 0xD0, 0x14, 0x6F, 0x10, 0x62, 0x83, 0x63, 0xE4, 0x03, 0x20, 0x41, 0x3B, 0x04, 0x08, 0x08, 0x30, 0x00, 0xE0, 0x26, 0x03, 0x90, 0x14, 0x00, 0x00, 0xD0, 0x14, 0x00, 0x74, 0x4C, 0x38, 0x03, 0x0C, 0x49, 0x38, 0x0C, 0x08, 0x66, 0x10, 0x01, 0x32, 0x45, 0xA3, 0x62, 0x83, 0x63, 0xE4, 0x03, 0x20, 0x41, 0x3B, 0x04, 0x0C, 0x00, 0x30, 0xFF, 0xE3, 0x1B, 0xFC, 0x90, 0x14, 0x40, 0x00, 0x00, 0x20, 0x2C, 0x39, 0x00, 0x00, 0xC3, 0x6C, 0x84, 0x74, 0x41, 0x3A, 0x0C, 0x0C, 0x22, 0x58, 0x01, 0x2A, 0x88, 0x74, 0x28, 0x59, 0x00, 0x30, 0x40, 0x83, 0x09, 0x6C, 0x00, 0x23, 0x4E, 0x64, 0xFC, 0x0B, 0x02, 0x04, 0x00, 0x30, 0x3C, 0x78, 0x00, 0x00, 0xD1, 0x14, 0x03, 0x6D, 0xC4, 0x74, 0x48, 0x74, 0x40, 0x38, 0x09, 0x0C, 0x02, 0x32, 0x40, 0xA0, 0x61, 0xA0, 0xFF, 0xE3, 0xE5, 0xFF, 0x02, 0xA4, 0x01, 0x30, 0x02, 0x04, 0x00, 0x30, 0x91, 0x14, 0xD1, 0x14, 0x03, 0x6D, 0xC4, 0x74, 0x48, 0x74, 0x40, 0x38, 0x09, 0x0C, 0x01, 0x32, 0x40, 0xA0, 0x61, 0xA0, 0xFF, 0xE3, 0xD5, 0xFF, 0x02, 0xA4, 0x01, 0x30, 0x02, 0x04, 0x00, 0x30, 0x91, 0x14, 0xD2, 0x14, 0x22, 0x14, 0x03, 0x6D, 0x44, 0x75, 0x01, 0x1B, 0x00, 0x32, 0x40, 0xA3, 0x6E, 0x31, 0x21, 0xA3, 0x4E, 0xDC, 0x00, 0x00, 0x4E, 0xDC, 0x01, 0x00, 0x00, 0x32, 0x38, 0x2A, 0x4E, 0xDC, 0x02, 0x00, 0x3D, 0x32, 0x4E, 0xDC, 0x03, 0x00, 0x40, 0x38, 0x16, 0x0C, 0x71, 0x33, 0x60, 0xA0, 0x12, 0x33, 0x61, 0xA0, 0x01, 0x20, 0x01, 0x19, 0x02, 0x32, 0xFF, 0xE3, 0x75, 0xF7, 0x0E, 0x5C, 0x7B, 0x6C, 0x04, 0x32, 0xFF, 0xE3, 0x70, 0xF7, 0x13, 0x6C, 0x57, 0x6C, 0xFF, 0xE3, 0xA8, 0xFF, 0x08, 0xA4, 0x01, 0x30, 0x02, 0x04, 0x00, 0x30, 0x02, 0x14, 0x92, 0x14, 0xD1, 0x14, 0x03, 0x6D, 0xC4, 0x74, 0x48, 0x74, 0x40, 0x38, 0x09, 0x0C, 0x06, 0x32, 0x40, 0xA0, 0x61, 0xA0, 0xFF, 0xE3, 0x97, 0xFF, 0x02, 0xA4, 0x01, 0x30, 0x02, 0x04, 0x00, 0x30, 0x91, 0x14, 0xD1, 0x14, 0x03, 0x6D, 0x44, 0x74, 0x40, 0x38, 0x14, 0x0C, 0x51, 0x33, 0x60, 0xA0, 0x0A, 0x33, 0x61, 0xA0, 0x00, 0x33, 0x62, 0xA0, 0x00, 0x33, 0x7F, 0x2B, 0x63, 0xA0, 0x2A, 0x33, 0x64, 0xA0, 0x00, 0x33, 0x4F, 0x2B, 0x65, 0xA0, 0xFF, 0xE3, 0x7D, 0xFF, 0x06, 0xA4, 0x01, 0x30, 0x02, 0x04, 0x00, 0x30, 0x91, 0x14, 0xD1, 0x14, 0x03, 0x6D, 0x44, 0x74, 0x88, 0x74, 0x40, 0x38, 0x0B, 0x0C, 0x20, 0x33, 0x60, 0xA0, 0x21, 0xA0, 0x42, 0xA0, 0x04, 0x31, 0xFF, 0xE3, 0x6B, 0xFF, 0x03, 0xA4, 0x01, 0x30, 0x02, 0x04, 0x00, 0x30, 0x91, 0x14, 0xD1, 0x14, 0x03, 0x6D, 0x44, 0x74, 0x40, 0x38, 0x0A, 0x0C, 0x07, 0x33, 0x60, 0xA0, 0x21, 0xA0, 0x03, 0x31, 0xFF, 0xE3, 0x5B, 0xFF, 0x02, 0xA4, 0x01, 0x30, 0x02, 0x04, 0x00, 0x30, 0x91, 0x14, 0xD1, 0x14, 0x03, 0x6D, 0x44, 0x74, 0x88, 0x74, 0x40, 0x38, 0x0B, 0x0C, 0x22, 0x33, 0x60, 0xA0, 0x21, 0xA0, 0x42, 0xA0, 0x04, 0x31, 0xFF, 0xE3, 0x49, 0xFF, 0x03, 0xA4, 0x01, 0x30, 0x02, 0x04, 0x00, 0x30, 0x91, 0x14, 0xD1, 0x14, 0x03, 0x6D, 0x44, 0x74, 0x89, 0x74, 0x40, 0x38, 0x0D, 0x0C, 0x31, 0x33, 0x60, 0xA0, 0x21, 0xA0, 0x68, 0x4A, 0x62, 0xA0, 0x43, 0xA0, 0x05, 0x31, 0xFF, 0xE3, 0x35, 0xFF, 0x04, 0xA4, 0x01, 0x30, 0x02, 0x04, 0x00, 0x30, 0x91, 0x14, 0xD1, 0x14, 0x03, 0x6D, 0x44, 0x74, 0x40, 0x38, 0x0A, 0x0C, 0x04, 0x33, 0x60, 0xA0, 0x21, 0xA0, 0x03, 0x31, 0xFF, 0xE3, 0x25, 0xFF, 0x02, 0xA4, 0x01, 0x30, 0x02, 0x04, 0x00, 0x30, 0x91, 0x14, 0xD1, 0x14, 0x03, 0x6D, 0x44, 0x74, 0x40, 0x38, 0x0A, 0x0C, 0x03, 0x33, 0x60, 0xA0, 0x21, 0xA0, 0x03, 0x31, 0xFF, 0xE3, 0x15, 0xFF, 0x02, 0xA4, 0x01, 0x30, 0x02, 0x04, 0x00, 0x30, 0x91, 0x14, 0xD1, 0x14, 0x03, 0x6D, 0x38, 0x33, 0x60, 0xA0, 0x3B, 0x33, 0x61, 0xA0, 0x02, 0x33, 0x62, 0xA0, 0x00, 0x33, 0x44, 0x2B, 0x63, 0xA0, 0x05, 0x31, 0xFF, 0xE3, 0x02, 0xFF, 0x04, 0xA4, 0x91, 0x14, 0xD3, 0x14, 0x22, 0x14, 0x03, 0x6D, 0x01, 0x1D, 0xBB, 0x6D, 0x17, 0x6C, 0x7B, 0x6C, 0x00, 0xE0, 0xD3, 0x0A, 0x00, 0x33, 0x5B, 0x2B, 0x60, 0xA4, 0x08, 0x33, 0x61, 0xA4, 0x60, 0x95, 0x62, 0xA4, 0x48, 0x4B, 0x43, 0xA4, 0x50, 0x4B, 0x44, 0xA4, 0x78, 0x4B, 0x65, 0xA4, 0x60, 0x98, 0x66, 0xA4, 0x48, 0x4B, 0x47, 0xA4, 0x50, 0x4B, 0x48, 0xA4, 0x78, 0x4B, 0x69, 0xA4, 0x00, 0x33, 0x6A, 0xA4, 0x6B, 0xA4, 0x6C, 0xA4, 0x13, 0x6C, 0x0B, 0x31, 0xFF, 0xE3, 0xDA, 0xFE, 0x0D, 0xA4, 0x02, 0x14, 0x93, 0x14, 0x00, 0x00, 0xD1, 0x14, 0x03, 0x6D, 0x18, 0x33, 0x60, 0xA0, 0x0B, 0x33, 0x61, 0xA0, 0x03, 0x31, 0xFF, 0xE3, 0xCD, 0xFE, 0x02, 0xA4, 0x91, 0x14, 0x00, 0x00, 0xD1, 0x14, 0x03, 0x6D, 0x38, 0x33, 0x60, 0xA0, 0x0A, 0x33, 0x61, 0xA0, 0x28, 0x33, 0x62, 0xA0, 0x23, 0x33, 0x63, 0xA0, 0x05, 0x31, 0xFF, 0xE3, 0xBD, 0xFE, 0x04, 0xA4, 0x91, 0x14, 0x00, 0x00, 0xD1, 0x14, 0x03, 0x6D, 0x72, 0x11, 0x60, 0x93, 0x03, 0xC4, 0x83, 0x70, 0x60, 0xA0, 0x38, 0x32, 0x8E, 0x64, 0x25, 0x0C, 0xC8, 0x64, 0x07, 0x0C, 0x58, 0x3B, 0x0C, 0x0C, 0x28, 0x32, 0x8E, 0x64, 0x50, 0x08, 0x11, 0x04, 0x48, 0x32, 0x8E, 0x64, 0x28, 0x0C, 0x58, 0x32, 0x8E, 0x64, 0x49, 0x08, 0x35, 0x04, 0x67, 0x11, 0x60, 0x93, 0x70, 0x4B, 0x61, 0xA0, 0x03, 0x31, 0xFF, 0xE3, 0x9A, 0xFE, 0x02, 0xA4, 0x3F, 0x04, 0x62, 0x11, 0x40, 0x93, 0x50, 0x4A, 0x41, 0xA0, 0x60, 0x93, 0x78, 0x4B, 0x62, 0xA0, 0x04, 0x31, 0xFF, 0xE3, 0x8E, 0xFE, 0x03, 0xA4, 0x33, 0x04, 0x7C, 0x10, 0x40, 0x93, 0x50, 0x4A, 0x41, 0xA0, 0x40, 0x93, 0x58, 0x4A, 0x42, 0xA0, 0x61, 0x93, 0x63, 0xA0, 0x05, 0x31, 0xFF, 0xE3, 0x80, 0xFE, 0x04, 0xA4, 0x25, 0x04, 0x75, 0x10, 0x40, 0x93, 0x50, 0x4A, 0x41, 0xA0, 0x40, 0x93, 0x58, 0x4A, 0x42, 0xA0, 0x41, 0x93, 0x43, 0xA0, 0x61, 0x93, 0x68, 0x4B, 0x64, 0xA0, 0x06, 0x31, 0xFF, 0xE3, 0x6F, 0xFE, 0x05, 0xA4, 0x14, 0x04, 0x6D, 0x10, 0x40, 0x93, 0x50, 0x4A, 0x41, 0xA0, 0x40, 0x93, 0x58, 0x4A, 0x42, 0xA0, 0x41, 0x93, 0x43, 0xA0, 0x41, 0x93, 0x48, 0x4A, 0x44, 0xA0, 0x61, 0x93, 0x70, 0x4B, 0x65, 0xA0, 0x07, 0x31, 0xFF, 0xE3, 0x5B, 0xFE, 0x06, 0xA4, 0x63, 0x10, 0x00, 0x93, 0x00, 0xC4, 0x80, 0x70, 0x04, 0x48, 0x91, 0x14, 0x00, 0x90, 0x00, 0x40, 0xD4, 0x14, 0xC7, 0x6D, 0x80, 0x75, 0x03, 0x30, 0x00, 0xE0, 0xC0, 0x03, 0x03, 0x6D, 0x00, 0x30, 0x00, 0xE0, 0xBC, 0x03, 0x43, 0x6D, 0x43, 0x44, 0x66, 0x44, 0x6C, 0x5A, 0x71, 0x5B, 0x42, 0x43, 0x6D, 0x5A, 0x43, 0x43, 0x4D, 0x5A, 0x44, 0xC4, 0x42, 0x08, 0x54, 0x4A, 0x80, 0xC4, 0x23, 0x84, 0x23, 0x43, 0x06, 0x43, 0x25, 0x58, 0x6D, 0x59, 0x6A, 0x4B, 0x08, 0x7D, 0x90, 0x5B, 0x00, 0xE0, 0xC3, 0x0A, 0x40, 0x7D, 0xA5, 0xC4, 0x25, 0x08, 0x64, 0x45, 0xB5, 0x5B, 0xAB, 0x4D, 0x94, 0x5C, 0xFA, 0x33, 0x63, 0x43, 0x0C, 0x65, 0x23, 0x08, 0xFA, 0x33, 0x64, 0x43, 0x0C, 0x65, 0x21, 0x08, 0x03, 0xEA, 0x70, 0x17, 0x0C, 0x65, 0x1F, 0x08, 0xFA, 0x33, 0x65, 0x43, 0x0C, 0x65, 0x1D, 0x08, 0x03, 0xEA, 0x10, 0x27, 0x0C, 0x65, 0x1B, 0x08, 0x63, 0xE4, 0xCF, 0x07, 0x0C, 0x65, 0x19, 0x08, 0x63, 0xE4, 0xCF, 0x07, 0x0C, 0x65, 0x17, 0x08, 0xFA, 0x33, 0x66, 0x43, 0x0C, 0x65, 0x15, 0x08, 0x03, 0xEA, 0x50, 0x46, 0x0C, 0x65, 0xC3, 0x64, 0x07, 0x23, 0x10, 0x04, 0x00, 0x33, 0x0E, 0x04, 0x01, 0x33, 0x0C, 0x04, 0x02, 0x33, 0x0A, 0x04, 0x03, 0x33, 0x08, 0x04, 0x04, 0x33, 0x06, 0x04, 0x05, 0x33, 0x04, 0x04, 0x06, 0x33, 0x02, 0x04, 0x07, 0x33, 0x52, 0x11, 0x62, 0xC4, 0x42, 0x08, 0x40, 0x82, 0xC0, 0x3A, 0x0A, 0x0C, 0x4F, 0x11, 0x62, 0xC4, 0x43, 0x08, 0x40, 0x8B, 0x41, 0x4A, 0x08, 0x7D, 0x8A, 0x4C, 0x61, 0x8B, 0x8C, 0x5C, 0x40, 0x3E, 0x2A, 0x08, 0x84, 0xE4, 0xE7, 0x03, 0x68, 0x11, 0x63, 0xD8, 0x28, 0x00, 0x40, 0x3B, 0x08, 0x08, 0x63, 0x44, 0xA6, 0x44, 0x6D, 0x5D, 0xA4, 0x43, 0xAD, 0x5D, 0xB1, 0x5D, 0x12, 0x04, 0x41, 0x3B, 0x08, 0x08, 0xA4, 0x44, 0x66, 0x44, 0x6C, 0x5D, 0xA3, 0x43, 0xAD, 0x5D, 0xB1, 0x5D, 0x09, 0x04, 0x42, 0x3B, 0x07, 0x08, 0x62, 0x44, 0x45, 0x44, 0x6D, 0x5A, 0xA4, 0x43, 0x6D, 0x5D, 0xB1, 0x5B, 0xAF, 0x4D, 0x54, 0x75, 0xA0, 0xA7, 0xFF, 0x33, 0xD6, 0x64, 0x2C, 0x08, 0x00, 0x33, 0x00, 0x2B, 0x60, 0xA7, 0x28, 0x04, 0x41, 0x3E, 0x26, 0x08, 0x74, 0x10, 0x63, 0xD8, 0x28, 0x00, 0x43, 0x3B, 0x0A, 0x08, 0xA7, 0x44, 0x85, 0xC4, 0x45, 0x08, 0xB1, 0x5D, 0xA4, 0x45, 0xB0, 0x5D, 0xA4, 0xC4, 0x45, 0x08, 0x16, 0x04, 0x44, 0x3B, 0x0A, 0x08, 0x63, 0x44, 0x46, 0x44, 0x6D, 0x5A, 0xA4, 0x43, 0xAD, 0x5D, 0xB1, 0x5D, 0xA3, 0x45, 0xB1, 0x5D, 0x0B, 0x04, 0x45, 0x3B, 0x09, 0x08, 0x65, 0x44, 0x47, 0x44, 0x6D, 0x5A, 0xA3, 0x43, 0xAD, 0x5D, 0xB1, 0x5D, 0xA5, 0xC4, 0x45, 0x08, 0xAB, 0x4D, 0xA0, 0xAF, 0x94, 0x14, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x20, 0x74, 0x11, 0x03, 0xDC, 0x28, 0x00, 0x3C, 0x78, 0x00, 0x74, 0x45, 0x74, 0x89, 0x74, 0x67, 0x58, 0xCC, 0x74, 0x13, 0x3B, 0x10, 0x08, 0x01, 0x48, 0x00, 0x28, 0x6E, 0x11, 0x03, 0xC4, 0x40, 0x08, 0x60, 0x80, 0xA0, 0x3B, 0x60, 0xA0, 0x21, 0x41, 0x60, 0x88, 0x63, 0xE4, 0x01, 0x20, 0x4C, 0x6C, 0x20, 0xA8, 0x41, 0xA8, 0x3C, 0x78, 0x00, 0x00, 0xD1, 0x14, 0x86, 0x11, 0x00, 0x33, 0x64, 0xDC, 0x28, 0x00, 0x13, 0x6C, 0x00, 0x31, 0x28, 0x32, 0xFF, 0xE3, 0x20, 0xF5, 0x60, 0x84, 0x01, 0x32, 0xC8, 0x6C, 0x60, 0xA4, 0x20, 0x8C, 0x01, 0x33, 0x4C, 0x68, 0xAB, 0x39, 0x20, 0xAC, 0x91, 0x31, 0x22, 0x41, 0x21, 0xAC, 0x24, 0x84, 0x48, 0x6C, 0x24, 0xA4, 0x22, 0x8C, 0x4C, 0x68, 0x21, 0xEC, 0x4C, 0x07, 0x22, 0xAC, 0xDA, 0x31, 0x22, 0x41, 0x23, 0xAC, 0x28, 0x84, 0x48, 0x6C, 0x28, 0xA4, 0x24, 0x8C, 0x4C, 0x68, 0x21, 0xEC, 0x30, 0x09, 0x24, 0xAC, 0xC2, 0x31, 0x25, 0xAC, 0x2C, 0x84, 0x48, 0x6C, 0x2C, 0xA4, 0x26, 0x8C, 0x4C, 0x68, 0x21, 0xEC, 0xD6, 0x07, 0x26, 0xAC, 0xEE, 0x31, 0x22, 0x41, 0x27, 0xAC, 0x30, 0x84, 0x48, 0x6C, 0x30, 0xA4, 0x28, 0x8C, 0x4C, 0x68, 0x21, 0xEC, 0x16, 0x08, 0x28, 0xAC, 0x01, 0xEA, 0x36, 0x03, 0x29, 0xAC, 0x34, 0x84, 0x84, 0x6C, 0x54, 0xA4, 0x4A, 0x8C, 0xC8, 0x68, 0xAB, 0x3B, 0x6A, 0xAC, 0x03, 0xEA, 0x01, 0x04, 0x6B, 0xAC, 0x91, 0x14, 0x4C, 0x00, 0x00, 0x20, 0x68, 0x10, 0x40, 0x93, 0xA1, 0x3A, 0x40, 0xB3, 0x14, 0x33, 0x03, 0x6C, 0x00, 0x2B, 0xCC, 0x74, 0x40, 0x3B, 0xFC, 0x0B, 0x63, 0x10, 0x40, 0x93, 0x81, 0x3A, 0x40, 0xB3, 0x3C, 0x78, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x40, 0xD0, 0x14, 0x01, 0x13, 0x00, 0x33, 0x60, 0xA0, 0x61, 0xA0, 0x62, 0xA0, 0x01, 0x32, 0x43, 0xA0, 0x64, 0xA0, 0x65, 0xA0, 0x66, 0xA0, 0x67, 0xA0, 0x68, 0xA0, 0x0B, 0x20, 0x00, 0x31, 0x0C, 0x32, 0xFF, 0xE3, 0xB8, 0xF4, 0xB2, 0x30, 0x00, 0xE0, 0xF7, 0x0E, 0x06, 0x30, 0x01, 0x31, 0x0B, 0x32, 0x00, 0xE0, 0xD6, 0x04, 0x00, 0xE0, 0xEA, 0x0E, 0x90, 0x14, 0x00, 0x00, 0xD0, 0x14, 0x00, 0x74, 0x71, 0x12, 0x25, 0x83, 0x42, 0x59, 0x42, 0xE4, 0x07, 0x20, 0x64, 0x83, 0x8E, 0x64, 0x19, 0x0C, 0x6D, 0x12, 0x24, 0x5B, 0x18, 0xA1, 0x45, 0xA3, 0x4D, 0x38, 0x09, 0x08, 0xFF, 0xE3, 0xC0, 0xF4, 0x43, 0x6C, 0x0A, 0x12, 0x06, 0x32, 0xFF, 0xE3, 0xA3, 0xF4, 0x0B, 0x04, 0x4C, 0x33, 0xC2, 0x64, 0x08, 0x08, 0xFF, 0xE3, 0xB5, 0xF4, 0x43, 0x6C, 0x05, 0x12, 0x06, 0x32, 0xFF, 0xE3, 0x98, 0xF4, 0x90, 0x14, 0x00, 0x00, 0x63, 0x12, 0x43, 0x93, 0x88, 0x74, 0xAA, 0x33, 0xCA, 0x64, 0x05, 0x08, 0x01, 0x32, 0x7D, 0x11, 0x43, 0xA3, 0x0A, 0x04, 0x7E, 0x11, 0x43, 0x93, 0x88, 0x74, 0x55, 0x33, 0xCA, 0x64, 0x04, 0x08, 0x01, 0x32, 0x78, 0x11, 0x40, 0xA3, 0x3C, 0x78, 0x01, 0x32, 0x76, 0x11, 0x48, 0xA3, 0x3C, 0x78, 0xD0, 0x14, 0x00, 0x74, 0x00, 0xE0, 0xAE, 0x0E, 0x90, 0x14, 0x00, 0x00, 0x00, 0x74, 0x71, 0x11, 0x01, 0x32, 0x02, 0xC4, 0x20, 0x40, 0x41, 0x83, 0x08, 0x6C, 0x01, 0xA3, 0x46, 0x83, 0xA0, 0x3A, 0x46, 0xA3, 0x3C, 0x78, 0xD2, 0x14, 0x6B, 0x11, 0x63, 0x83, 0x41, 0x3B, 0x92, 0x08, 0x69, 0x11, 0x00, 0x32, 0x47, 0xA3, 0x44, 0x83, 0x65, 0x83, 0x8E, 0x64, 0xA5, 0x0C, 0x25, 0x11, 0x68, 0x59, 0x78, 0x83, 0x00, 0x22, 0x42, 0xE4, 0x07, 0x20, 0x44, 0xA1, 0x45, 0x11, 0x2C, 0x92, 0x21, 0xE4, 0xFF, 0x30, 0x4C, 0x6C, 0x2C, 0xB2, 0x4C, 0x3B, 0x18, 0x0C, 0x0C, 0x3B, 0x04, 0x08, 0x4B, 0x3B, 0x6C, 0x08, 0x07, 0x04, 0x4D, 0x3B, 0x2B, 0x0C, 0x4C, 0x32, 0x8E, 0x64, 0x66, 0x08, 0x49, 0x04, 0x9B, 0x10, 0xAD, 0x94, 0x5B, 0x10, 0x48, 0x69, 0xFF, 0xE3, 0x50, 0xF8, 0x6A, 0x48, 0x63, 0xE4, 0x00, 0x2F, 0x4C, 0x6D, 0xAD, 0xB4, 0x10, 0x05, 0x75, 0x10, 0x2D, 0x93, 0x55, 0x10, 0x48, 0x68, 0x21, 0xEC, 0x00, 0x16, 0x2D, 0xB3, 0x2D, 0x93, 0x21, 0xE4, 0xFF, 0x30, 0x21, 0xEC, 0x18, 0x00, 0x2D, 0xB3, 0x2E, 0x93, 0x84, 0x68, 0x42, 0xEC, 0x00, 0x14, 0x4E, 0xB3, 0x4E, 0x93, 0x42, 0xE4, 0xFF, 0x30, 0x28, 0x10, 0x28, 0x81, 0x84, 0x6C, 0x4E, 0xB3, 0xF6, 0x04, 0x68, 0x10, 0x0D, 0x93, 0x28, 0x10, 0x04, 0x68, 0x44, 0x10, 0x80, 0x82, 0x88, 0x44, 0x10, 0x6C, 0x0C, 0x04, 0x00, 0x00, 0x78, 0x00, 0x00, 0x20, 0x84, 0x00, 0x00, 0x20, 0x8A, 0x00, 0x00, 0x20, 0x00, 0x90, 0x00, 0x40, 0xFF, 0x00, 0xFF, 0xFF, 0x0D, 0xB3, 0x0D, 0x93, 0x00, 0xE4, 0xFF, 0x30, 0x81, 0x82, 0x10, 0x6C, 0x0D, 0xB3, 0x0E, 0x93, 0x40, 0x68, 0x42, 0x82, 0x48, 0x42, 0x84, 0x6C, 0x4E, 0xB3, 0xD4, 0x04, 0x62, 0x13, 0x0D, 0x93, 0x22, 0x13, 0x04, 0x68, 0x42, 0x13, 0x88, 0x82, 0x88, 0x44, 0x10, 0x6C, 0x0D, 0xB3, 0x0D, 0x93, 0x00, 0xE4, 0xFF, 0x30, 0x89, 0x82, 0x10, 0x6C, 0x0D, 0xB3, 0x0E, 0x93, 0x40, 0x68, 0x0A, 0x82, 0x08, 0x40, 0x40, 0x6C, 0x2E, 0xB3, 0x2E, 0x93, 0x21, 0xE4, 0xFF, 0x30, 0x4B, 0x82, 0x84, 0x6C, 0x4E, 0xB3, 0xB8, 0x04, 0x00, 0x31, 0x57, 0x12, 0x22, 0xA2, 0x06, 0x32, 0xC8, 0x64, 0x04, 0x08, 0x00, 0x32, 0x74, 0x12, 0x43, 0xA3, 0xFF, 0xE3, 0xEF, 0xFE, 0x1B, 0x04, 0x51, 0x12, 0x62, 0x82, 0x00, 0x23, 0xCC, 0x74, 0x62, 0xA2, 0x4B, 0x32, 0xC8, 0x64, 0x13, 0x08, 0x8D, 0x12, 0x00, 0x33, 0x62, 0xA4, 0xFF, 0xE3, 0xE1, 0xFE, 0x67, 0x84, 0x00, 0x23, 0xCC, 0x74, 0x67, 0xA4, 0x04, 0x32, 0xC8, 0x64, 0x07, 0x08, 0x47, 0xA4, 0x64, 0x12, 0x0C, 0x93, 0x00, 0x74, 0xFF, 0xE3, 0xE2, 0xFB, 0x64, 0x12, 0x60, 0x83, 0x41, 0x3B, 0x55, 0x08, 0x7F, 0x11, 0x60, 0x93, 0x7F, 0x2B, 0x8C, 0x74, 0x0A, 0x3A, 0x4C, 0x08, 0xCB, 0x6C, 0x40, 0x12, 0x62, 0xC4, 0x43, 0x08, 0x60, 0x93, 0x0C, 0x78, 0x79, 0x11, 0x60, 0x93, 0x03, 0xC4, 0x83, 0x70, 0x27, 0x43, 0x4D, 0x43, 0x48, 0x59, 0x4D, 0x5A, 0x43, 0xC4, 0x43, 0x08, 0x6F, 0x53, 0xD1, 0x32, 0xC8, 0x64, 0x02, 0x08, 0xD1, 0x33, 0xD1, 0x30, 0x6D, 0x58, 0x0C, 0x74, 0x00, 0xE0, 0xCC, 0x0D, 0x31, 0x04, 0x20, 0x30, 0x00, 0xE0, 0x72, 0x03, 0x2D, 0x04, 0x72, 0x11, 0x41, 0x93, 0xA6, 0x3A, 0x41, 0xB3, 0x00, 0x30, 0x00, 0xE0, 0x14, 0x09, 0x25, 0x04, 0x01, 0x30, 0xFF, 0xE3, 0x12, 0xFF, 0x21, 0x04, 0x02, 0x30, 0xFF, 0xE3, 0x0E, 0xFF, 0x1D, 0x04, 0x03, 0x30, 0xFF, 0xE3, 0x0A, 0xFF, 0x19, 0x04, 0x04, 0x30, 0xFF, 0xE3, 0x06, 0xFF, 0x15, 0x04, 0x61, 0x11, 0x00, 0x93, 0xA0, 0x93, 0x20, 0x93, 0x81, 0x93, 0x41, 0x93, 0x00, 0xC4, 0x80, 0x70, 0x05, 0xC4, 0x43, 0x70, 0x68, 0x43, 0x38, 0x49, 0x4C, 0x6C, 0xD0, 0x74, 0x68, 0x43, 0x02, 0xC4, 0x82, 0x70, 0x8C, 0x6C, 0xFF, 0xE3, 0x19, 0xFE, 0x00, 0x32, 0x79, 0x10, 0x40, 0xA3, 0x06, 0x30, 0x00, 0xE0, 0x11, 0x01, 0x94, 0x10, 0x62, 0x94, 0x63, 0xE4, 0xFF, 0x30, 0x42, 0x48, 0x88, 0x74, 0xC8, 0x6C, 0x62, 0xB4, 0x62, 0x94, 0x50, 0x10, 0xC8, 0x68, 0x00, 0xE4, 0x03, 0x20, 0x08, 0x40, 0x0C, 0x6C, 0x02, 0xB4, 0xA2, 0x94, 0x71, 0x10, 0x4C, 0x69, 0x00, 0x30, 0x00, 0xE0, 0xFB, 0x00, 0x62, 0x48, 0xCC, 0x74, 0x70, 0x43, 0x4C, 0x6D, 0xA2, 0xB4, 0xA2, 0x94, 0xA8, 0x45, 0xA8, 0x4D, 0x03, 0x30, 0x00, 0xE0, 0xF0, 0x00, 0x02, 0x48, 0x18, 0x40, 0x40, 0x6D, 0xA2, 0xB4, 0x10, 0x04, 0x00, 0x00, 0x00, 0x90, 0x00, 0x40, 0xFF, 0x00, 0xFF, 0xFF, 0x84, 0x00, 0x00, 0x20, 0x78, 0x00, 0x00, 0x20, 0x34, 0x39, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x40, 0xFF, 0xFF, 0x00, 0xFF, 0x92, 0x14, 0x00, 0x32, 0x70, 0x11, 0x42, 0xA3, 0x4C, 0x07, 0x00, 0x00, 0x00, 0x74, 0x05, 0x33, 0x0C, 0x64, 0x05, 0x08, 0x00, 0x32, 0x6C, 0x11, 0x41, 0xA3, 0x08, 0x04, 0x4A, 0x11, 0x00, 0x33, 0x01, 0x2B, 0xC3, 0x70, 0x21, 0x82, 0xC4, 0x68, 0x61, 0xA2, 0x3C, 0x78, 0xD0, 0x14, 0x66, 0x11, 0x66, 0x83, 0xC0, 0x3B, 0x2F, 0x0C, 0x44, 0x11, 0x80, 0x3B, 0x66, 0xA2, 0x61, 0x82, 0xC0, 0x3B, 0x05, 0x0C, 0x00, 0x30, 0xFF, 0xE3, 0x1C, 0xF7, 0x1E, 0x04, 0xC1, 0x3B, 0x05, 0x0C, 0x01, 0x30, 0xFF, 0xE3, 0x16, 0xF7, 0x18, 0x04, 0xC2, 0x3B, 0x05, 0x0C, 0x02, 0x30, 0xFF, 0xE3, 0x10, 0xF7, 0x12, 0x04, 0xC3, 0x3B, 0x05, 0x0C, 0x03, 0x30, 0xFF, 0xE3, 0x0A, 0xF7, 0x0C, 0x04, 0xC4, 0x3B, 0x05, 0x0C, 0x04, 0x30, 0xFF, 0xE3, 0x04, 0xF7, 0x06, 0x04, 0xC5, 0x3B, 0x04, 0x0C, 0x05, 0x30, 0xFF, 0xE3, 0xFE, 0xF6, 0x08, 0x30, 0x00, 0xE0, 0x1F, 0x03, 0x06, 0x30, 0x00, 0xE0, 0x92, 0x02, 0x06, 0x04, 0xC1, 0x3B, 0x04, 0x0C, 0x81, 0x3B, 0x4B, 0x10, 0x66, 0xA2, 0x90, 0x14, 0x00, 0x00, 0x69, 0x10, 0x46, 0x83, 0x08, 0x6C, 0x06, 0xA3, 0x3C, 0x78, 0x00, 0x00, 0x66, 0x10, 0x61, 0x83, 0x40, 0x3B, 0x07, 0x08, 0x64, 0x10, 0x66, 0x83, 0x40, 0x3B, 0x00, 0xC4, 0x00, 0x05, 0x02, 0x04, 0x01, 0x30, 0x3C, 0x78, 0x78, 0x00, 0x00, 0x20, 0xD1, 0x14, 0x00, 0xE0, 0xF7, 0x06, 0x91, 0x10, 0x00, 0xE4, 0xFF, 0x23, 0x60, 0x8C, 0x63, 0xE4, 0xFF, 0x33, 0xC0, 0x6C, 0x60, 0xAC, 0x00, 0x30, 0x00, 0xE0, 0x10, 0x07, 0x60, 0xE4, 0xFF, 0x23, 0x6A, 0x43, 0x00, 0x94, 0x4A, 0x10, 0x08, 0x68, 0x0C, 0x6C, 0x00, 0xB4, 0x03, 0x30, 0x00, 0xE0, 0x05, 0x07, 0x60, 0xE4, 0xFF, 0x23, 0x64, 0x43, 0x01, 0x8C, 0x46, 0x10, 0x08, 0x68, 0x0C, 0x6C, 0x01, 0xAC, 0x00, 0xE0, 0xED, 0x06, 0x91, 0x14, 0x98, 0x00, 0x00, 0x20, 0xFF, 0x03, 0xF0, 0xFF, 0x0F, 0xC0, 0xFF, 0xFF, 0xD4, 0x14, 0x92, 0x12, 0xA0, 0x94, 0xB6, 0x45, 0xB6, 0x4D, 0x62, 0x55, 0xAD, 0x5D, 0xA5, 0xE4, 0xFF, 0x23, 0x60, 0x8C, 0x63, 0xE4, 0xFF, 0x33, 0xD4, 0x6C, 0x60, 0xAC, 0x00, 0xE0, 0xC0, 0x06, 0x02, 0x48, 0xA0, 0x5D, 0xA5, 0xE4, 0xFF, 0x23, 0x60, 0x8C, 0x63, 0xE4, 0xFF, 0x33, 0xD4, 0x6C, 0x60, 0xAC, 0x60, 0x94, 0xAC, 0x43, 0xB6, 0x4D, 0x42, 0x55, 0xA9, 0x5D, 0xA5, 0xE4, 0xFF, 0x23, 0x4A, 0x45, 0xC3, 0x12, 0xD8, 0x68, 0xC8, 0x6C, 0x60, 0xB4, 0x00, 0x30, 0x00, 0xE0, 0xCC, 0x06, 0x02, 0x48, 0xA0, 0x5D, 0xA5, 0xE4, 0xFF, 0x23, 0xAA, 0x45, 0x60, 0x94, 0x8C, 0x69, 0x94, 0x6D, 0xC0, 0xB4, 0xA2, 0x46, 0xB6, 0x4D, 0x62, 0x55, 0xAD, 0x5D, 0xA5, 0xE4, 0xFF, 0x23, 0x64, 0x45, 0xD0, 0x4E, 0xF8, 0x11, 0x9C, 0x69, 0x8C, 0x6D, 0xC1, 0xAC, 0x06, 0x30, 0x00, 0xE0, 0xB4, 0x06, 0x02, 0x48, 0xA0, 0x5D, 0xA5, 0xE4, 0xFF, 0x23, 0xA4, 0x45, 0x61, 0x8C, 0xCC, 0x69, 0xD4, 0x6D, 0xE1, 0xAC, 0x00, 0xE0, 0x9B, 0x06, 0x94, 0x14, 0x00, 0x74, 0x40, 0x38, 0x0C, 0x08, 0x6B, 0x11, 0x60, 0x93, 0x4D, 0x11, 0xC8, 0x68, 0x40, 0x3B, 0x1B, 0x0C, 0x68, 0x11, 0x00, 0x93, 0x0C, 0x40, 0x16, 0x48, 0x1C, 0x04, 0x46, 0x38, 0x0D, 0x08, 0x64, 0x11, 0x61, 0x8B, 0x02, 0xEA, 0xF0, 0x3F, 0xC8, 0x68, 0x40, 0x3B, 0x0F, 0x0C, 0x61, 0x11, 0x00, 0x93, 0x02, 0x40, 0x16, 0x48, 0x0E, 0x04, 0x43, 0x38, 0x0A, 0x08, 0x7D, 0x10, 0x00, 0x93, 0x16, 0x40, 0x16, 0x48, 0x07, 0x04, 0x01, 0x30, 0x05, 0x04, 0x01, 0x30, 0x03, 0x04, 0x00, 0xEA, 0xFF, 0x03, 0x3C, 0x78, 0x77, 0x10, 0x40, 0x93, 0x6C, 0x42, 0x76, 0x4B, 0x56, 0x42, 0x56, 0x4A, 0xC8, 0x7C, 0x43, 0x43, 0x06, 0x43, 0x09, 0x58, 0x6D, 0x58, 0x0A, 0x53, 0x3C, 0x78, 0x00, 0x00, 0xD4, 0x14, 0x21, 0x14, 0xC0, 0x75, 0x00, 0x35, 0x06, 0xEA, 0xFF, 0xFF, 0xA0, 0xB8, 0x00, 0x34, 0x1F, 0x6C, 0x00, 0xE0, 0x65, 0x06, 0xA0, 0x5D, 0x80, 0x65, 0x05, 0x0C, 0x60, 0x98, 0x0C, 0x64, 0x04, 0x0C, 0x04, 0x04, 0x83, 0x6D, 0x02, 0x04, 0x00, 0xB8, 0x00, 0x24, 0x10, 0x75, 0x44, 0x3C, 0xF0, 0x0B, 0x19, 0x5D, 0x60, 0x98, 0x0D, 0x58, 0x01, 0x48, 0x01, 0x74, 0x01, 0x14, 0x94, 0x14, 0x98, 0x00, 0x00, 0x20, 0xFF, 0x03, 0xF0, 0xFF, 0x0F, 0xC0, 0xFF, 0xFF, 0x00, 0xFC, 0x0F, 0x00, 0x68, 0x10, 0x00, 0x32, 0x40, 0xA3, 0x00, 0x32, 0x44, 0xA3, 0x48, 0x83, 0x80, 0x3A, 0x48, 0xA3, 0x42, 0x93, 0x42, 0xE4, 0x01, 0x20, 0x42, 0xB3, 0x00, 0x32, 0x46, 0xAB, 0x3C, 0x78, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x20, 0xD2, 0x14, 0xFF, 0xE3, 0xD3, 0xF9, 0x41, 0x38, 0x15, 0x08, 0xFF, 0xE3, 0xDF, 0xF9, 0x42, 0x38, 0x27, 0x08, 0x00, 0x30, 0xFF, 0xE3, 0x84, 0xFF, 0x03, 0xEA, 0x6B, 0x01, 0x0C, 0x64, 0x06, 0x0C, 0x63, 0x13, 0x46, 0x8B, 0x00, 0x22, 0x46, 0xAB, 0x1B, 0x04, 0x00, 0x32, 0x60, 0x13, 0x46, 0xAB, 0x2B, 0x04, 0xFF, 0xE3, 0xBB, 0xF9, 0x40, 0x38, 0x13, 0x08, 0x7E, 0x12, 0x61, 0x93, 0xC0, 0x3B, 0x0F, 0x0C, 0xFF, 0xE3, 0x6D, 0xFF, 0xF9, 0x33, 0x0C, 0x64, 0x06, 0x0C, 0x78, 0x12, 0x46, 0x8B, 0x00, 0x22, 0x46, 0xAB, 0x05, 0x04, 0x00, 0x32, 0x75, 0x12, 0x46, 0xAB, 0x15, 0x04, 0x74, 0x12, 0x46, 0x8B, 0x03, 0xEA, 0x8F, 0x01, 0x8C, 0x64, 0x0F, 0x08, 0xC8, 0x33, 0x61, 0x43, 0x50, 0x12, 0x66, 0xAA, 0x71, 0x12, 0x41, 0x93, 0xA6, 0x3A, 0x41, 0xB3, 0x00, 0x30, 0x00, 0xE0, 0x28, 0x07, 0x20, 0x30, 0x00, 0xE0, 0x7B, 0x01, 0x6B, 0x12, 0x61, 0x93, 0xC0, 0x3B, 0x28, 0x0C, 0x68, 0x12, 0x64, 0x83, 0xC0, 0x3B, 0x12, 0x0C, 0x06, 0x30, 0xFF, 0xE3, 0x42, 0xFF, 0xF9, 0x33, 0x0C, 0x64, 0x0C, 0x0C, 0x65, 0x12, 0x41, 0x93, 0xA6, 0x3A, 0x41, 0xB3, 0x00, 0x30, 0x00, 0xE0, 0x10, 0x07, 0x20, 0x30, 0x00, 0xE0, 0x63, 0x01, 0x13, 0x04, 0x5D, 0x11, 0x24, 0x82, 0x78, 0x41, 0x79, 0x4B, 0x00, 0x23, 0x63, 0xE4, 0x7F, 0x20, 0x01, 0x43, 0x21, 0xE4, 0x01, 0x20, 0x40, 0x6C, 0x24, 0xA2, 0x45, 0x3B, 0x05, 0x08, 0xCB, 0x6C, 0x44, 0x82, 0xA0, 0x3A, 0x44, 0xA3, 0x00, 0x30, 0xFF, 0xE3, 0x1F, 0xFF, 0x03, 0x6D, 0x03, 0xEA, 0x8E, 0x03, 0x0C, 0x64, 0x0C, 0x08, 0x72, 0x11, 0x41, 0x93, 0xA6, 0x3A, 0x41, 0xB3, 0x01, 0x30, 0x00, 0xE0, 0x41, 0x01, 0x04, 0x30, 0xFF, 0xE3, 0xA8, 0xFC, 0x1D, 0x04, 0xC7, 0x33, 0x62, 0x43, 0x0C, 0x64, 0x16, 0x08, 0x49, 0x11, 0x6E, 0x82, 0x00, 0x23, 0xCC, 0x74, 0x6E, 0xA2, 0x32, 0x32, 0xC8, 0x64, 0x11, 0x08, 0x65, 0x11, 0x4E, 0xA3, 0x66, 0x11, 0x41, 0x93, 0xA6, 0x3A, 0x41, 0xB3, 0x01, 0x30, 0x00, 0xE0, 0x28, 0x01, 0x04, 0x30, 0xFF, 0xE3, 0x8F, 0xFC, 0x04, 0x04, 0x00, 0x32, 0x7E, 0x10, 0x4E, 0xA3, 0x00, 0xE0, 0xF9, 0x00, 0x04, 0x38, 0x0B, 0x08, 0xB6, 0x33, 0x61, 0x43, 0x0C, 0x65, 0x07, 0x08, 0x00, 0x30, 0x00, 0xE0, 0xCC, 0x05, 0x10, 0x30, 0x00, 0xE0, 0x13, 0x01, 0x03, 0x30, 0xFF, 0xE3, 0xE2, 0xFE, 0x80, 0x33, 0x63, 0x43, 0x0C, 0x64, 0x0E, 0x08, 0x74, 0x10, 0x41, 0x93, 0xA6, 0x3A, 0x41, 0xB3, 0x00, 0x30, 0x00, 0xE0, 0xAF, 0x06, 0x02, 0x30, 0x00, 0xE0, 0x02, 0x01, 0x05, 0x30, 0xFF, 0xE3, 0x69, 0xFC, 0x6E, 0x10, 0x61, 0x93, 0xC6, 0x3B, 0x2A, 0x0C, 0x6A, 0x10, 0x60, 0x83, 0xFA, 0x32, 0xC8, 0x64, 0x21, 0x08, 0x00, 0x30, 0xFF, 0xE3, 0xC5, 0xFE, 0x03, 0xEA, 0x1B, 0x03, 0x0C, 0x64, 0x21, 0x0C, 0x00, 0xE0, 0x49, 0x04, 0x40, 0x38, 0x1D, 0x08, 0x64, 0x10, 0x41, 0x93, 0x07, 0x04, 0x9C, 0x00, 0x00, 0x20, 0x00, 0x50, 0x00, 0x40, 0x00, 0xA0, 0x00, 0x40, 0x86, 0x3A, 0x41, 0xB3, 0x41, 0x93, 0xA8, 0x3A, 0x41, 0xB3, 0x41, 0x93, 0x88, 0x3A, 0x41, 0xB3, 0x41, 0x93, 0x86, 0x3A, 0x41, 0xB3, 0x08, 0x04, 0x00, 0x23, 0x44, 0x11, 0x60, 0xA2, 0x04, 0x04, 0x00, 0x32, 0x62, 0x11, 0x40, 0xA3, 0xFF, 0xE3, 0xEF, 0xF8, 0x42, 0x38, 0x06, 0x08, 0x7F, 0x10, 0x48, 0x83, 0x80, 0x3A, 0x48, 0xA3, 0x38, 0x04, 0x7C, 0x10, 0x68, 0x83, 0xC0, 0x3B, 0x19, 0x08, 0x7B, 0x10, 0x61, 0x93, 0x63, 0xE4, 0x0F, 0x20, 0x25, 0x3B, 0x2E, 0x08, 0x97, 0x10, 0x68, 0x84, 0x01, 0x35, 0xD4, 0x6C, 0x68, 0xA4, 0x00, 0xE0, 0x69, 0x01, 0x52, 0x58, 0x41, 0x42, 0x62, 0x94, 0x63, 0xE4, 0x01, 0x20, 0xC8, 0x6C, 0x62, 0xB4, 0xD4, 0x68, 0xCC, 0x74, 0x40, 0x3B, 0x1C, 0x0C, 0x00, 0xE0, 0x5C, 0x01, 0x6D, 0x10, 0x62, 0x93, 0x61, 0x4B, 0x0C, 0x64, 0x15, 0x08, 0x00, 0xE0, 0x0B, 0x06, 0x40, 0x38, 0x11, 0x08, 0x6A, 0x10, 0x61, 0x93, 0x63, 0xE4, 0x0F, 0x20, 0x01, 0x32, 0xC8, 0x64, 0x06, 0x08, 0x00, 0x2B, 0x0C, 0x74, 0x00, 0xE0, 0x4A, 0x05, 0x05, 0x04, 0x63, 0x10, 0x48, 0x83, 0x80, 0x3A, 0x48, 0xA3, 0x92, 0x14, 0x9C, 0x00, 0x00, 0x20, 0x00, 0xA0, 0x00, 0x40, 0xD0, 0x14, 0x6F, 0x10, 0x00, 0x32, 0x40, 0xAB, 0x41, 0xAB, 0x44, 0xA3, 0x45, 0xA3, 0x02, 0x32, 0x46, 0xA3, 0x07, 0x23, 0x07, 0x32, 0x40, 0xA3, 0x41, 0xA3, 0x42, 0xA3, 0xFF, 0xE3, 0xCA, 0xF4, 0xFF, 0xE3, 0x82, 0xF2, 0xFF, 0xE3, 0xF6, 0xF0, 0xFF, 0xE3, 0x58, 0xF1, 0xFF, 0xE3, 0xEE, 0xF0, 0xFF, 0xE3, 0x38, 0xF6, 0xFF, 0xE3, 0x9E, 0xF2, 0x90, 0x14, 0x00, 0x00, 0xAC, 0x00, 0x00, 0x20, 0x7E, 0x11, 0x5E, 0x5B, 0x00, 0x31, 0x21, 0xA2, 0x22, 0xA2, 0x01, 0x32, 0x46, 0xA3, 0x3C, 0x78, 0xD2, 0x14, 0x7A, 0x11, 0x01, 0x8B, 0x40, 0x38, 0x05, 0x0C, 0x00, 0x74, 0x00, 0xE0, 0x2A, 0x03, 0x26, 0x04, 0x76, 0x11, 0x44, 0x83, 0x65, 0x83, 0x8E, 0x64, 0x0D, 0x0C, 0x73, 0x11, 0x43, 0xC4, 0x21, 0x08, 0xA6, 0x89, 0x20, 0x8B, 0x55, 0x68, 0x20, 0xAB, 0x00, 0x22, 0x42, 0xE4, 0x07, 0x20, 0x44, 0xA3, 0x02, 0x04, 0x00, 0x35, 0x8E, 0x11, 0x41, 0x84, 0x6E, 0x11, 0x43, 0xC4, 0x43, 0x08, 0x60, 0x93, 0x17, 0x6C, 0xCD, 0x7B, 0x61, 0x84, 0x60, 0xA4, 0x42, 0x84, 0x8E, 0x64, 0x07, 0x0C, 0x40, 0x3D, 0x05, 0x08, 0x65, 0x11, 0x49, 0xA3, 0x01, 0x32, 0x46, 0xA3, 0x92, 0x14, 0x00, 0x00, 0x62, 0x11, 0x00, 0x32, 0x46, 0xA3, 0x0A, 0xA3, 0x3C, 0x78, 0x00, 0x00, 0x60, 0x11, 0x00, 0x83, 0x3C, 0x78, 0x00, 0x00, 0x7E, 0x10, 0x01, 0x83, 0x3C, 0x78, 0x00, 0x00, 0x7C, 0x10, 0x40, 0x83, 0x61, 0x83, 0xCA, 0x64, 0x00, 0xC4, 0x00, 0x05, 0x3C, 0x78, 0x00, 0x00, 0xC1, 0x14, 0x01, 0x74, 0x76, 0x10, 0x66, 0x83, 0x40, 0x3B, 0x14, 0x0C, 0x74, 0x10, 0x60, 0x8B, 0xC2, 0x68, 0x10, 0x08, 0x32, 0x10, 0x45, 0x81, 0x82, 0x5A, 0x84, 0xE4, 0x07, 0x20, 0x24, 0x81, 0x52, 0x64, 0x08, 0x0C, 0x2E, 0x10, 0xC0, 0x6C, 0x60, 0xA9, 0x41, 0xC4, 0x23, 0x08, 0x06, 0xAB, 0x85, 0xA1, 0x81, 0x14, 0x00, 0x74, 0x6A, 0x10, 0x41, 0x8B, 0x08, 0x6C, 0x01, 0xAB, 0x3C, 0x78, 0x00, 0x74, 0x67, 0x10, 0x41, 0x8B, 0x02, 0xC4, 0x40, 0x20, 0x01, 0xAB, 0x3C, 0x78, 0x00, 0x00, 0x63, 0x10, 0x61, 0x8B, 0x40, 0x3B, 0x00, 0xC4, 0x00, 0x05, 0x3C, 0x78, 0xAC, 0x00, 0x00, 0x20, 0xB4, 0x00, 0x00, 0x20, 0x60, 0x39, 0x00, 0x00, 0x4A, 0x10, 0x60, 0x92, 0x01, 0x92, 0x0E, 0x64, 0x0E, 0x0C, 0xC0, 0x64, 0x04, 0x08, 0x01, 0x5B, 0x01, 0x74, 0x0A, 0x04, 0x45, 0x10, 0x28, 0x82, 0x00, 0x21, 0x28, 0xA2, 0x02, 0x6C, 0x0C, 0x58, 0x01, 0x74, 0x02, 0x04, 0x00, 0x30, 0x3C, 0x78, 0xC8, 0x00, 0x00, 0x20, 0x68, 0x12, 0x00, 0x32, 0x00, 0x2A, 0x45, 0xAB, 0x00, 0x32, 0x41, 0xB3, 0x40, 0xB3, 0x48, 0xA3, 0x3C, 0x78, 0x00, 0x00, 0xD2, 0x14, 0x00, 0x74, 0x49, 0x75, 0x62, 0x12, 0x00, 0x32, 0x01, 0x2A, 0x83, 0x70, 0x85, 0x8B, 0x90, 0x68, 0x45, 0xAB, 0x83, 0xE4, 0x0B, 0x00, 0x04, 0xC4, 0x44, 0x08, 0x21, 0xE4, 0x01, 0x20, 0x60, 0x84, 0x80, 0x3B, 0x4C, 0x6C, 0x20, 0xA4, 0x41, 0x45, 0x60, 0x8C, 0x63, 0xE4, 0x01, 0x20, 0xC8, 0x6C, 0x60, 0xAC, 0xFF, 0xE3, 0xC6, 0xFF, 0xB4, 0x58, 0xA1, 0xAC, 0x92, 0x14, 0x00, 0x00, 0x00, 0x74, 0x73, 0x11, 0x01, 0x32, 0x02, 0xC4, 0x20, 0x40, 0x45, 0x8B, 0x08, 0x6C, 0x05, 0xAB, 0x3C, 0x78, 0x00, 0x00, 0x00, 0x74, 0x4E, 0x11, 0x00, 0x33, 0x01, 0x2B, 0xC3, 0x70, 0x25, 0x8A, 0xC4, 0x68, 0x65, 0xAA, 0x3C, 0x78, 0x00, 0x00, 0x69, 0x11, 0x40, 0x93, 0x00, 0x22, 0x40, 0xB3, 0x3C, 0x78, 0x00, 0x00, 0xD4, 0x14, 0x23, 0x14, 0xFF, 0xE3, 0xA4, 0xFF, 0x01, 0xB8, 0x64, 0x11, 0x40, 0x93, 0x41, 0xB3, 0x40, 0x38, 0x31, 0x0C, 0xA2, 0x11, 0xC3, 0x11, 0x00, 0x34, 0xCF, 0x6D, 0x0B, 0x23, 0x62, 0xB8, 0x65, 0x8F, 0x83, 0xC4, 0x81, 0x40, 0xC0, 0x39, 0x21, 0x08, 0xA0, 0xB8, 0x20, 0x8D, 0x01, 0x98, 0x40, 0x64, 0x04, 0x08, 0x21, 0x59, 0x20, 0xAD, 0x19, 0x04, 0x00, 0x31, 0x20, 0xAD, 0x02, 0x98, 0x80, 0xC4, 0x41, 0x08, 0x20, 0x81, 0xC0, 0x39, 0x08, 0x0C, 0x80, 0xC4, 0x42, 0x08, 0x60, 0x8A, 0x61, 0x4B, 0x40, 0x98, 0x60, 0xAA, 0x06, 0x04, 0x01, 0x31, 0x81, 0xC4, 0x22, 0x40, 0xC8, 0x6C, 0x65, 0xAF, 0x60, 0x96, 0x40, 0x3B, 0x02, 0x0C, 0xCD, 0x7B, 0x00, 0x24, 0x03, 0x25, 0x03, 0x26, 0x4A, 0x3C, 0xD7, 0x0B, 0x03, 0x14, 0x94, 0x14, 0x68, 0x10, 0x00, 0x93, 0x3C, 0x78, 0x00, 0x00, 0x66, 0x10, 0x48, 0x83, 0x16, 0x42, 0x49, 0x58, 0x60, 0x93, 0x6A, 0x4B, 0x0C, 0x5A, 0x3C, 0x78, 0x62, 0x10, 0x00, 0x93, 0x3C, 0x78, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x20, 0xD6, 0x00, 0x00, 0x20, 0x7C, 0x39, 0x00, 0x00, 0x6A, 0x10, 0x41, 0x8B, 0x42, 0xE4, 0x1F, 0x20, 0x41, 0xAB, 0x40, 0x83, 0x00, 0x31, 0x07, 0x29, 0x84, 0x68, 0x40, 0xA3, 0x40, 0x8B, 0x42, 0xE4, 0xF8, 0x37, 0x40, 0xAB, 0x40, 0x93, 0x24, 0x10, 0x84, 0x68, 0x40, 0xB3, 0x3C, 0x78, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x20, 0xFF, 0x07, 0xE0, 0xFF, 0x6D, 0x13, 0x40, 0x83, 0xA0, 0x3A, 0x40, 0xA3, 0x3C, 0x78, 0x00, 0x00, 0x6A, 0x13, 0x40, 0x83, 0x80, 0x3A, 0x40, 0xA3, 0x3C, 0x78, 0x00, 0x00, 0x67, 0x13, 0x00, 0x93, 0x00, 0xE4, 0x01, 0x20, 0x3C, 0x78, 0x00, 0x00, 0xD2, 0x14, 0x00, 0x75, 0xFF, 0xE3, 0xF8, 0xFF, 0x40, 0x38, 0x0E, 0x0C, 0xFF, 0xE3, 0x3C, 0xEF, 0x43, 0x6D, 0x53, 0x6C, 0x03, 0x32, 0xFF, 0xE3, 0x85, 0xF7, 0x03, 0x30, 0x57, 0x6C, 0x00, 0xE0, 0x73, 0x04, 0x01, 0x30, 0x02, 0x04, 0x00, 0x30, 0x92, 0x14, 0x00, 0x00, 0xD1, 0x14, 0xFF, 0xE3, 0xE3, 0xFF, 0x40, 0x38, 0x0D, 0x0C, 0xFF, 0xE3, 0x27, 0xEF, 0x03, 0x6D, 0x09, 0x31, 0xFF, 0xE3, 0x81, 0xF7, 0x09, 0x30, 0x53, 0x6C, 0x00, 0xE0, 0x5F, 0x04, 0x01, 0x30, 0x02, 0x04, 0x00, 0x30, 0x91, 0x14, 0x00, 0x00, 0xD1, 0x14, 0xFF, 0xE3, 0xCF, 0xFF, 0x40, 0x38, 0x0D, 0x0C, 0xFF, 0xE3, 0x13, 0xEF, 0x03, 0x6D, 0x07, 0x31, 0xFF, 0xE3, 0xAB, 0xF7, 0x07, 0x30, 0x53, 0x6C, 0x00, 0xE0, 0x4B, 0x04, 0x01, 0x30, 0x02, 0x04, 0x00, 0x30, 0x91, 0x14, 0x00, 0x00, 0xD2, 0x14, 0x00, 0x75, 0xFF, 0xE3, 0xBA, 0xFF, 0x40, 0x38, 0x0D, 0x0C, 0xFF, 0xE3, 0xFE, 0xEE, 0x43, 0x6D, 0x53, 0x6C, 0xFF, 0xE3, 0x08, 0xF8, 0x03, 0x30, 0x57, 0x6C, 0x00, 0xE0, 0x36, 0x04, 0x01, 0x30, 0x05, 0x04, 0x08, 0x30, 0xFF, 0xE3, 0xA5, 0xFE, 0x00, 0x30, 0x92, 0x14, 0x00, 0x00, 0xD2, 0x14, 0x21, 0x14, 0x00, 0x75, 0xFF, 0xE3, 0xA1, 0xFF, 0x40, 0x38, 0x50, 0x0C, 0xFF, 0xE3, 0xE5, 0xEE, 0x43, 0x6D, 0x40, 0x3C, 0x13, 0x08, 0x8E, 0xE4, 0x01, 0x00, 0x00, 0x33, 0x60, 0xA4, 0x00, 0x30, 0x53, 0x6C, 0xFF, 0xE3, 0xB6, 0xF8, 0x20, 0x84, 0x17, 0x6C, 0xFF, 0xE3, 0xD4, 0xF7, 0x03, 0x30, 0x57, 0x6C, 0x00, 0xE0, 0x12, 0x04, 0x01, 0x30, 0x3D, 0x04, 0x2E, 0xE4, 0x01, 0x00, 0x00, 0x33, 0x60, 0xA9, 0x13, 0x6C, 0xFF, 0xE3, 0xA5, 0xF8, 0xFF, 0xE3, 0xBD, 0xF6, 0x40, 0x38, 0x15, 0x08, 0x6E, 0xE4, 0x01, 0x00, 0x40, 0x8B, 0x03, 0xEA, 0x89, 0x08, 0x8C, 0x64, 0x06, 0x08, 0x02, 0xEA, 0x89, 0x08, 0x6E, 0xE4, 0x01, 0x00, 0x40, 0xAB, 0x6E, 0xE4, 0x01, 0x00, 0x40, 0x8B, 0x17, 0x6C, 0x01, 0x31, 0xFF, 0xE3, 0x9C, 0xF7, 0x14, 0x04, 0xFF, 0xE3, 0xA5, 0xF6, 0x41, 0x38, 0x09, 0x08, 0x6E, 0xE4, 0x01, 0x00, 0x40, 0x8B, 0x17, 0x6C, 0x02, 0x31, 0xFF, 0xE3, 0x90, 0xF7, 0x08, 0x04, 0x6E, 0xE4, 0x01, 0x00, 0x40, 0x8B, 0x17, 0x6C, 0x00, 0x31, 0xFF, 0xE3, 0x88, 0xF7, 0x05, 0x30, 0x57, 0x6C, 0x00, 0xE0, 0xDA, 0x03, 0x01, 0x30, 0x05, 0x04, 0x10, 0x30, 0xFF, 0xE3, 0x49, 0xFE, 0x00, 0x30, 0x01, 0x14, 0x92, 0x14, 0xD2, 0x14, 0x00, 0x75, 0xFF, 0xE3, 0x46, 0xFF, 0x40, 0x38, 0x0E, 0x0C, 0xFF, 0xE3, 0x8A, 0xEE, 0x43, 0x6D, 0x53, 0x6C, 0x03, 0x32, 0xFF, 0xE3, 0xC3, 0xF6, 0x03, 0x30, 0x57, 0x6C, 0x00, 0xE0, 0xC1, 0x03, 0x01, 0x30, 0x02, 0x04, 0x00, 0x30, 0x92, 0x14, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x20, 0xD2, 0x14, 0x40, 0x75, 0xFF, 0xE3, 0x76, 0xEE, 0x03, 0x6D, 0x00, 0x31, 0x97, 0x6C, 0xFF, 0xE3, 0x49, 0xF7, 0x04, 0x30, 0x53, 0x6C, 0x00, 0xE0, 0xAD, 0x03, 0x92, 0x14, 0xD2, 0x14, 0x40, 0x75, 0xFF, 0xE3, 0x68, 0xEE, 0x03, 0x6D, 0x02, 0x31, 0x97, 0x6C, 0xFF, 0xE3, 0x19, 0xF7, 0x04, 0x30, 0x53, 0x6C, 0x00, 0xE0, 0x9F, 0x03, 0x92, 0x14, 0xD1, 0x14, 0xFF, 0xE3, 0x5B, 0xEE, 0x03, 0x6D, 0x30, 0x31, 0xFF, 0xE3, 0x1F, 0xF7, 0x03, 0x30, 0x53, 0x6C, 0x00, 0xE0, 0x93, 0x03, 0x91, 0x14, 0xD1, 0x14, 0xFF, 0xE3, 0x4F, 0xEE, 0x03, 0x6D, 0x31, 0x31, 0xFF, 0xE3, 0x13, 0xF7, 0x03, 0x30, 0x53, 0x6C, 0x00, 0xE0, 0x87, 0x03, 0x91, 0x14, 0xD2, 0x14, 0x40, 0x75, 0xFF, 0xE3, 0x42, 0xEE, 0x03, 0x6D, 0x45, 0xE4, 0x3F, 0x20, 0x01, 0x31, 0xFF, 0xE3, 0xF2, 0xF6, 0x04, 0x30, 0x53, 0x6C, 0x00, 0xE0, 0x78, 0x03, 0x92, 0x14, 0x00, 0x00, 0xD2, 0x14, 0x40, 0x75, 0xFF, 0xE3, 0x32, 0xEE, 0x03, 0x6D, 0x45, 0xE4, 0x3F, 0x20, 0x04, 0x31, 0xFF, 0xE3, 0xE2, 0xF6, 0x04, 0x30, 0x53, 0x6C, 0x00, 0xE0, 0x68, 0x03, 0x92, 0x14, 0x00, 0x00, 0xD2, 0x14, 0x40, 0x75, 0xFF, 0xE3, 0x22, 0xEE, 0x03, 0x6D, 0x00, 0x31, 0x97, 0x6C, 0xFF, 0xE3, 0xD3, 0xF6, 0x04, 0x30, 0x53, 0x6C, 0x00, 0xE0, 0x59, 0x03, 0x92, 0x14, 0xD1, 0x14, 0xFF, 0xE3, 0xCD, 0xFE, 0x40, 0x38, 0x0C, 0x0C, 0xFF, 0xE3, 0x11, 0xEE, 0x03, 0x6D, 0xFF, 0xE3, 0x2C, 0xF7, 0x05, 0x30, 0x53, 0x6C, 0x00, 0xE0, 0x4A, 0x03, 0x01, 0x30, 0x02, 0x04, 0x00, 0x30, 0x91, 0x14, 0xD1, 0x14, 0xFF, 0xE3, 0xBB, 0xFE, 0x40, 0x38, 0x0C, 0x0C, 0xFF, 0xE3, 0xFF, 0xED, 0x03, 0x6D, 0xFF, 0xE3, 0x2A, 0xF7, 0x0E, 0x30, 0x53, 0x6C, 0x00, 0xE0, 0x38, 0x03, 0x01, 0x30, 0x02, 0x04, 0x00, 0x30, 0x91, 0x14, 0xD1, 0x14, 0xFF, 0xE3, 0xA9, 0xFE, 0x40, 0x38, 0x0C, 0x0C, 0xFF, 0xE3, 0xED, 0xED, 0x03, 0x6D, 0xFF, 0xE3, 0x42, 0xF7, 0x03, 0x30, 0x53, 0x6C, 0x00, 0xE0, 0x26, 0x03, 0x01, 0x30, 0x02, 0x04, 0x00, 0x30, 0x91, 0x14, 0xD1, 0x14, 0xFF, 0xE3, 0x97, 0xFE, 0x40, 0x38, 0x0C, 0x0C, 0xFF, 0xE3, 0xDB, 0xED, 0x03, 0x6D, 0xFF, 0xE3, 0x3C, 0xF7, 0x05, 0x30, 0x53, 0x6C, 0x00, 0xE0, 0x14, 0x03, 0x01, 0x30, 0x02, 0x04, 0x00, 0x30, 0x91, 0x14, 0xD1, 0x14, 0xFF, 0xE3, 0x85, 0xFE, 0x40, 0x38, 0x0D, 0x0C, 0xFF, 0xE3, 0xC9, 0xED, 0x03, 0x6D, 0xFF, 0xE3, 0x3A, 0xF7, 0x01, 0x20, 0x00, 0x74, 0x53, 0x6C, 0x00, 0xE0, 0x01, 0x03, 0x01, 0x30, 0x02, 0x04, 0x00, 0x30, 0x91, 0x14, 0x00, 0x00, 0xD1, 0x14, 0x00, 0x30, 0xFF, 0xE3, 0x58, 0xFB, 0x03, 0xEA, 0x7B, 0x03, 0x0C, 0x64, 0x48, 0x0C, 0x63, 0x13, 0x60, 0x93, 0x75, 0x43, 0x78, 0x4B, 0x09, 0x3B, 0x3A, 0x08, 0x60, 0x13, 0x60, 0x93, 0x6B, 0x43, 0x76, 0x4B, 0xC1, 0x64, 0x05, 0x08, 0x03, 0xEA, 0xAA, 0x02, 0x0C, 0x64, 0x1E, 0x0C, 0x9B, 0x12, 0x60, 0x8C, 0x63, 0xE4, 0xF8, 0x37, 0x60, 0xAC, 0x7A, 0x12, 0x41, 0x93, 0xA9, 0x3A, 0x41, 0xB3, 0x41, 0x93, 0x87, 0x3A, 0x41, 0xB3, 0x41, 0x93, 0x86, 0x3A, 0x41, 0xB3, 0x40, 0x93, 0x42, 0xEC, 0x0C, 0x00, 0x40, 0xB3, 0x05, 0x30, 0xFF, 0xE3, 0xB6, 0xFD, 0x01, 0x30, 0xFF, 0xE3, 0x5F, 0xFD, 0x60, 0x84, 0xA1, 0x3B, 0x60, 0xA4, 0x22, 0x04, 0xA0, 0x30, 0xFF, 0xE3, 0x80, 0xFE, 0x40, 0x38, 0x1D, 0x0C, 0x6A, 0x12, 0x20, 0x93, 0x35, 0x41, 0x38, 0x49, 0x00, 0x21, 0x44, 0x74, 0x23, 0x41, 0x40, 0x8B, 0x42, 0xE4, 0xF8, 0x37, 0x84, 0x6C, 0x40, 0xAB, 0x10, 0x04, 0x64, 0x12, 0x40, 0x83, 0xA2, 0x3A, 0x40, 0xA3, 0x05, 0x30, 0xFF, 0xE3, 0x96, 0xFD, 0x08, 0x04, 0x60, 0x12, 0x40, 0x83, 0xA2, 0x3A, 0x40, 0xA3, 0x05, 0x30, 0xFF, 0xE3, 0x8E, 0xFD, 0x91, 0x14, 0x00, 0x00, 0xD4, 0x14, 0x21, 0x14, 0x00, 0x75, 0xFF, 0xE3, 0x07, 0xFD, 0x04, 0x28, 0x00, 0x74, 0x00, 0xB8, 0x01, 0x38, 0x04, 0x08, 0x07, 0x30, 0xFF, 0xE3, 0x7F, 0xFD, 0x00, 0xE0, 0x9F, 0x05, 0x74, 0x11, 0x40, 0x83, 0x81, 0x3A, 0xA2, 0x3A, 0x40, 0xA3, 0xC4, 0x3C, 0x2C, 0x08, 0xC1, 0x3C, 0x2C, 0x08, 0xC2, 0x3C, 0x2C, 0x08, 0xC3, 0x3C, 0x2C, 0x08, 0xC0, 0x3C, 0x2C, 0x0C, 0x06, 0x30, 0xFF, 0xE3, 0xE3, 0xFA, 0x31, 0x33, 0x0C, 0x64, 0x06, 0x0C, 0x00, 0x30, 0x00, 0xE0, 0xB5, 0x02, 0x04, 0x37, 0x22, 0x04, 0x87, 0x11, 0x60, 0x84, 0x82, 0x3B, 0x60, 0xA4, 0x00, 0x30, 0xFF, 0xE3, 0xD4, 0xFA, 0x60, 0xE4, 0xE3, 0x10, 0x63, 0xE4, 0xFF, 0x23, 0x6B, 0x43, 0x40, 0x94, 0x22, 0x11, 0x84, 0x68, 0xC8, 0x6C, 0x60, 0xB4, 0x05, 0x30, 0x01, 0x31, 0x1E, 0x32, 0xFF, 0xE3, 0x2D, 0xFD, 0x04, 0x37, 0x0A, 0x04, 0x0B, 0x37, 0x08, 0x04, 0x05, 0x37, 0x06, 0x04, 0x03, 0x37, 0x04, 0x04, 0x0A, 0x37, 0x02, 0x04, 0x00, 0x37, 0x03, 0x30, 0x00, 0x31, 0x07, 0x32, 0xFF, 0xE3, 0x1D, 0xFD, 0x94, 0x10, 0x04, 0x35, 0x02, 0x36, 0xFF, 0xE3, 0x52, 0xFD, 0x60, 0x84, 0x4E, 0x69, 0x04, 0x0C, 0x1F, 0x6C, 0xFF, 0xE3, 0x7A, 0xFE, 0x60, 0x84, 0x8E, 0x69, 0xF6, 0x0F, 0x6D, 0x10, 0x40, 0x83, 0x81, 0x3A, 0x40, 0xA3, 0x8C, 0x10, 0x62, 0x94, 0x70, 0x4B, 0x70, 0x43, 0x62, 0xB4, 0x63, 0x94, 0x70, 0x4B, 0x70, 0x43, 0x63, 0xB4, 0x0A, 0x30, 0x00, 0xE0, 0xC5, 0x06, 0x0B, 0x30, 0x00, 0xE0, 0xC2, 0x06, 0x60, 0x94, 0x98, 0x3B, 0x60, 0xB4, 0x60, 0x94, 0x08, 0x04, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x20, 0x00, 0xA0, 0x00, 0x40, 0xFF, 0x07, 0xE0, 0xFF, 0x98, 0x3B, 0x60, 0xB4, 0x03, 0x30, 0x00, 0x31, 0x06, 0x32, 0xFF, 0xE3, 0xE9, 0xFC, 0x60, 0x98, 0x01, 0x3B, 0x04, 0x08, 0x07, 0x30, 0xFF, 0xE3, 0x0D, 0xFD, 0x01, 0x14, 0x94, 0x14, 0x00, 0x00, 0x63, 0x10, 0x00, 0x93, 0x02, 0x48, 0x00, 0xE4, 0x01, 0x20, 0x3C, 0x78, 0xFC, 0x00, 0x00, 0x20, 0xD4, 0x14, 0x36, 0x16, 0x7C, 0x12, 0x00, 0x93, 0x00, 0xB8, 0x81, 0x93, 0xA2, 0x93, 0xC3, 0x93, 0x64, 0x93, 0x62, 0xB8, 0x04, 0x1F, 0xA4, 0x32, 0x41, 0x42, 0x1F, 0x6C, 0x00, 0x31, 0x00, 0xE0, 0x07, 0x08, 0x40, 0x31, 0x2F, 0xB7, 0x75, 0x12, 0x55, 0x12, 0x40, 0xB3, 0x20, 0x33, 0x61, 0xB8, 0xF4, 0x12, 0xA3, 0xB8, 0x53, 0x6D, 0x20, 0x34, 0x00, 0x30, 0x00, 0xE0, 0xF7, 0x00, 0x00, 0xE4, 0x01, 0x20, 0x00, 0x74, 0x60, 0x97, 0x60, 0xC4, 0x20, 0x08, 0x00, 0xB7, 0x01, 0x30, 0x00, 0xE0, 0xED, 0x00, 0x00, 0xE4, 0x01, 0x20, 0x00, 0x74, 0x61, 0x97, 0x60, 0xC4, 0x20, 0x08, 0x01, 0xB7, 0x00, 0x2C, 0x10, 0x75, 0x40, 0x3C, 0xE9, 0x0B, 0x17, 0x6D, 0xA3, 0x98, 0x63, 0x12, 0x00, 0x37, 0xE0, 0xB3, 0x44, 0x12, 0x20, 0x92, 0x04, 0x1B, 0x20, 0xB3, 0x41, 0x92, 0x41, 0xB3, 0x80, 0x30, 0x18, 0x40, 0x02, 0xB3, 0x23, 0xDC, 0x50, 0x20, 0x43, 0xDC, 0x51, 0x20, 0x8F, 0x6C, 0xE3, 0xE4, 0xFF, 0x00, 0x08, 0x93, 0x2D, 0x93, 0x05, 0x6C, 0x22, 0x93, 0x05, 0x6C, 0x20, 0x93, 0x41, 0x6C, 0x21, 0xC4, 0x01, 0x49, 0x30, 0xB3, 0x03, 0x23, 0xCE, 0x65, 0xF4, 0x0B, 0x00, 0x33, 0x00, 0x98, 0x13, 0x3B, 0x0A, 0x08, 0x86, 0xC4, 0x47, 0x20, 0x85, 0xC4, 0x21, 0x20, 0x5C, 0x6C, 0x20, 0xB8, 0x31, 0x11, 0x21, 0xB8, 0x25, 0x04, 0x27, 0x37, 0xDC, 0x64, 0x08, 0x0C, 0x85, 0xC4, 0x41, 0x24, 0x59, 0x6C, 0x20, 0xB8, 0x2D, 0x11, 0x21, 0xB8, 0x1B, 0x04, 0x3B, 0x37, 0xDC, 0x64, 0x0B, 0x0C, 0xA6, 0xC4, 0x27, 0x24, 0xD0, 0x69, 0xA6, 0xC4, 0x21, 0x20, 0x5C, 0x6C, 0x20, 0xB8, 0x28, 0x11, 0x21, 0xB8, 0x0E, 0x04, 0x4F, 0x31, 0xC4, 0x64, 0x08, 0x0C, 0x85, 0xC4, 0x41, 0x24, 0x59, 0x6C, 0x20, 0xB8, 0xE4, 0x11, 0xE1, 0xB8, 0x04, 0x04, 0x00, 0x31, 0x21, 0xB8, 0x20, 0xB8, 0xE0, 0x92, 0x22, 0x98, 0xE4, 0x5F, 0xA0, 0xC4, 0x01, 0x49, 0x24, 0x5F, 0xE0, 0x98, 0x3C, 0x59, 0xE1, 0x98, 0x3C, 0x59, 0x00, 0x23, 0xCC, 0x74, 0x03, 0x22, 0x50, 0x37, 0xCE, 0x65, 0x09, 0x0C, 0xC4, 0xC7, 0x07, 0x49, 0x03, 0x6D, 0xC2, 0xB8, 0x97, 0x6D, 0x5F, 0x6D, 0x07, 0x6C, 0xBA, 0x07, 0x00, 0xB8, 0x6D, 0x10, 0x40, 0x93, 0x28, 0x59, 0x20, 0xB3, 0x41, 0x93, 0x40, 0x5A, 0x41, 0xB3, 0x42, 0x93, 0x40, 0x5A, 0x42, 0xB3, 0x43, 0x93, 0xA8, 0x5D, 0xA3, 0xB3, 0x44, 0x93, 0xC8, 0x5E, 0xC4, 0xB3, 0x68, 0x10, 0x04, 0x1A, 0x02, 0xD8, 0x50, 0x20, 0x00, 0xB3, 0x42, 0xD8, 0x51, 0x20, 0x41, 0xB3, 0x16, 0x16, 0x94, 0x14, 0x00, 0x00, 0x00, 0x20, 0x00, 0xA0, 0x00, 0x40, 0x80, 0xFE, 0xFE, 0x00, 0x00, 0x01, 0x00, 0x20, 0x99, 0x79, 0x82, 0x5A, 0xA1, 0xEB, 0xD9, 0x6E, 0xDC, 0xBC, 0x1B, 0x8F, 0xD6, 0xC1, 0x62, 0xCA, 0x70, 0x10, 0x40, 0x93, 0x40, 0xB0, 0x41, 0x93, 0x41, 0xB0, 0x42, 0x93, 0x42, 0xB0, 0x43, 0x93, 0x43, 0xB0, 0x64, 0x93, 0x64, 0xB0, 0x3C, 0x78, 0x6B, 0x10, 0x40, 0x93, 0x40, 0xB0, 0x61, 0x93, 0x60, 0xB1, 0x3C, 0x78, 0x3C, 0x78, 0x00, 0x00, 0xD0, 0x14, 0xFF, 0xE3, 0x97, 0xFB, 0x67, 0x10, 0x03, 0xC4, 0x40, 0x08, 0x60, 0x90, 0x40, 0x3B, 0x02, 0x0C, 0xCD, 0x7B, 0x90, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x01, 0x00, 0x20, 0xA4, 0x39, 0x00, 0x00, 0xC1, 0x14, 0x00, 0x33, 0x09, 0x10, 0x01, 0x31, 0x04, 0xEA, 0x10, 0x27, 0x05, 0x04, 0x00, 0x23, 0xCD, 0x74, 0x0E, 0x65, 0x04, 0x0C, 0x40, 0x90, 0x4A, 0x68, 0xFA, 0x0F, 0x63, 0x10, 0x04, 0x93, 0x00, 0xE4, 0xFF, 0x23, 0x81, 0x14, 0x00, 0x00, 0x00, 0x80, 0x00, 0x40, 0x74, 0x10, 0x02, 0xEA, 0xCC, 0x07, 0x40, 0xB3, 0x3C, 0x78, 0x00, 0x00, 0xD0, 0x14, 0x71, 0x10, 0x00, 0x32, 0x40, 0xB3, 0xFF, 0xE3, 0xF6, 0xFF, 0x90, 0x14, 0x00, 0x00, 0x00, 0x74, 0x6D, 0x10, 0x00, 0x32, 0x40, 0xB3, 0x03, 0x6C, 0x42, 0x40, 0x42, 0xEC, 0x80, 0x07, 0x40, 0xB3, 0x03, 0x6C, 0x4F, 0x6C, 0x01, 0x32, 0x60, 0x91, 0x8E, 0x68, 0xFE, 0x0F, 0x66, 0x10, 0x40, 0x93, 0x89, 0x3A, 0x40, 0xB3, 0x65, 0x10, 0x0C, 0x58, 0x02, 0x40, 0x00, 0x90, 0x00, 0xE4, 0xFF, 0x23, 0x3C, 0x78, 0x00, 0x80, 0x00, 0x40, 0x01, 0x20, 0x00, 0x10, 0x00, 0x74, 0x67, 0x10, 0x60, 0x93, 0x82, 0x74, 0xDF, 0x3A, 0x02, 0x0C, 0x7F, 0x30, 0x45, 0x10, 0xC8, 0x68, 0x09, 0x40, 0x0C, 0x6C, 0x62, 0x10, 0x00, 0xB3, 0x3C, 0x78, 0x00, 0xA0, 0x00, 0x40, 0xFF, 0x01, 0xFF, 0xFF, 0x00, 0x74, 0x7B, 0x11, 0x60, 0x93, 0x82, 0x74, 0xDF, 0x3A, 0x02, 0x0C, 0x7F, 0x30, 0x59, 0x11, 0xC8, 0x68, 0x11, 0x40, 0x0C, 0x6C, 0x76, 0x11, 0x00, 0xB3, 0x3C, 0x78, 0x74, 0x11, 0x41, 0x93, 0x42, 0xE4, 0x0F, 0x30, 0x00, 0xE4, 0x0F, 0x20, 0x80, 0x6C, 0x41, 0xB3, 0x3C, 0x78, 0x00, 0x00, 0xD0, 0x14, 0x7D, 0x30, 0xFF, 0xE3, 0xD4, 0xFF, 0x7F, 0x30, 0xFF, 0xE3, 0xE3, 0xFF, 0x6C, 0x11, 0x40, 0x93, 0xA7, 0x3A, 0x40, 0xB3, 0x40, 0x93, 0xBC, 0x3A, 0x40, 0xB3, 0x40, 0x93, 0x90, 0x3A, 0x40, 0xB3, 0x40, 0x93, 0xBB, 0x3A, 0x40, 0xB3, 0x40, 0x93, 0x98, 0x3A, 0x40, 0xB3, 0x40, 0x93, 0xBD, 0x3A, 0x40, 0xB3, 0x40, 0x93, 0x98, 0x3A, 0x40, 0xB3, 0x41, 0x93, 0x8B, 0x3A, 0x41, 0xB3, 0x41, 0x93, 0xAA, 0x3A, 0x41, 0xB3, 0x40, 0x93, 0xA4, 0x3A, 0x40, 0xB3, 0x41, 0x93, 0xA9, 0x3A, 0x41, 0xB3, 0x40, 0x93, 0x81, 0x3A, 0x40, 0xB3, 0x08, 0x30, 0xFF, 0xE3, 0xC9, 0xFF, 0x90, 0x14, 0x77, 0x10, 0x61, 0x93, 0x63, 0xE4, 0x01, 0x20, 0x40, 0x3B, 0x00, 0xC4, 0x00, 0x05, 0x03, 0x40, 0x73, 0x10, 0x61, 0x93, 0xC1, 0x3B, 0x03, 0x0C, 0x08, 0x20, 0x00, 0x74, 0x70, 0x10, 0x61, 0x93, 0xC2, 0x3B, 0x03, 0x0C, 0x10, 0x20, 0x00, 0x74, 0x6D, 0x10, 0x61, 0x93, 0xC3, 0x3B, 0x03, 0x0C, 0x21, 0x20, 0x00, 0x74, 0x3C, 0x78, 0x00, 0x00, 0x32, 0x32, 0x69, 0x10, 0x01, 0x30, 0x20, 0x93, 0x40, 0x6C, 0x20, 0xB3, 0x03, 0x6C, 0x03, 0x6C, 0x20, 0x93, 0x80, 0x39, 0x20, 0xB3, 0x03, 0x6C, 0x00, 0x2A, 0x88, 0x74, 0x40, 0x3A, 0xF4, 0x0B, 0x3C, 0x78, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x40, 0xFF, 0xFF, 0x01, 0xFF, 0x60, 0x14, 0x62, 0x14, 0xD0, 0x14, 0x7D, 0x10, 0x60, 0x83, 0xCC, 0x74, 0x40, 0x3B, 0x13, 0x0C, 0x5B, 0x10, 0x60, 0x92, 0x8B, 0x3B, 0x60, 0xB2, 0x78, 0x10, 0x21, 0x93, 0x00, 0x81, 0x01, 0xB2, 0x00, 0xEA, 0xC1, 0x01, 0x00, 0xB2, 0x00, 0x21, 0x21, 0xB3, 0x40, 0x83, 0x00, 0x2A, 0x88, 0x74, 0x40, 0xA3, 0x1D, 0x04, 0x72, 0x10, 0x00, 0x32, 0x40, 0xB3, 0x70, 0x10, 0x48, 0xA3, 0xFF, 0xE3, 0xFB, 0xEA, 0xFF, 0xE3, 0xCB, 0xFA, 0x40, 0x38, 0x04, 0x08, 0x01, 0x30, 0xFF, 0xE3, 0x9E, 0xFA, 0x6A, 0x10, 0x09, 0x83, 0x40, 0x38, 0x06, 0x0C, 0xFF, 0xE3, 0x68, 0xFF, 0x00, 0x32, 0x67, 0x10, 0x49, 0xA3, 0x03, 0x30, 0x00, 0x31, 0x07, 0x32, 0xFF, 0xE3, 0xE4, 0xFA, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0x00, 0x00, 0x08, 0x01, 0x00, 0x20, 0x00, 0x60, 0x00, 0x40, 0x00, 0x32, 0x78, 0x10, 0x40, 0xB3, 0x78, 0x10, 0x48, 0xA3, 0x41, 0xB3, 0x40, 0xA3, 0x3C, 0x78, 0x75, 0x10, 0x68, 0x83, 0xCC, 0x74, 0x40, 0x3B, 0x00, 0xC4, 0x00, 0x05, 0x3C, 0x78, 0x00, 0x00, 0xD0, 0x14, 0x00, 0x74, 0x70, 0x10, 0x21, 0xB3, 0x00, 0xA3, 0x01, 0x32, 0x48, 0xA3, 0xFF, 0xE3, 0x99, 0xF2, 0x41, 0x38, 0x0B, 0x08, 0x6D, 0x10, 0x41, 0x93, 0x42, 0xE4, 0x0F, 0x20, 0x6A, 0x10, 0x49, 0xA3, 0x0F, 0x30, 0xFF, 0xE3, 0x32, 0xFF, 0x04, 0x04, 0x00, 0x32, 0x66, 0x10, 0x49, 0xA3, 0xFF, 0xE3, 0x5A, 0xFB, 0x63, 0x10, 0x02, 0xEA, 0xC1, 0x09, 0x40, 0xB3, 0x90, 0x14, 0x00, 0x00, 0x00, 0x60, 0x00, 0x40, 0x08, 0x01, 0x00, 0x20, 0x00, 0xA0, 0x00, 0x40, 0x64, 0x10, 0x41, 0x93, 0x01, 0x31, 0x01, 0xC4, 0x20, 0x40, 0x08, 0x6C, 0x01, 0xB3, 0x3C, 0x78, 0x00, 0x50, 0x00, 0x40, 0x7E, 0x11, 0x61, 0x93, 0x03, 0xC4, 0x40, 0x40, 0x00, 0xE4, 0x01, 0x20, 0x3C, 0x78, 0x00, 0x00, 0x5A, 0x11, 0x21, 0x92, 0x00, 0x33, 0x01, 0x2B, 0xC3, 0x70, 0xC4, 0x68, 0x61, 0xB2, 0x3C, 0x78, 0xD0, 0x14, 0x76, 0x11, 0x61, 0x93, 0x01, 0x32, 0x80, 0x70, 0xCA, 0x68, 0x04, 0x0C, 0xFF, 0xE3, 0xF1, 0xFF, 0x03, 0x04, 0xFF, 0xE3, 0xDC, 0xFF, 0x90, 0x14, 0x00, 0x00, 0x6F, 0x11, 0x43, 0x93, 0x01, 0x31, 0x01, 0xC4, 0x20, 0x40, 0x80, 0x6C, 0x43, 0xB3, 0x45, 0x93, 0x08, 0x6C, 0x05, 0xB3, 0x3C, 0x78, 0x00, 0x00, 0x69, 0x11, 0x43, 0x93, 0x01, 0x31, 0x01, 0xC4, 0x20, 0x40, 0x80, 0x6C, 0x43, 0xB3, 0x45, 0x93, 0x80, 0x6C, 0x45, 0xB3, 0x47, 0x93, 0x08, 0x6C, 0x07, 0xB3, 0x3C, 0x78, 0x62, 0x11, 0x20, 0x93, 0x00, 0x32, 0x01, 0x2A, 0x80, 0x70, 0x48, 0x68, 0x20, 0xB3, 0x23, 0x93, 0x84, 0x68, 0x43, 0xB3, 0x3C, 0x78, 0x00, 0x00, 0x7C, 0x10, 0x42, 0x93, 0x01, 0x31, 0x01, 0xC4, 0x20, 0x40, 0x08, 0x6C, 0x02, 0xB3, 0x3C, 0x78, 0xD0, 0x14, 0x03, 0x30, 0xFF, 0xE3, 0xF6, 0xFF, 0x00, 0x30, 0xFF, 0xE3, 0xF3, 0xFF, 0x02, 0x30, 0xFF, 0xE3, 0xF0, 0xFF, 0x03, 0x30, 0xFF, 0xE3, 0xE1, 0xFF, 0x00, 0x30, 0xFF, 0xE3, 0xDE, 0xFF, 0x02, 0x30, 0xFF, 0xE3, 0xDB, 0xFF, 0x01, 0x30, 0xFF, 0xE3, 0xE4, 0xFF, 0x01, 0x30, 0xFF, 0xE3, 0xD5, 0xFF, 0x00, 0x30, 0xFF, 0xE3, 0xA2, 0xFF, 0x01, 0x30, 0xFF, 0xE3, 0x8D, 0xFF, 0x00, 0x30, 0xFF, 0xE3, 0x9C, 0xFF, 0x02, 0x30, 0xFF, 0xE3, 0x99, 0xFF, 0x90, 0x14, 0x65, 0x10, 0x60, 0x93, 0x45, 0x10, 0x00, 0x5A, 0x40, 0x80, 0xC8, 0x68, 0x40, 0x3B, 0x00, 0xC4, 0x00, 0x05, 0x3C, 0x78, 0x00, 0x50, 0x00, 0x40, 0xC0, 0x39, 0x00, 0x00, 0x60, 0x14, 0x62, 0x14, 0x65, 0x10, 0x40, 0x93, 0xA3, 0x3A, 0x40, 0xB3, 0x64, 0x10, 0x00, 0x32, 0x40, 0xB3, 0x63, 0x14, 0x61, 0x14, 0x00, 0x00, 0x00, 0x70, 0x00, 0x40, 0x00, 0x10, 0x00, 0x40, 0x60, 0x14, 0x62, 0x14, 0xD1, 0x14, 0x63, 0x13, 0x63, 0x83, 0x63, 0xE4, 0x3F, 0x30, 0xCC, 0x74, 0x80, 0x32, 0x8E, 0x64, 0x78, 0x09, 0x60, 0x13, 0x41, 0x93, 0x7E, 0x12, 0x62, 0x8B, 0xCD, 0x74, 0x89, 0x74, 0x8C, 0x64, 0x08, 0x08, 0x7C, 0x12, 0x41, 0x93, 0x7A, 0x12, 0x62, 0x8B, 0x4D, 0x5A, 0x88, 0x74, 0x07, 0x04, 0x77, 0x12, 0x42, 0x8B, 0x77, 0x12, 0x61, 0x93, 0x4D, 0x5A, 0x88, 0x74, 0x04, 0x33, 0x8C, 0x64, 0x06, 0x08, 0x73, 0x12, 0x40, 0x83, 0xA0, 0x3A, 0x40, 0xA3, 0x05, 0x04, 0x70, 0x12, 0x40, 0x83, 0x80, 0x3A, 0x40, 0xA3, 0x6E, 0x12, 0x60, 0x83, 0x41, 0x4B, 0x63, 0xE4, 0x01, 0x20, 0x42, 0xE4, 0x01, 0x20, 0x8E, 0x64, 0x20, 0x08, 0x2A, 0x12, 0x60, 0x81, 0x43, 0x4B, 0x00, 0x22, 0x42, 0xE4, 0x0F, 0x20, 0x03, 0x42, 0x63, 0xE4, 0x78, 0x30, 0xC0, 0x6C, 0x60, 0xA1, 0x2C, 0x3A, 0x41, 0x08, 0x60, 0x81, 0x63, 0xE4, 0x04, 0x20, 0xCC, 0x74, 0x40, 0x3B, 0x07, 0x08, 0x40, 0x81, 0x42, 0xE4, 0x78, 0x30, 0xA6, 0x3A, 0x40, 0xA1, 0x07, 0x04, 0x7D, 0x11, 0x40, 0x83, 0x42, 0xE4, 0x78, 0x30, 0xA5, 0x3A, 0x40, 0xA3, 0x7A, 0x11, 0x60, 0x83, 0x63, 0xE4, 0x04, 0x20, 0xCC, 0x74, 0x40, 0x3B, 0x13, 0x08, 0x77, 0x11, 0x60, 0x83, 0x63, 0x4B, 0x63, 0xE4, 0x0F, 0x20, 0x25, 0x3B, 0x06, 0x0C, 0x73, 0x11, 0x40, 0x83, 0xA2, 0x3A, 0x40, 0xA3, 0x17, 0x04, 0x71, 0x11, 0x42, 0x83, 0x80, 0x3A, 0x81, 0x3A, 0x42, 0xA3, 0x11, 0x04, 0x6E, 0x11, 0x60, 0x83, 0x63, 0x4B, 0x63, 0xE4, 0x0F, 0x20, 0x25, 0x3B, 0x0A, 0x0C, 0x6A, 0x11, 0x42, 0x83, 0x80, 0x3A, 0x81, 0x3A, 0xA0, 0x3A, 0x42, 0xA3, 0x40, 0x83, 0x82, 0x3A, 0x40, 0xA3, 0x66, 0x11, 0x40, 0x83, 0x42, 0xE4, 0x78, 0x30, 0x40, 0xA3, 0x63, 0x11, 0x40, 0x83, 0x22, 0xE4, 0x01, 0x20, 0x21, 0x41, 0x81, 0x3A, 0x84, 0x6C, 0x40, 0xA3, 0x62, 0x83, 0x63, 0xE4, 0x03, 0x20, 0x21, 0x3B, 0x2D, 0x0D, 0x5D, 0x10, 0x42, 0x82, 0x22, 0xE4, 0x04, 0x20, 0x44, 0x74, 0x40, 0x39, 0x43, 0x08, 0x79, 0x10, 0x26, 0x83, 0x21, 0x49, 0x26, 0xA3, 0x42, 0xE4, 0x03, 0x20, 0x41, 0x3A, 0x05, 0x08, 0x00, 0x30, 0x7F, 0x28, 0x40, 0x6C, 0x26, 0xA3, 0x33, 0x10, 0x62, 0x81, 0x43, 0x4B, 0x00, 0x22, 0x42, 0xE4, 0x0F, 0x20, 0x03, 0x42, 0x63, 0xE4, 0x78, 0x30, 0xC0, 0x6C, 0x62, 0xA1, 0x48, 0x3A, 0xC5, 0x08, 0x6E, 0x10, 0x40, 0x93, 0x83, 0x3A, 0x40, 0xB3, 0x66, 0x81, 0x26, 0x5B, 0x44, 0x74, 0x01, 0x32, 0x48, 0x64, 0x04, 0x08, 0x3C, 0x32, 0x8E, 0x64, 0x05, 0x08, 0x80, 0x30, 0xFF, 0xE3, 0xF7, 0xF8, 0xB4, 0x04, 0x40, 0x3B, 0x0B, 0x08, 0x80, 0x30, 0x02, 0x40, 0xFF, 0xE3, 0xF0, 0xF8, 0xAD, 0x04, 0x00, 0x00, 0x14, 0x01, 0x00, 0x20, 0x00, 0x70, 0x00, 0x40, 0x55, 0x32, 0x8E, 0x64, 0x06, 0x08, 0x80, 0x30, 0x01, 0x40, 0xFF, 0xE3, 0xE3, 0xF8, 0xA0, 0x04, 0x40, 0x30, 0xFF, 0xE3, 0xDF, 0xF8, 0x9C, 0x04, 0x24, 0x13, 0x23, 0x81, 0x21, 0xE4, 0x3E, 0x20, 0x40, 0x39, 0x8F, 0x0C, 0x21, 0x13, 0x21, 0x89, 0x27, 0x49, 0x21, 0xE4, 0x03, 0x20, 0x41, 0x39, 0x1A, 0x0C, 0x21, 0x39, 0x04, 0x0C, 0x40, 0x39, 0x07, 0x0C, 0x8A, 0x04, 0x42, 0x39, 0x5E, 0x0C, 0x43, 0x39, 0x86, 0x08, 0x66, 0x04, 0x42, 0xE4, 0x03, 0x20, 0x40, 0x3A, 0x88, 0x08, 0x77, 0x12, 0x41, 0x8B, 0x87, 0x3A, 0x88, 0x3A, 0xA7, 0x3A, 0x41, 0xAB, 0x40, 0x83, 0x42, 0xE4, 0x7F, 0x20, 0x40, 0xA3, 0x76, 0x04, 0x71, 0x12, 0x62, 0x93, 0x20, 0x83, 0x44, 0x74, 0x21, 0x49, 0x20, 0xA3, 0x42, 0xE4, 0x03, 0x20, 0x41, 0x3A, 0x12, 0x08, 0x40, 0x83, 0x00, 0x31, 0x7F, 0x29, 0x84, 0x6C, 0x88, 0x74, 0x40, 0xA3, 0x49, 0x12, 0x20, 0x82, 0x07, 0x49, 0x00, 0xE4, 0x01, 0x20, 0x02, 0x6C, 0x07, 0x40, 0x21, 0xE4, 0x7F, 0x20, 0x40, 0x6C, 0x20, 0xA2, 0x04, 0x12, 0x42, 0x80, 0x23, 0x4A, 0x00, 0x21, 0x21, 0xE4, 0x0F, 0x20, 0x83, 0x41, 0x42, 0xE4, 0x78, 0x30, 0x90, 0x6C, 0x42, 0xA0, 0x48, 0x39, 0x4E, 0x08, 0x22, 0x80, 0x21, 0xE4, 0x78, 0x30, 0x22, 0xA0, 0x21, 0x88, 0x87, 0x39, 0x88, 0x39, 0xA8, 0x39, 0x21, 0xA8, 0x4C, 0x80, 0x40, 0x3A, 0x13, 0x08, 0x01, 0x31, 0x2C, 0xA0, 0x23, 0x80, 0x21, 0xE4, 0x3E, 0x20, 0x3E, 0x32, 0x86, 0x64, 0x0B, 0x08, 0x20, 0x83, 0x44, 0x74, 0x24, 0x49, 0x01, 0x21, 0x21, 0x41, 0x43, 0x80, 0x42, 0xE4, 0x3E, 0x30, 0x84, 0x6C, 0x43, 0xA0, 0x6E, 0x11, 0x42, 0x93, 0x00, 0x22, 0x42, 0xB3, 0x2B, 0x04, 0x4C, 0x11, 0x40, 0x82, 0x47, 0x4A, 0x8E, 0x64, 0x2D, 0x08, 0x69, 0x11, 0x41, 0x8B, 0x42, 0xEC, 0x80, 0x01, 0x41, 0xAB, 0x20, 0x04, 0x42, 0xE4, 0x03, 0x20, 0x41, 0x3A, 0x23, 0x08, 0x64, 0x11, 0x21, 0x8B, 0x87, 0x39, 0x88, 0x39, 0x00, 0xEA, 0x7F, 0xFE, 0x01, 0xC4, 0x22, 0x20, 0x21, 0xAB, 0x48, 0x4A, 0x21, 0x4A, 0x1E, 0x21, 0x21, 0xE4, 0x1F, 0x20, 0x21, 0x41, 0x42, 0xE4, 0x3E, 0x30, 0x84, 0x6C, 0x43, 0xA3, 0x08, 0x04, 0x7B, 0x10, 0x40, 0x93, 0x83, 0x3A, 0x40, 0xB3, 0x02, 0x30, 0xFF, 0xE3, 0x43, 0xF8, 0x77, 0x10, 0x42, 0x83, 0x80, 0x3A, 0x81, 0x3A, 0xA1, 0x3A, 0x42, 0xA3, 0x42, 0x04, 0x74, 0x10, 0x40, 0x93, 0x83, 0x3A, 0x40, 0xB3, 0x40, 0x30, 0xFF, 0xE3, 0x35, 0xF8, 0x3A, 0x04, 0x40, 0x3B, 0x0C, 0x08, 0x6F, 0x10, 0x41, 0x93, 0x89, 0x74, 0x6D, 0x10, 0x42, 0xAB, 0x43, 0x83, 0x42, 0xE4, 0x3F, 0x20, 0xA6, 0x3A, 0x43, 0xA3, 0x2D, 0x04, 0x40, 0x32, 0x8E, 0x64, 0x2A, 0x08, 0x68, 0x10, 0x41, 0x93, 0x66, 0x10, 0x62, 0x8B, 0xCD, 0x74, 0x89, 0x74, 0x8C, 0x64, 0x0E, 0x08, 0x64, 0x10, 0x41, 0x93, 0x62, 0x10, 0x62, 0x8B, 0x06, 0x04, 0x00, 0x00, 0x14, 0x01, 0x00, 0x20, 0x00, 0x70, 0x00, 0x40, 0x4D, 0x5A, 0x88, 0x74, 0x07, 0x04, 0x76, 0x12, 0x42, 0x8B, 0x76, 0x12, 0x61, 0x93, 0x4D, 0x5A, 0x88, 0x74, 0x04, 0x33, 0x8C, 0x64, 0x0C, 0x08, 0x71, 0x12, 0x43, 0x83, 0x42, 0xE4, 0x3F, 0x20, 0x00, 0x31, 0x7F, 0x29, 0x84, 0x6C, 0x43, 0xA3, 0x09, 0x30, 0xFF, 0xE3, 0x6F, 0xF8, 0xEE, 0xD9, 0x01, 0x20, 0x80, 0x98, 0x02, 0x14, 0x63, 0x14, 0x61, 0x14, 0x00, 0x00, 0x69, 0x12, 0x00, 0x32, 0x40, 0xB3, 0x20, 0x93, 0x21, 0xEC, 0x03, 0x00, 0x20, 0xB3, 0x20, 0x93, 0xA2, 0x39, 0x20, 0xB3, 0x63, 0x12, 0x46, 0xA3, 0x00, 0x31, 0x2C, 0xA3, 0x42, 0xAB, 0x40, 0x83, 0x42, 0xE4, 0x78, 0x20, 0x42, 0xE4, 0x78, 0x30, 0x40, 0xA3, 0x42, 0x83, 0x80, 0x3A, 0x81, 0x3A, 0x42, 0xA3, 0x41, 0x8B, 0x87, 0x3A, 0x88, 0x3A, 0x00, 0xEA, 0x7F, 0xFE, 0x02, 0xC4, 0x21, 0x20, 0x28, 0x49, 0x21, 0xE4, 0x3E, 0x30, 0x19, 0x11, 0x80, 0x68, 0x42, 0xE4, 0x78, 0x30, 0x42, 0xA3, 0x21, 0xE4, 0x3E, 0x20, 0x23, 0xA3, 0x3C, 0x78, 0xD3, 0x14, 0x00, 0x74, 0x44, 0x74, 0x71, 0x11, 0x63, 0x83, 0x63, 0xE4, 0x3F, 0x30, 0xCC, 0x74, 0xC0, 0x34, 0x0E, 0x65, 0x46, 0x0C, 0x6D, 0x11, 0x42, 0xB3, 0x40, 0x83, 0x02, 0x35, 0x94, 0x6C, 0x42, 0xE4, 0x78, 0x30, 0x82, 0x3A, 0x40, 0xA3, 0x81, 0x8B, 0x87, 0x3C, 0x88, 0x3C, 0x06, 0xEA, 0x7F, 0xFE, 0xC4, 0xC4, 0x22, 0x20, 0xC8, 0x11, 0x18, 0x69, 0x14, 0x6D, 0x84, 0xE4, 0x78, 0x30, 0x00, 0x35, 0xAC, 0xA3, 0x00, 0xE4, 0x01, 0x20, 0x02, 0x40, 0x82, 0x3C, 0x10, 0x6C, 0x02, 0xA3, 0x21, 0xE4, 0x1F, 0x20, 0x21, 0x41, 0x48, 0x4A, 0x42, 0xE4, 0x3E, 0x30, 0x84, 0x6C, 0x42, 0xE4, 0x3F, 0x20, 0x43, 0xA3, 0x7A, 0x10, 0x40, 0x93, 0x83, 0x3A, 0x40, 0xB3, 0x7B, 0x10, 0x00, 0x32, 0x40, 0xB3, 0x40, 0x93, 0xA5, 0x3A, 0x40, 0xB3, 0x40, 0x93, 0x40, 0xB3, 0x40, 0x93, 0xA1, 0x3A, 0x40, 0xB3, 0x40, 0x93, 0xA6, 0x3A, 0x40, 0xB3, 0xFA, 0x32, 0x42, 0x42, 0x41, 0xB3, 0x42, 0xB3, 0x40, 0x93, 0xA9, 0x3A, 0x40, 0xB3, 0x09, 0x30, 0x00, 0x31, 0x2D, 0x32, 0xFF, 0xE3, 0xCC, 0xF7, 0x93, 0x14, 0x00, 0x00, 0xD0, 0x14, 0x6A, 0x10, 0x40, 0x93, 0x83, 0x3A, 0x40, 0xB3, 0x04, 0x30, 0xFF, 0xE3, 0x6E, 0xF7, 0x90, 0x14, 0x00, 0x00, 0x68, 0x10, 0x00, 0x32, 0x40, 0xB3, 0x64, 0x10, 0x40, 0x93, 0x83, 0x3A, 0x40, 0xB3, 0x3C, 0x78, 0x14, 0x01, 0x00, 0x20, 0x00, 0x70, 0x00, 0x40, 0x7B, 0xFE, 0xFF, 0xFF, 0x7C, 0xFE, 0xFF, 0xFF, 0x00, 0x10, 0x00, 0x40, 0x60, 0x14, 0x62, 0x14, 0xC4, 0x14, 0x21, 0x14, 0x02, 0x32, 0x4E, 0xDC, 0x00, 0x00, 0x00, 0x31, 0x2E, 0xDC, 0x01, 0x00, 0x4E, 0xDC, 0x02, 0x00, 0x00, 0x36, 0x03, 0x32, 0x00, 0x35, 0x3B, 0x10, 0x9C, 0x30, 0x04, 0x40, 0x3B, 0x6D, 0x20, 0x04, 0x00, 0x33, 0x60, 0xB1, 0x40, 0x3D, 0x04, 0x0C, 0x41, 0x3D, 0x1A, 0x08, 0x0B, 0x04, 0x00, 0xB1, 0x10, 0x35, 0x60, 0x91, 0x4E, 0x69, 0xFE, 0x0F, 0x60, 0x91, 0x8B, 0x3B, 0x60, 0xB1, 0x01, 0x35, 0x0F, 0x04, 0x78, 0x5C, 0x60, 0x83, 0x61, 0xB1, 0x00, 0x26, 0x98, 0x75, 0xE0, 0x33, 0x61, 0x43, 0x60, 0xB1, 0x10, 0x37, 0x60, 0x91, 0xCE, 0x69, 0xFE, 0x0F, 0x00, 0x2A, 0x88, 0x74, 0x40, 0x3A, 0xE0, 0x0B, 0x00, 0x33, 0x60, 0xB1, 0x03, 0xEA, 0x98, 0x3A, 0x02, 0xEA, 0xFF, 0xFF, 0x00, 0x2B, 0xCD, 0x74, 0x8E, 0x64, 0xFD, 0x0B, 0x00, 0x36, 0x03, 0x32, 0x00, 0x35, 0xF1, 0x07, 0x00, 0x00, 0x00, 0x60, 0x00, 0x40, 0x3C, 0x78, 0x00, 0x00, 0x60, 0x14, 0x62, 0x14, 0xD0, 0x14, 0x00, 0xE0, 0x17, 0x02, 0xFF, 0xE3, 0x93, 0xF7, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0x60, 0x14, 0x62, 0x14, 0x63, 0x14, 0x61, 0x14, 0x60, 0x14, 0x62, 0x14, 0x63, 0x14, 0x61, 0x14, 0x60, 0x14, 0x62, 0x14, 0x63, 0x14, 0x61, 0x14, 0x60, 0x14, 0x62, 0x14, 0x63, 0x14, 0x61, 0x14, 0x60, 0x14, 0x62, 0x14, 0xD0, 0x14, 0xFF, 0xE3, 0x99, 0xF2, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0x60, 0x14, 0x62, 0x14, 0xD1, 0x14, 0x7C, 0x12, 0x62, 0x93, 0xC0, 0x3B, 0x18, 0x0C, 0x9A, 0x12, 0x61, 0x94, 0xA6, 0x3B, 0x61, 0xB4, 0x00, 0x30, 0xFF, 0xE3, 0xA2, 0xFC, 0x0A, 0x30, 0x00, 0xE0, 0xFD, 0x00, 0x60, 0x94, 0xB8, 0x3B, 0x60, 0xB4, 0x62, 0x94, 0x80, 0x3B, 0x62, 0xB4, 0x04, 0x30, 0xFF, 0xE3, 0xEC, 0xF6, 0x05, 0x30, 0xFF, 0xE3, 0x53, 0xF2, 0x20, 0x04, 0x6E, 0x12, 0x41, 0x93, 0xA6, 0x3A, 0x41, 0xB3, 0x62, 0x93, 0xC4, 0x3B, 0x08, 0x0C, 0x00, 0x30, 0xFF, 0xE3, 0x88, 0xFC, 0x05, 0x30, 0xFF, 0xE3, 0x45, 0xF2, 0x04, 0x04, 0x04, 0x30, 0xFF, 0xE3, 0x41, 0xF2, 0x86, 0x12, 0x62, 0x94, 0x63, 0xE4, 0x3F, 0x30, 0x62, 0xB4, 0x01, 0x30, 0xFF, 0xE3, 0xCF, 0xF6, 0x0A, 0x30, 0x00, 0xE0, 0xD4, 0x00, 0x60, 0x94, 0xB8, 0x3B, 0x60, 0xB4, 0xEE, 0xD9, 0x01, 0x20, 0x80, 0x98, 0x02, 0x14, 0x63, 0x14, 0x61, 0x14, 0x00, 0x00, 0x60, 0x14, 0x62, 0x14, 0xD1, 0x14, 0x9A, 0x11, 0x61, 0x94, 0xA6, 0x3B, 0x61, 0xB4, 0x04, 0x30, 0xFF, 0xE3, 0x22, 0xF2, 0x01, 0x30, 0xFF, 0xE3, 0xB5, 0xF6, 0x0B, 0x30, 0x00, 0xE0, 0xBA, 0x00, 0x60, 0x94, 0xBA, 0x3B, 0x60, 0xB4, 0x63, 0x94, 0x63, 0xE4, 0xFF, 0x30, 0x63, 0xB4, 0xEE, 0xD9, 0x01, 0x20, 0x80, 0x98, 0x02, 0x14, 0x63, 0x14, 0x61, 0x14, 0x00, 0x00, 0x60, 0x14, 0x62, 0x14, 0x63, 0x14, 0x61, 0x14, 0x60, 0x14, 0x62, 0x14, 0x63, 0x14, 0x61, 0x14, 0x60, 0x14, 0x62, 0x14, 0x63, 0x14, 0x61, 0x14, 0x60, 0x14, 0x62, 0x14, 0x63, 0x14, 0x61, 0x14, 0x60, 0x14, 0x62, 0x14, 0x63, 0x14, 0x61, 0x14, 0xD0, 0x14, 0x00, 0xE0, 0x47, 0x00, 0x00, 0xE0, 0x83, 0x00, 0x00, 0x30, 0x00, 0xE0, 0x88, 0x00, 0x01, 0x30, 0x00, 0xE0, 0x85, 0x00, 0x02, 0x30, 0x00, 0xE0, 0x82, 0x00, 0x0B, 0x30, 0x00, 0xE0, 0x7F, 0x00, 0x0A, 0x30, 0x00, 0xE0, 0x7C, 0x00, 0x07, 0x30, 0x00, 0xE0, 0x79, 0x00, 0x78, 0x10, 0x47, 0x93, 0xA0, 0x3A, 0x47, 0xB3, 0x09, 0x30, 0x00, 0xE0, 0x72, 0x00, 0x04, 0x30, 0x00, 0xE0, 0x77, 0x00, 0x06, 0x30, 0x00, 0xE0, 0x6C, 0x00, 0x06, 0x30, 0x01, 0x31, 0x00, 0xE0, 0x80, 0x00, 0x0A, 0x30, 0x00, 0x31, 0x00, 0xE0, 0x7C, 0x00, 0x0B, 0x30, 0x00, 0x31, 0x00, 0xE0, 0x78, 0x00, 0x09, 0x30, 0x00, 0x31, 0x00, 0xE0, 0x74, 0x00, 0x07, 0x30, 0x02, 0x31, 0x00, 0xE0, 0x70, 0x00, 0x01, 0x30, 0x01, 0x31, 0x00, 0xE0, 0x6C, 0x00, 0x00, 0x30, 0x03, 0x31, 0x00, 0xE0, 0x68, 0x00, 0x90, 0x14, 0x00, 0x00, 0x3C, 0x78, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x40, 0x00, 0x90, 0x00, 0x40, 0x71, 0x10, 0x52, 0x10, 0x60, 0xB2, 0x52, 0x10, 0x43, 0xDC, 0x00, 0x21, 0x51, 0x10, 0x43, 0xDC, 0x01, 0x21, 0x51, 0x10, 0x43, 0xDC, 0x02, 0x21, 0x50, 0x10, 0x43, 0xDC, 0x03, 0x21, 0x50, 0x10, 0x43, 0xDC, 0x04, 0x21, 0x4F, 0x10, 0x43, 0xDC, 0x05, 0x21, 0x4F, 0x10, 0x43, 0xDC, 0x06, 0x21, 0x4E, 0x10, 0x43, 0xDC, 0x07, 0x21, 0x00, 0x32, 0x43, 0xDC, 0x01, 0x23, 0x43, 0xDC, 0xC0, 0x20, 0x3C, 0x78, 0x00, 0x00, 0x00, 0xE0, 0x00, 0xE0, 0x24, 0x01, 0x00, 0x20, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, 0x0B, 0x0A, 0x09, 0x08, 0x0F, 0x0E, 0x0D, 0x0C, 0x13, 0x12, 0x11, 0x10, 0x17, 0x16, 0x15, 0x14, 0x1B, 0x1A, 0x19, 0x18, 0x1F, 0x1E, 0x1D, 0x1C, 0x61, 0x11, 0x60, 0x93, 0x01, 0x32, 0x02, 0xC4, 0x20, 0x40, 0x03, 0xDC, 0x80, 0x20, 0x3C, 0x78, 0x80, 0xC1, 0x20, 0x74, 0x3C, 0x78, 0x00, 0x00, 0x80, 0xC0, 0x20, 0x70, 0x3C, 0x78, 0x00, 0x00, 0x79, 0x10, 0x60, 0x93, 0x01, 0x32, 0x02, 0xC4, 0x20, 0x40, 0x03, 0xDC, 0x40, 0x20, 0x3C, 0x78, 0x75, 0x10, 0x60, 0x93, 0x01, 0x32, 0x02, 0xC4, 0x20, 0x40, 0x03, 0xDC, 0x60, 0x20, 0x3C, 0x78, 0x71, 0x10, 0x60, 0x93, 0x01, 0x32, 0x02, 0xC4, 0x20, 0x40, 0x03, 0xDC, 0xA0, 0x20, 0x3C, 0x78, 0xC1, 0x14, 0xDF, 0x38, 0x16, 0x08, 0x60, 0xE4, 0x03, 0x20, 0x63, 0x43, 0x4A, 0x10, 0x40, 0x92, 0x02, 0x50, 0x00, 0x74, 0xFF, 0x20, 0x02, 0xC4, 0x40, 0x08, 0x80, 0x90, 0xFF, 0x32, 0x8C, 0x70, 0x44, 0xC4, 0x42, 0x20, 0x40, 0xB0, 0x40, 0x90, 0x05, 0x23, 0x4C, 0x70, 0x48, 0x6C, 0x20, 0xB0, 0x81, 0x14, 0x00, 0x00, 0x24, 0x01, 0x00, 0x20, 0x6B, 0x10, 0x00, 0x32, 0x40, 0xB3, 0x40, 0x93, 0xA5, 0x3A, 0x40, 0xB3, 0x40, 0x93, 0x40, 0xB3, 0x40, 0x93, 0x40, 0xB3, 0xFF, 0x32, 0x41, 0xB3, 0x42, 0xB3, 0x66, 0x10, 0x42, 0x93, 0xA2, 0x3A, 0x42, 0xB3, 0x65, 0x10, 0x00, 0x32, 0x42, 0xB3, 0x3C, 0x78, 0x00, 0x00, 0x00, 0x20, 0x00, 0x40, 0x00, 0x50, 0x00, 0x40, 0x00, 0x30, 0x00, 0x40, 0x68, 0x10, 0x40, 0x93, 0xA9, 0x3A, 0x40, 0xB3, 0x3C, 0x78, 0x00, 0x00, 0x00, 0x74, 0x66, 0x10, 0x02, 0xB3, 0x3C, 0x78, 0x63, 0x10, 0x40, 0x93, 0x89, 0x3A, 0x40, 0xB3, 0x3C, 0x78, 0x00, 0x00, 0x00, 0x20, 0x00, 0x40, 0x00, 0x30, 0x00, 0x40, 0x80, 0x33, 0x77, 0x43, 0x00, 0x32, 0x40, 0xB3, 0x40, 0x93, 0xA6, 0x3A, 0x40, 0xB3, 0x40, 0x93, 0xA5, 0x3A, 0x40, 0xB3, 0x40, 0x93, 0x40, 0xB3, 0x40, 0x93, 0xA1, 0x3A, 0x40, 0xB3, 0xFA, 0x32, 0x42, 0x42, 0x41, 0xB3, 0x42, 0xB3, 0x3C, 0x78, 0x80, 0x33, 0x77, 0x43, 0x40, 0x93, 0xA9, 0x3A, 0x40, 0xB3, 0x3C, 0x78, 0x3C, 0x78, 0x00, 0x00, 0x3C, 0x78, 0x00, 0x00, 0xD0, 0x14, 0x21, 0x14, 0x00, 0xE0, 0x48, 0x01, 0x00, 0x74, 0x6E, 0xE4, 0x02, 0x00, 0x00, 0xA3, 0x8F, 0x6C, 0x60, 0x82, 0xCC, 0x74, 0x23, 0x5B, 0x44, 0x74, 0x20, 0xA2, 0x40, 0x3B, 0xFA, 0x0B, 0x01, 0x14, 0x90, 0x14, 0x21, 0x14, 0x00, 0x74, 0x44, 0x48, 0x6E, 0xE4, 0x02, 0x00, 0x40, 0xA3, 0x60, 0x83, 0xCC, 0x74, 0x09, 0x3B, 0x07, 0x08, 0x6E, 0xE4, 0x02, 0x00, 0x40, 0x83, 0x2F, 0x22, 0x88, 0x74, 0x06, 0x04, 0x6E, 0xE4, 0x02, 0x00, 0x40, 0x83, 0x36, 0x22, 0x88, 0x74, 0x6E, 0xE4, 0x02, 0x00, 0x40, 0xA3, 0x40, 0x83, 0x00, 0xE4, 0x0F, 0x20, 0x00, 0xA3, 0x60, 0x83, 0xCC, 0x74, 0x09, 0x3B, 0x07, 0x08, 0x6E, 0xE4, 0x02, 0x00, 0x40, 0x83, 0x2F, 0x22, 0x88, 0x74, 0x06, 0x04, 0x6E, 0xE4, 0x02, 0x00, 0x40, 0x83, 0x36, 0x22, 0x88, 0x74, 0x6E, 0xE4, 0x02, 0x00, 0x40, 0xA3, 0x60, 0x83, 0x01, 0x14, 0x3C, 0x78, 0x00, 0x00, 0xD1, 0x14, 0x01, 0x75, 0x08, 0x4C, 0xFF, 0xE3, 0xCB, 0xFF, 0x10, 0x74, 0xFF, 0xE3, 0xC8, 0xFF, 0x91, 0x14, 0x00, 0x00, 0xD1, 0x14, 0x03, 0x6D, 0x18, 0x48, 0xFF, 0xE3, 0xC1, 0xFF, 0x04, 0xC4, 0x40, 0x70, 0xFF, 0xE3, 0xBD, 0xFF, 0x04, 0xC4, 0x80, 0x70, 0xFF, 0xE3, 0xB9, 0xFF, 0x10, 0x74, 0xFF, 0xE3, 0xB6, 0xFF, 0x91, 0x14, 0x00, 0x00, 0x66, 0x10, 0x42, 0x93, 0xA1, 0x3A, 0x42, 0xB3, 0x42, 0x93, 0xA0, 0x3A, 0x42, 0xB3, 0xE0, 0xC6, 0x22, 0x50, 0x40, 0xB3, 0x3C, 0x78, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x64, 0x10, 0x45, 0x10, 0x40, 0xB3, 0x3C, 0x78, 0x62, 0x10, 0x00, 0x32, 0x42, 0xB3, 0x3C, 0x78, 0x00, 0x40, 0x00, 0x40, 0x40, 0x42, 0x0F, 0x00, 0xD0, 0x14, 0xFF, 0xE3, 0xC3, 0xF9, 0xFF, 0xE3, 0xF5, 0xFC, 0xFF, 0xE3, 0x71, 0xFE, 0xFF, 0xE3, 0xEB, 0xFA, 0xFF, 0xE3, 0x6B, 0xF9, 0xFF, 0xE3, 0x5B, 0xFA, 0xFF, 0xE3, 0xEB, 0xFC, 0xFF, 0xE3, 0x73, 0xFF, 0xFF, 0xE3, 0x57, 0xFF, 0xFF, 0xE3, 0x25, 0xFF, 0xFF, 0xE3, 0xD1, 0xFF, 0xFF, 0xE3, 0xB3, 0xF5, 0xFF, 0xE3, 0x1B, 0xF5, 0xFF, 0xE3, 0x1B, 0xF3, 0xFF, 0xE3, 0x33, 0xF0, 0xFF, 0xE3, 0x41, 0xF2, 0xFF, 0xE3, 0xCF, 0xEF, 0xFF, 0xE3, 0x59, 0xF4, 0x90, 0x14, 0xD0, 0x14, 0x00, 0x30, 0x01, 0x31, 0x02, 0x32, 0xFF, 0xE3, 0x14, 0xF5, 0x01, 0x30, 0x01, 0x31, 0x02, 0x32, 0xFF, 0xE3, 0x0F, 0xF5, 0xFF, 0xE3, 0x6B, 0xF4, 0xFF, 0xE3, 0x49, 0xFF, 0x90, 0x14, 0xD0, 0x14, 0xFF, 0xE3, 0x41, 0xF5, 0xFF, 0xE3, 0x6B, 0xF4, 0xFC, 0x07, 0xD0, 0x14, 0xFF, 0xE3, 0xC3, 0xFF, 0xFF, 0xE3, 0xE7, 0xFF, 0xFF, 0xE3, 0xF5, 0xFF, 0x00, 0x30, 0x90, 0x14, 0x00, 0x00, 0x40, 0x39, 0x03, 0x08, 0x00, 0xC0, 0x20, 0x2C, 0x40, 0x38, 0x02, 0x08, 0x3C, 0x78, 0xC2, 0x14, 0x43, 0x6D, 0x45, 0x6D, 0xE0, 0xC7, 0x80, 0x28, 0x03, 0x0C, 0x02, 0x6C, 0x00, 0x20, 0xE1, 0xC7, 0x80, 0x28, 0x03, 0x0C, 0x46, 0x6C, 0x00, 0x21, 0x01, 0x32, 0xC3, 0x6C, 0x03, 0xC4, 0x44, 0x7C, 0x90, 0x70, 0x10, 0x70, 0xC7, 0x6C, 0x03, 0xC4, 0x44, 0x7C, 0x00, 0x24, 0xC3, 0x6C, 0xD1, 0x70, 0x12, 0x6D, 0x20, 0x24, 0x90, 0x70, 0x10, 0x70, 0x40, 0x3A, 0x09, 0x0C, 0x00, 0xC4, 0x20, 0x4C, 0xCD, 0x60, 0x4C, 0x64, 0x02, 0x0C, 0xC6, 0x60, 0x89, 0x60, 0xF9, 0x0F, 0x0B, 0x6C, 0x4F, 0x6C, 0x05, 0xC4, 0x25, 0x4C, 0x05, 0x0C, 0x02, 0x6C, 0x00, 0x20, 0x46, 0x6C, 0x00, 0x21, 0x82, 0x14, 0x40, 0x3A, 0x25, 0x0C, 0x43, 0x6F, 0x80, 0xE5, 0x03, 0x20, 0x4C, 0xEB, 0x00, 0x00, 0x20, 0x0C, 0x2D, 0xDC, 0x00, 0x00, 0x00, 0x2A, 0x40, 0x3A, 0x1A, 0x0C, 0xAD, 0xE5, 0x00, 0x00, 0x8D, 0xE5, 0x03, 0x20, 0x4C, 0xEB, 0x00, 0x00, 0x14, 0x0C, 0x2D, 0xDC, 0x00, 0x00, 0x00, 0x2A, 0x40, 0x3A, 0x0E, 0x0C, 0xAD, 0xE5, 0x00, 0x00, 0x8D, 0xE5, 0x03, 0x20, 0x4C, 0xEB, 0x00, 0x00, 0x08, 0x0C, 0x2D, 0xDC, 0x00, 0x00, 0x00, 0x2A, 0xAD, 0xE5, 0x00, 0x00, 0x02, 0x04, 0x3C, 0x78, 0x68, 0x41, 0x4C, 0x6C, 0x70, 0x41, 0x4C, 0x6C, 0x2F, 0x3A, 0x0E, 0x08, 0x2D, 0xDC, 0x00, 0x20, 0x2D, 0xDC, 0x01, 0x20, 0x2D, 0xDC, 0x02, 0x20, 0x2D, 0xDC, 0x03, 0x20, 0x0F, 0x2A, 0xAD, 0xE5, 0x0F, 0x00, 0x2F, 0x3A, 0xF4, 0x0F, 0x23, 0x3A, 0x08, 0x08, 0x03, 0x2A, 0x2D, 0xDC, 0x00, 0x20, 0xAD, 0xE5, 0x03, 0x00, 0x23, 0x3A, 0xFA, 0x0F, 0x40, 0x3A, 0xE2, 0x0F, 0x00, 0x2A, 0x2D, 0xDC, 0x00, 0x00, 0x40, 0x3A, 0xDD, 0x0F, 0x00, 0x2A, 0x2D, 0xDC, 0x01, 0x00, 0x40, 0x3A, 0xD8, 0x0F, 0x2D, 0xDC, 0x02, 0x00, 0x3C, 0x78, 0x43, 0x6C, 0x00, 0xE4, 0x03, 0x20, 0x40, 0x38, 0x08, 0x0C, 0x00, 0x30, 0x40, 0x81, 0x40, 0x3A, 0x16, 0x0C, 0x00, 0x21, 0x00, 0x20, 0xFB, 0x07, 0x40, 0x91, 0x0B, 0x68, 0x04, 0x0C, 0x03, 0x21, 0x03, 0x20, 0xFB, 0x07, 0x02, 0xC4, 0x03, 0x71, 0x0A, 0x0C, 0x00, 0x20, 0x02, 0xC4, 0x83, 0x70, 0x06, 0x0C, 0x00, 0x20, 0x02, 0xC4, 0x43, 0x70, 0x02, 0x0C, 0x00, 0x20, 0x3C, 0x78, 0x00, 0x00, 0x5E, 0x04, 0x00, 0x00, 0x66, 0x04, 0x00, 0x00, 0xB6, 0x04, 0x00, 0x00, 0x6E, 0x04, 0x00, 0x00, 0x74, 0x04, 0x00, 0x00, 0x7A, 0x04, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, 0xA6, 0x04, 0x00, 0x00, 0x0E, 0x07, 0x00, 0x00, 0x0E, 0x07, 0x00, 0x00, 0x0E, 0x07, 0x00, 0x00, 0x38, 0x07, 0x00, 0x00, 0x44, 0x07, 0x00, 0x00, 0x52, 0x07, 0x00, 0x00, 0x62, 0x07, 0x00, 0x00, 0xD0, 0x08, 0x00, 0x00, 0xE2, 0x08, 0x00, 0x00, 0xF4, 0x08, 0x00, 0x00, 0x16, 0x09, 0x00, 0x00, 0x28, 0x09, 0x00, 0x00, 0x28, 0x09, 0x00, 0x00, 0x05, 0x06, 0x04, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x0F, 0x14, 0x14, 0x06, 0x28, 0x28, 0x00, 0xE4, 0x09, 0x00, 0x00, 0xCE, 0x09, 0x00, 0x00, 0xFA, 0x09, 0x00, 0x00, 0x10, 0x0A, 0x00, 0x00, 0x26, 0x0A, 0x00, 0x00, 0x66, 0x0A, 0x00, 0x00, 0xF8, 0x0B, 0x00, 0x00, 0x90, 0x0B, 0x00, 0x00, 0xC8, 0x19, 0x00, 0x00, 0x52, 0x1A, 0x00, 0x00, 0x52, 0x1A, 0x00, 0x00, 0x52, 0x1A, 0x00, 0x00, 0xF2, 0x19, 0x00, 0x00, 0xFA, 0x19, 0x00, 0x00, 0x0A, 0x1A, 0x00, 0x00, 0x12, 0x1A, 0x00, 0x00, 0x1A, 0x1A, 0x00, 0x00, 0x22, 0x1A, 0x00, 0x00, 0x2A, 0x1A, 0x00, 0x00, 0x84, 0x09, 0x00, 0x00, 0xF8, 0x04, 0x00, 0x00, 0xE4, 0x01, 0x00, 0x00, 0xBC, 0x02, 0x00, 0x00, 0xD4, 0x01, 0x00, 0x00, 0xBC, 0x0C, 0x00, 0x00, 0x90, 0x05, 0x00, 0x00, 0xE4, 0x1B, 0x00, 0x00, 0x60, 0x1D, 0x00, 0x00, 0x5C, 0x29, 0x00, 0x00, 0x94, 0x22, 0x00, 0x00, 0xD4, 0x10, 0x00, 0x00, 0xC8, 0x25, 0x00, 0x00, 0x48, 0x18, 0x00, 0x00, 0xA4, 0x10, 0x00, 0x00, 0x34, 0x11, 0x00, 0x00, 0xBC, 0x31, 0x00, 0x00, 0xAC, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x11, 0x00, 0x00, 0x68, 0x09, 0x00, 0x00, 0x01, 0x02, 0x04, 0x08, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0xF0, 0xE1, 0xD2, 0xC3 +}; + +#endif diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 891ef068d66f..f98aafd77dc9 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -231,6 +231,8 @@ enum power_supply_property { POWER_SUPPLY_PROP_CHARGE_ENABLED, POWER_SUPPLY_PROP_SET_SHIP_MODE, POWER_SUPPLY_PROP_REAL_TYPE, + POWER_SUPPLY_PROP_HVDCP3_TYPE, + POWER_SUPPLY_PROP_QUICK_CHARGE_TYPE, POWER_SUPPLY_PROP_CHARGE_NOW_RAW, POWER_SUPPLY_PROP_CHARGE_NOW_ERROR, POWER_SUPPLY_PROP_CAPACITY_RAW, @@ -285,6 +287,7 @@ enum power_supply_property { POWER_SUPPLY_PROP_TYPEC_SRC_RP, POWER_SUPPLY_PROP_PD_ALLOWED, POWER_SUPPLY_PROP_PD_ACTIVE, + POWER_SUPPLY_PROP_PD_AUTHENTICATION, POWER_SUPPLY_PROP_PD_IN_HARD_RESET, POWER_SUPPLY_PROP_PD_CURRENT_MAX, POWER_SUPPLY_PROP_PD_USB_SUSPEND_SUPPORTED, @@ -308,9 +311,16 @@ enum power_supply_property { POWER_SUPPLY_PROP_PD_VOLTAGE_MAX, POWER_SUPPLY_PROP_PD_VOLTAGE_MIN, POWER_SUPPLY_PROP_SDP_CURRENT_MAX, + POWER_SUPPLY_PROP_DC_THERMAL_LEVELS, POWER_SUPPLY_PROP_CONNECTOR_TYPE, POWER_SUPPLY_PROP_PARALLEL_BATFET_MODE, POWER_SUPPLY_PROP_PARALLEL_FCC_MAX, + POWER_SUPPLY_PROP_WIRELESS_VERSION, + POWER_SUPPLY_PROP_SIGNAL_STRENGTH, + POWER_SUPPLY_PROP_WIRELESS_CP_EN, + POWER_SUPPLY_PROP_WIRELESS_POWER_GOOD_EN, + POWER_SUPPLY_PROP_WIRELESS_WAKELOCK, + POWER_SUPPLY_PROP_TX_ADAPTER, POWER_SUPPLY_PROP_MIN_ICL, POWER_SUPPLY_PROP_MOISTURE_DETECTED, POWER_SUPPLY_PROP_BATT_PROFILE_VERSION, @@ -326,6 +336,9 @@ enum power_supply_property { POWER_SUPPLY_PROP_FORCE_RECHARGE, POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE, POWER_SUPPLY_PROP_TOGGLE_STAT, + POWER_SUPPLY_PROP_TYPE_RECHECK, + POWER_SUPPLY_PROP_LIQUID_DETECTION, + POWER_SUPPLY_PROP_DYNAMIC_FV_ENABLED, POWER_SUPPLY_PROP_MAIN_FCC_MAX, POWER_SUPPLY_PROP_FG_RESET, POWER_SUPPLY_PROP_QC_OPTI_DISABLE, @@ -394,6 +407,7 @@ enum power_supply_type { POWER_SUPPLY_TYPE_UFP, /* Type-C UFP */ POWER_SUPPLY_TYPE_DFP, /* Type-C DFP */ POWER_SUPPLY_TYPE_CHARGE_PUMP, /* Charge Pump */ + POWER_SUPPLY_TYPE_ZIMI_CAR_POWER, /* Zimi Car chargr */ }; /* Indicates USB Type-C CC connection status */