From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=DKIMWL_WL_HIGH,DKIM_SIGNED, DKIM_VALID,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,UNPARSEABLE_RELAY,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 117FEC33C99 for ; Tue, 7 Jan 2020 07:03:48 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id B53B7206DB for ; Tue, 7 Jan 2020 07:03:47 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="kJH/oOr5"; dkim=fail reason="signature verification failed" (1024-bit key) header.d=mediatek.com header.i=@mediatek.com header.b="Kni1eguz" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org B53B7206DB Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=mediatek.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-mediatek-bounces+linux-mediatek=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=uNvbJtINjkFhTMc68B1zEikWLm2W8rjwlJvUNTomHU8=; b=kJH/oOr5/5WEV4 f62OwQYrZzwZ7IiWDV8FgdAEl64kKBkfqNbInad+f7bcLkvlbG8g7tcxX13D7Kx5mx7XpThjkPmQJ b8j6DPGCz31wJrQcWP3EZU66uIV5gyndegz8Pux8PnJOycnKvSjXR0TSOd9qp6skXO5z/sopOsXGk ntrkTFNlUbjLPqOl+TLKOmgLBWQta4uzOiA0EWsr3V2bmvKsAgyn4f4r0IruyZA1JF50HLsAmgF5b GOlXcdOiu5KpLCfOT+TxSnm3bWyS4FdiSf7Q0UlikNL8QrwsozkfuRJU6oCTgcouWGrqvHBjOyLWQ dKKax0jpjP6mrTFRlLtA==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1ioiu3-0002zU-2m; Tue, 07 Jan 2020 07:03:47 +0000 Received: from mailgw01.mediatek.com ([216.200.240.184]) by bombadil.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1ioitx-0002yZ-8M; Tue, 07 Jan 2020 07:03:45 +0000 X-UUID: 50c0a69672d54a38acee72d7e71341c2-20200106 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mediatek.com; s=dk; h=Content-Transfer-Encoding:Content-Type:MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:CC:To:From; bh=VjjSHPgZ0wy2flMgNuu3m046BjiufOod+qrlXQt1vkw=; b=Kni1eguztIKmj7GAGBPZKWoE696/yHhyP3CAeUhW4kqvRtiDe68ANEJwnO2i/7u6zmDVTfTZiO7HNzHT8fBY5wGOENT+V/2s5ex2ti+2ilu/QOVkyXVausWhq8y7L2NID6SLX33Idll4oVUkbnQqtywnXrOUyLO2Y7Z/MxAG5fs=; X-UUID: 50c0a69672d54a38acee72d7e71341c2-20200106 Received: from mtkcas66.mediatek.inc [(172.29.193.44)] by mailgw01.mediatek.com (envelope-from ) (musrelay.mediatek.com ESMTP with TLS) with ESMTP id 1359242466; Mon, 06 Jan 2020 23:03:33 -0800 Received: from mtkmbs05n1.mediatek.inc (172.21.101.15) by MTKMBS62N1.mediatek.inc (172.29.193.41) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Mon, 6 Jan 2020 23:02:49 -0800 Received: from mtkcas07.mediatek.inc (172.21.101.84) by mtkmbs05n1.mediatek.inc (172.21.101.15) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Tue, 7 Jan 2020 15:01:45 +0800 Received: from mtksdaap41.mediatek.inc (172.21.77.4) by mtkcas07.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1395.4 via Frontend Transport; Tue, 7 Jan 2020 15:01:09 +0800 From: Roger Lu To: Kevin Hilman , Rob Herring , Nicolas Boichat , Stephen Boyd Subject: [PATCH v6 3/3] PM / AVS: SVS: Introduce SVS engine Date: Tue, 7 Jan 2020 15:01:54 +0800 Message-ID: <20200107070154.1574-4-roger.lu@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20200107070154.1574-1-roger.lu@mediatek.com> References: <20200107070154.1574-1-roger.lu@mediatek.com> MIME-Version: 1.0 X-MTK: N X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200106_230341_324911_0FA9C4AB X-CRM114-Status: GOOD ( 14.57 ) X-BeenThere: linux-mediatek@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Mark Rutland , Nishanth Menon , Angus Lin , devicetree@vger.kernel.org, linux-pm@vger.kernel.org, Roger Lu , linux-kernel@vger.kernel.org, Xiaoqing Liu , YT Lee , Fan Chen , linux-mediatek@lists.infradead.org, HenryC Chen , Charles Yang , Matthias Brugger , linux-arm-kernel@lists.infradead.org Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "Linux-mediatek" Errors-To: linux-mediatek-bounces+linux-mediatek=archiver.kernel.org@lists.infradead.org The SVS (Smart Voltage Scaling) engine is a piece of hardware which is used to calculate optimized voltage values of several power domains, e.g. CPU/GPU/CCI, according to chip process corner, temperatures, and other factors. Then DVFS driver could apply those optimized voltage values to reduce power consumption. Signed-off-by: Roger Lu --- drivers/power/avs/Kconfig | 10 + drivers/power/avs/Makefile | 1 + drivers/power/avs/mtk_svs.c | 2075 +++++++++++++++++++++++++++++++++ include/linux/power/mtk_svs.h | 23 + 4 files changed, 2109 insertions(+) create mode 100644 drivers/power/avs/mtk_svs.c create mode 100644 include/linux/power/mtk_svs.h diff --git a/drivers/power/avs/Kconfig b/drivers/power/avs/Kconfig index 089b6244b716..ba9d296bbdc1 100644 --- a/drivers/power/avs/Kconfig +++ b/drivers/power/avs/Kconfig @@ -19,3 +19,13 @@ config ROCKCHIP_IODOMAIN Say y here to enable support io domains on Rockchip SoCs. It is necessary for the io domain setting of the SoC to match the voltage supplied by the regulators. + +config MTK_SVS + bool "MediaTek Smart Voltage Scaling(SVS)" + depends on POWER_AVS && MTK_EFUSE && NVMEM + help + The SVS engine is a piece of hardware which is used to calculate + optimized voltage values of several power domains, e.g. + CPU clusters/GPU/CCI, according to chip process corner, temperatures, + and other factors. Then DVFS driver could apply those optimized voltage + values to reduce power consumption. diff --git a/drivers/power/avs/Makefile b/drivers/power/avs/Makefile index a1b8cd453f19..57246b977a93 100644 --- a/drivers/power/avs/Makefile +++ b/drivers/power/avs/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_POWER_AVS_OMAP) += smartreflex.o obj-$(CONFIG_ROCKCHIP_IODOMAIN) += rockchip-io-domain.o +obj-$(CONFIG_MTK_SVS) += mtk_svs.o diff --git a/drivers/power/avs/mtk_svs.c b/drivers/power/avs/mtk_svs.c new file mode 100644 index 000000000000..c46211a15fcd --- /dev/null +++ b/drivers/power/avs/mtk_svs.c @@ -0,0 +1,2075 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 MediaTek Inc. + */ + +#define pr_fmt(fmt) "[mtk_svs] " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* svs sw id */ +#define SVS_CPU_LITTLE BIT(0) +#define SVS_CPU_BIG BIT(1) +#define SVS_CCI BIT(2) +#define SVS_GPU BIT(3) + +/* svs bank mode support */ +#define SVSB_MODE_ALL_DISABLE (0) +#define SVSB_MODE_INIT01 BIT(1) +#define SVSB_MODE_INIT02 BIT(2) +#define SVSB_MODE_MON BIT(3) + +/* svs bank init01 condition */ +#define SVSB_INIT01_VOLT_IGNORE BIT(1) +#define SVSB_INIT01_VOLT_INC_ONLY BIT(2) + +/* svs bank common setting */ +#define RUNCONFIG_DEFAULT (0x80000000) +#define DC_SIGNED_BIT (0x8000) +#define INTEN_INIT0x (0x00005f01) +#define INTEN_MONVOPEN (0x00ff0000) +#define SVSEN_OFF (0x0) +#define SVSEN_MASK (0x7) +#define SVSEN_INIT01 (0x1) +#define SVSEN_INIT02 (0x5) +#define SVSEN_MON (0x2) +#define INTSTS_MONVOP (0x00ff0000) +#define INTSTS_COMPLETE (0x1) +#define INTSTS_CLEAN (0x00ffffff) + +#define proc_fops_rw(name) \ + static int name ## _proc_open(struct inode *inode, \ + struct file *file) \ + { \ + return single_open(file, name ## _proc_show, \ + PDE_DATA(inode)); \ + } \ + static const struct file_operations name ## _proc_fops = { \ + .owner = THIS_MODULE, \ + .open = name ## _proc_open, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ + .write = name ## _proc_write, \ + } + +#define proc_fops_ro(name) \ + static int name ## _proc_open(struct inode *inode, \ + struct file *file) \ + { \ + return single_open(file, name ## _proc_show, \ + PDE_DATA(inode)); \ + } \ + static const struct file_operations name ## _proc_fops = { \ + .owner = THIS_MODULE, \ + .open = name ## _proc_open, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ + } + +#define proc_entry(name) {__stringify(name), &name ## _proc_fops} + +static DEFINE_SPINLOCK(mtk_svs_lock); +struct mtk_svs; + +enum svsb_phase { + SVSB_PHASE_INIT01 = 0, + SVSB_PHASE_INIT02, + SVSB_PHASE_MON, + SVSB_PHASE_ERROR, +}; + +enum reg_index { + TEMPMONCTL0 = 0, + TEMPMONCTL1, + TEMPMONCTL2, + TEMPMONINT, + TEMPMONINTSTS, + TEMPMONIDET0, + TEMPMONIDET1, + TEMPMONIDET2, + TEMPH2NTHRE, + TEMPHTHRE, + TEMPCTHRE, + TEMPOFFSETH, + TEMPOFFSETL, + TEMPMSRCTL0, + TEMPMSRCTL1, + TEMPAHBPOLL, + TEMPAHBTO, + TEMPADCPNP0, + TEMPADCPNP1, + TEMPADCPNP2, + TEMPADCMUX, + TEMPADCEXT, + TEMPADCEXT1, + TEMPADCEN, + TEMPPNPMUXADDR, + TEMPADCMUXADDR, + TEMPADCEXTADDR, + TEMPADCEXT1ADDR, + TEMPADCENADDR, + TEMPADCVALIDADDR, + TEMPADCVOLTADDR, + TEMPRDCTRL, + TEMPADCVALIDMASK, + TEMPADCVOLTAGESHIFT, + TEMPADCWRITECTRL, + TEMPMSR0, + TEMPMSR1, + TEMPMSR2, + TEMPADCHADDR, + TEMPIMMD0, + TEMPIMMD1, + TEMPIMMD2, + TEMPMONIDET3, + TEMPADCPNP3, + TEMPMSR3, + TEMPIMMD3, + TEMPPROTCTL, + TEMPPROTTA, + TEMPPROTTB, + TEMPPROTTC, + TEMPSPARE0, + TEMPSPARE1, + TEMPSPARE2, + TEMPSPARE3, + TEMPMSR0_1, + TEMPMSR1_1, + TEMPMSR2_1, + TEMPMSR3_1, + DESCHAR, + TEMPCHAR, + DETCHAR, + AGECHAR, + DCCONFIG, + AGECONFIG, + FREQPCT30, + FREQPCT74, + LIMITVALS, + VBOOT, + DETWINDOW, + CONFIG, + TSCALCS, + RUNCONFIG, + SVSEN, + INIT2VALS, + DCVALUES, + AGEVALUES, + VOP30, + VOP74, + TEMP, + INTSTS, + INTSTSRAW, + INTEN, + CHKINT, + CHKSHIFT, + STATUS, + VDESIGN30, + VDESIGN74, + DVT30, + DVT74, + AGECOUNT, + SMSTATE0, + SMSTATE1, + CTL0, + DESDETSEC, + TEMPAGESEC, + CTRLSPARE0, + CTRLSPARE1, + CTRLSPARE2, + CTRLSPARE3, + CORESEL, + THERMINTST, + INTST, + THSTAGE0ST, + THSTAGE1ST, + THSTAGE2ST, + THAHBST0, + THAHBST1, + SPARE0, + SPARE1, + SPARE2, + SPARE3, + THSLPEVEB, + reg_num, +}; + +static const u32 svs_regs_v2[] = { + [TEMPMONCTL0] = 0x000, + [TEMPMONCTL1] = 0x004, + [TEMPMONCTL2] = 0x008, + [TEMPMONINT] = 0x00c, + [TEMPMONINTSTS] = 0x010, + [TEMPMONIDET0] = 0x014, + [TEMPMONIDET1] = 0x018, + [TEMPMONIDET2] = 0x01c, + [TEMPH2NTHRE] = 0x024, + [TEMPHTHRE] = 0x028, + [TEMPCTHRE] = 0x02c, + [TEMPOFFSETH] = 0x030, + [TEMPOFFSETL] = 0x034, + [TEMPMSRCTL0] = 0x038, + [TEMPMSRCTL1] = 0x03c, + [TEMPAHBPOLL] = 0x040, + [TEMPAHBTO] = 0x044, + [TEMPADCPNP0] = 0x048, + [TEMPADCPNP1] = 0x04c, + [TEMPADCPNP2] = 0x050, + [TEMPADCMUX] = 0x054, + [TEMPADCEXT] = 0x058, + [TEMPADCEXT1] = 0x05c, + [TEMPADCEN] = 0x060, + [TEMPPNPMUXADDR] = 0x064, + [TEMPADCMUXADDR] = 0x068, + [TEMPADCEXTADDR] = 0x06c, + [TEMPADCEXT1ADDR] = 0x070, + [TEMPADCENADDR] = 0x074, + [TEMPADCVALIDADDR] = 0x078, + [TEMPADCVOLTADDR] = 0x07c, + [TEMPRDCTRL] = 0x080, + [TEMPADCVALIDMASK] = 0x084, + [TEMPADCVOLTAGESHIFT] = 0x088, + [TEMPADCWRITECTRL] = 0x08c, + [TEMPMSR0] = 0x090, + [TEMPMSR1] = 0x094, + [TEMPMSR2] = 0x098, + [TEMPADCHADDR] = 0x09c, + [TEMPIMMD0] = 0x0a0, + [TEMPIMMD1] = 0x0a4, + [TEMPIMMD2] = 0x0a8, + [TEMPMONIDET3] = 0x0b0, + [TEMPADCPNP3] = 0x0b4, + [TEMPMSR3] = 0x0b8, + [TEMPIMMD3] = 0x0bc, + [TEMPPROTCTL] = 0x0c0, + [TEMPPROTTA] = 0x0c4, + [TEMPPROTTB] = 0x0c8, + [TEMPPROTTC] = 0x0cc, + [TEMPSPARE0] = 0x0f0, + [TEMPSPARE1] = 0x0f4, + [TEMPSPARE2] = 0x0f8, + [TEMPSPARE3] = 0x0fc, + [TEMPMSR0_1] = 0x190, + [TEMPMSR1_1] = 0x194, + [TEMPMSR2_1] = 0x198, + [TEMPMSR3_1] = 0x1b8, + [DESCHAR] = 0xc00, + [TEMPCHAR] = 0xc04, + [DETCHAR] = 0xc08, + [AGECHAR] = 0xc0c, + [DCCONFIG] = 0xc10, + [AGECONFIG] = 0xc14, + [FREQPCT30] = 0xc18, + [FREQPCT74] = 0xc1c, + [LIMITVALS] = 0xc20, + [VBOOT] = 0xc24, + [DETWINDOW] = 0xc28, + [CONFIG] = 0xc2c, + [TSCALCS] = 0xc30, + [RUNCONFIG] = 0xc34, + [SVSEN] = 0xc38, + [INIT2VALS] = 0xc3c, + [DCVALUES] = 0xc40, + [AGEVALUES] = 0xc44, + [VOP30] = 0xc48, + [VOP74] = 0xc4c, + [TEMP] = 0xc50, + [INTSTS] = 0xc54, + [INTSTSRAW] = 0xc58, + [INTEN] = 0xc5c, + [CHKINT] = 0xc60, + [CHKSHIFT] = 0xc64, + [STATUS] = 0xc68, + [VDESIGN30] = 0xc6c, + [VDESIGN74] = 0xc70, + [DVT30] = 0xc74, + [DVT74] = 0xc78, + [AGECOUNT] = 0xc7c, + [SMSTATE0] = 0xc80, + [SMSTATE1] = 0xc84, + [CTL0] = 0xc88, + [DESDETSEC] = 0xce0, + [TEMPAGESEC] = 0xce4, + [CTRLSPARE0] = 0xcf0, + [CTRLSPARE1] = 0xcf4, + [CTRLSPARE2] = 0xcf8, + [CTRLSPARE3] = 0xcfc, + [CORESEL] = 0xf00, + [THERMINTST] = 0xf04, + [INTST] = 0xf08, + [THSTAGE0ST] = 0xf0c, + [THSTAGE1ST] = 0xf10, + [THSTAGE2ST] = 0xf14, + [THAHBST0] = 0xf18, + [THAHBST1] = 0xf1c, + [SPARE0] = 0xf20, + [SPARE1] = 0xf24, + [SPARE2] = 0xf28, + [SPARE3] = 0xf2c, + [THSLPEVEB] = 0xf30, +}; + +struct thermal_parameter { + int adc_ge_t; + int adc_oe_t; + int ge; + int oe; + int gain; + int o_vtsabb; + int o_vtsmcu1; + int o_vtsmcu2; + int o_vtsmcu3; + int o_vtsmcu4; + int o_vtsmcu5; + int degc_cali; + int adc_cali_en_t; + int o_slope; + int o_slope_sign; + int ts_id; +}; + +struct svs_bank_ops { + void (*set_freqs_pct)(struct mtk_svs *svs); + void (*get_vops)(struct mtk_svs *svs); +}; + +struct svs_bank { + struct svs_bank_ops *ops; + struct completion init_completion; + struct device *dev; + struct regulator *buck; + struct mutex lock; /* Lock to protect update voltage process */ + bool suspended; + bool mtcmos_request; + s32 volt_offset; + u32 mode_support; + u32 opp_freqs[16]; + u32 freqs_pct[16]; + u32 opp_volts[16]; + u32 init02_volts[16]; + u32 volts[16]; + u32 reg_data[3][reg_num]; + u32 freq_base; + u32 vboot; + u32 volt_step; + u32 volt_base; + u32 init01_volt_flag; + u32 phase; + u32 vmax; + u32 vmin; + u32 bts; + u32 mts; + u32 bdes; + u32 mdes; + u32 mtdes; + u32 dcbdet; + u32 dcmdet; + u32 dthi; + u32 dtlo; + u32 det_window; + u32 det_max; + u32 age_config; + u32 age_voffset_in; + u32 agem; + u32 dc_config; + u32 dc_voffset_in; + u32 dvt_fixed; + u32 vco; + u32 chkshift; + u32 svs_temp; + u32 upper_temp_bound; + u32 lower_temp_bound; + u32 low_temp_threashold; + u32 low_temp_offset; + u32 coresel; + u32 opp_count; + u32 intst; + u32 systemclk_en; + u32 sw_id; + u32 bank_id; + u32 ctl0; + u8 *of_compatible; + u8 *name; + u8 *zone_name; + u8 *buck_name; +}; + +struct svs_platform { + struct svs_bank *banks; + bool (*efuse_parsing)(struct mtk_svs *svs); + bool fake_efuse; + const u32 *regs; + u32 bank_num; + u32 efuse_num; + u32 efuse_check; + u32 thermal_efuse_num; + u8 *name; +}; + +struct mtk_svs { + const struct svs_platform *platform; + struct svs_bank *bank; + struct device *dev; + void __iomem *base; + struct clk *main_clk; + u32 *efuse; + u32 *thermal_efuse; +}; + +unsigned long claim_mtk_svs_lock(void) + __acquires(&mtk_svs_lock) +{ + unsigned long flags; + + spin_lock_irqsave(&mtk_svs_lock, flags); + + return flags; +} +EXPORT_SYMBOL_GPL(claim_mtk_svs_lock); + +void release_mtk_svs_lock(unsigned long flags) + __releases(&mtk_svs_lock) +{ + spin_unlock_irqrestore(&mtk_svs_lock, flags); +} +EXPORT_SYMBOL_GPL(release_mtk_svs_lock); + +static u32 percent(u32 numerator, u32 denominator) +{ + u32 percent; + + /* If not divide 1000, "numerator * 100" would be data overflow. */ + numerator /= 1000; + denominator /= 1000; + percent = ((numerator * 100) + denominator - 1) / denominator; + percent &= GENMASK(7, 0); + + return percent; +} + +static u32 svs_readl(struct mtk_svs *svs, enum reg_index rg_i) +{ + return readl(svs->base + svs->platform->regs[rg_i]); +} + +static void svs_writel(struct mtk_svs *svs, u32 val, enum reg_index rg_i) +{ + writel(val, svs->base + svs->platform->regs[rg_i]); +} + +static void svs_switch_bank(struct mtk_svs *svs) +{ + struct svs_bank *svsb = svs->bank; + + svs_writel(svs, svsb->coresel, CORESEL); +} + +static u32 svsb_volt_to_opp_volt(u32 svsb_volt, u32 svsb_volt_step, + u32 svsb_volt_base) +{ + u32 u_volt; + + u_volt = (svsb_volt * svsb_volt_step) + svsb_volt_base; + + return u_volt; +} + +static int svsb_get_zone_temperature(struct svs_bank *svsb, int *zone_temp) +{ + struct thermal_zone_device *tzd; + + tzd = thermal_zone_get_zone_by_name(svsb->zone_name); + if (IS_ERR(tzd)) + return PTR_ERR(tzd); + + return thermal_zone_get_temp(tzd, zone_temp); +} + +static int svsb_set_volts(struct svs_bank *svsb, bool force_update) +{ + u32 i, svsb_volt, opp_volt, low_temp_offset = 0; + int zone_temp, ret; + + mutex_lock(&svsb->lock); + + /* + * If bank is suspended, it means init02 voltage is applied. + * Don't need to update opp voltage anymore. + */ + if (svsb->suspended && !force_update) { + pr_notice("%s: bank is suspended\n", svsb->name); + mutex_unlock(&svsb->lock); + return -EPERM; + } + + /* Get thermal effect */ + if (svsb->phase == SVSB_PHASE_MON) { + if (svsb->svs_temp > svsb->upper_temp_bound && + svsb->svs_temp < svsb->lower_temp_bound) { + pr_err("%s: svs_temp is abnormal (0x%x)?\n", + svsb->name, svsb->svs_temp); + mutex_unlock(&svsb->lock); + return -EINVAL; + } + + ret = svsb_get_zone_temperature(svsb, &zone_temp); + if (ret) { + pr_err("%s: cannot get zone \"%s\" temperature(%d)\n", + svsb->name, svsb->zone_name, ret); + pr_err("%s: add low_temp_offset = %u\n", + svsb->name, svsb->low_temp_offset); + zone_temp = svsb->low_temp_threashold; + } + + if (zone_temp <= svsb->low_temp_threashold) + low_temp_offset = svsb->low_temp_offset; + } + + /* vmin <= svsb_volt (opp_volt) <= signed-off voltage */ + for (i = 0; i < svsb->opp_count; i++) { + if (svsb->phase == SVSB_PHASE_MON) { + svsb_volt = max((svsb->volts[i] + svsb->volt_offset + + low_temp_offset), svsb->vmin); + opp_volt = svsb_volt_to_opp_volt(svsb_volt, + svsb->volt_step, + svsb->volt_base); + } else if (svsb->phase == SVSB_PHASE_INIT02) { + svsb_volt = max((svsb->init02_volts[i] + + svsb->volt_offset), svsb->vmin); + opp_volt = svsb_volt_to_opp_volt(svsb_volt, + svsb->volt_step, + svsb->volt_base); + } else if (svsb->phase == SVSB_PHASE_ERROR) { + opp_volt = svsb->opp_volts[i]; + } else { + pr_err("%s: unknown phase: %u?\n", + svsb->name, svsb->phase); + mutex_unlock(&svsb->lock); + return -EINVAL; + } + + opp_volt = min(opp_volt, svsb->opp_volts[i]); + ret = dev_pm_opp_adjust_voltage(svsb->dev, svsb->opp_freqs[i], + opp_volt, opp_volt, + svsb->opp_volts[i]); + if (ret) { + pr_err("%s: set voltage failed: %d\n", svsb->name, ret); + mutex_unlock(&svsb->lock); + return ret; + } + } + + mutex_unlock(&svsb->lock); + + return 0; +} + +static u32 interpolate(u32 f0, u32 f1, u32 v0, u32 v1, u32 fx) +{ + u32 vy; + + if (v0 == v1 || f0 == f1) + return v0; + + /* *100 to have decimal fraction factor, +99 for rounding up. */ + vy = (v0 * 100) - ((((v0 - v1) * 100) / (f0 - f1)) * (f0 - fx)); + vy = (vy + 99) / 100; + + return vy; +} + +static void svs_get_vops_v2(struct mtk_svs *svs) +{ + struct svs_bank *svsb = svs->bank; + u32 temp, i; + + temp = svs_readl(svs, VOP30); + svsb->volts[6] = (temp >> 24) & GENMASK(7, 0); + svsb->volts[4] = (temp >> 16) & GENMASK(7, 0); + svsb->volts[2] = (temp >> 8) & GENMASK(7, 0); + svsb->volts[0] = (temp & GENMASK(7, 0)); + + temp = svs_readl(svs, VOP74); + svsb->volts[14] = (temp >> 24) & GENMASK(7, 0); + svsb->volts[12] = (temp >> 16) & GENMASK(7, 0); + svsb->volts[10] = (temp >> 8) & GENMASK(7, 0); + svsb->volts[8] = (temp & GENMASK(7, 0)); + + for (i = 0; i <= 7; i++) { + if (i < 7) { + svsb->volts[(i * 2) + 1] = + interpolate(svsb->freqs_pct[i * 2], + svsb->freqs_pct[(i + 1) * 2], + svsb->volts[i * 2], + svsb->volts[(i + 1) * 2], + svsb->freqs_pct[(i * 2) + 1]); + } else if (i == 7) { + svsb->volts[(i * 2) + 1] = + interpolate(svsb->freqs_pct[(i - 1) * 2], + svsb->freqs_pct[i * 2], + svsb->volts[(i - 1) * 2], + svsb->volts[i * 2], + svsb->freqs_pct[(i * 2) + 1]); + } + } +} + +static void svs_set_freqs_pct_v2(struct mtk_svs *svs) +{ + struct svs_bank *svsb = svs->bank; + + svs_writel(svs, + ((svsb->freqs_pct[6] << 24) & GENMASK(31, 24)) | + ((svsb->freqs_pct[4] << 16) & GENMASK(23, 16)) | + ((svsb->freqs_pct[2] << 8) & GENMASK(15, 8)) | + (svsb->freqs_pct[0] & GENMASK(7, 0)), + FREQPCT30); + svs_writel(svs, + ((svsb->freqs_pct[14] << 24) & GENMASK(31, 24)) | + ((svsb->freqs_pct[12] << 16) & GENMASK(23, 16)) | + ((svsb->freqs_pct[10] << 8) & GENMASK(15, 8)) | + ((svsb->freqs_pct[8]) & GENMASK(7, 0)), + FREQPCT74); +} + +static void svs_set_phase(struct mtk_svs *svs, u32 target_phase) +{ + struct svs_bank *svsb = svs->bank; + u32 des_char, temp_char, det_char, limit_vals; + u32 init2vals, ts_calcs, val, filter, i; + + svs_switch_bank(svs); + + des_char = ((svsb->bdes << 8) & GENMASK(15, 8)) | + (svsb->mdes & GENMASK(7, 0)); + svs_writel(svs, des_char, DESCHAR); + + temp_char = ((svsb->vco << 16) & GENMASK(23, 16)) | + ((svsb->mtdes << 8) & GENMASK(15, 8)) | + (svsb->dvt_fixed & GENMASK(7, 0)); + svs_writel(svs, temp_char, TEMPCHAR); + + det_char = ((svsb->dcbdet << 8) & GENMASK(15, 8)) | + (svsb->dcmdet & GENMASK(7, 0)); + svs_writel(svs, det_char, DETCHAR); + + svs_writel(svs, svsb->dc_config, DCCONFIG); + svs_writel(svs, svsb->age_config, AGECONFIG); + + if (svsb->agem == 0x0) { + svs_writel(svs, RUNCONFIG_DEFAULT, RUNCONFIG); + } else { + val = 0x0; + + for (i = 0; i < 24; i += 2) { + filter = 0x3 << i; + + if ((svsb->age_config & filter) == 0x0) + val |= (0x1 << i); + else + val |= (svsb->age_config & filter); + } + svs_writel(svs, val, RUNCONFIG); + } + + svsb->ops->set_freqs_pct(svs); + + limit_vals = ((svsb->vmax << 24) & GENMASK(31, 24)) | + ((svsb->vmin << 16) & GENMASK(23, 16)) | + ((svsb->dthi << 8) & GENMASK(15, 8)) | + (svsb->dtlo & GENMASK(7, 0)); + svs_writel(svs, limit_vals, LIMITVALS); + svs_writel(svs, (svsb->vboot & GENMASK(7, 0)), VBOOT); + svs_writel(svs, (svsb->det_window & GENMASK(15, 0)), DETWINDOW); + svs_writel(svs, (svsb->det_max & GENMASK(15, 0)), CONFIG); + + if (svsb->chkshift != 0) + svs_writel(svs, (svsb->chkshift & GENMASK(7, 0)), CHKSHIFT); + + if (svsb->ctl0 != 0) + svs_writel(svs, svsb->ctl0, CTL0); + + svs_writel(svs, INTSTS_CLEAN, INTSTS); + + switch (target_phase) { + case SVSB_PHASE_INIT01: + svs_writel(svs, INTEN_INIT0x, INTEN); + svs_writel(svs, SVSEN_INIT01, SVSEN); + break; + case SVSB_PHASE_INIT02: + svs_writel(svs, INTEN_INIT0x, INTEN); + init2vals = ((svsb->age_voffset_in << 16) & GENMASK(31, 16)) | + (svsb->dc_voffset_in & GENMASK(15, 0)); + svs_writel(svs, init2vals, INIT2VALS); + svs_writel(svs, SVSEN_INIT02, SVSEN); + break; + case SVSB_PHASE_MON: + ts_calcs = ((svsb->bts << 12) & GENMASK(23, 12)) | + (svsb->mts & GENMASK(11, 0)); + svs_writel(svs, ts_calcs, TSCALCS); + svs_writel(svs, INTEN_MONVOPEN, INTEN); + svs_writel(svs, SVSEN_MON, SVSEN); + break; + default: + WARN_ON(1); + break; + } +} + +static inline void svs_init01_isr_handler(struct mtk_svs *svs) +{ + struct svs_bank *svsb = svs->bank; + enum reg_index rg_i; + + pr_notice("%s: %s: VDN74:0x%08x, VDN30:0x%08x, DCVALUES:0x%08x\n", + svsb->name, __func__, svs_readl(svs, VDESIGN74), + svs_readl(svs, VDESIGN30), svs_readl(svs, DCVALUES)); + + for (rg_i = TEMPMONCTL0; rg_i < reg_num; rg_i++) + svsb->reg_data[SVSB_PHASE_INIT01][rg_i] = svs_readl(svs, rg_i); + + svsb->dc_voffset_in = ~(svs_readl(svs, DCVALUES) & GENMASK(15, 0)) + 1; + if (svsb->init01_volt_flag == SVSB_INIT01_VOLT_IGNORE) + svsb->dc_voffset_in = 0; + else if ((svsb->dc_voffset_in & DC_SIGNED_BIT) && + (svsb->init01_volt_flag == SVSB_INIT01_VOLT_INC_ONLY)) + svsb->dc_voffset_in = 0; + + svsb->age_voffset_in = svs_readl(svs, AGEVALUES) & GENMASK(15, 0); + + svs_writel(svs, SVSEN_OFF, SVSEN); + svs_writel(svs, INTSTS_COMPLETE, INTSTS); + + /* svs init01 clock gating */ + svsb->coresel &= ~svsb->systemclk_en; + + svsb->phase = SVSB_PHASE_INIT01; + complete(&svsb->init_completion); +} + +static inline void svs_init02_isr_handler(struct mtk_svs *svs) +{ + struct svs_bank *svsb = svs->bank; + enum reg_index rg_i; + + pr_notice("%s: %s: VOP74:0x%08x, VOP30:0x%08x, DCVALUES:0x%08x\n", + svsb->name, __func__, svs_readl(svs, VOP74), + svs_readl(svs, VOP30), svs_readl(svs, DCVALUES)); + + for (rg_i = TEMPMONCTL0; rg_i < reg_num; rg_i++) + svsb->reg_data[SVSB_PHASE_INIT02][rg_i] = svs_readl(svs, rg_i); + + svsb->ops->get_vops(svs); + memcpy(svsb->init02_volts, svsb->volts, sizeof(u32) * svsb->opp_count); + svsb->phase = SVSB_PHASE_INIT02; + + svs_writel(svs, SVSEN_OFF, SVSEN); + svs_writel(svs, INTSTS_COMPLETE, INTSTS); + + complete(&svsb->init_completion); +} + +static inline void svs_mon_mode_isr_handler(struct mtk_svs *svs) +{ + struct svs_bank *svsb = svs->bank; + enum reg_index rg_i; + + for (rg_i = TEMPMONCTL0; rg_i < reg_num; rg_i++) + svsb->reg_data[SVSB_PHASE_MON][rg_i] = svs_readl(svs, rg_i); + + svsb->svs_temp = svs_readl(svs, TEMP) & GENMASK(7, 0); + + svsb->ops->get_vops(svs); + svsb->phase = SVSB_PHASE_MON; + + svs_writel(svs, INTSTS_MONVOP, INTSTS); +} + +static inline void svs_error_isr_handler(struct mtk_svs *svs) +{ + const struct svs_platform *svsp = svs->platform; + struct svs_bank *svsb = svs->bank; + enum reg_index rg_i; + + pr_err("%s(): %s(%s)", __func__, svsp->name, svsb->name); + pr_err("CORESEL(0x%x) = 0x%08x\n", + svsp->regs[CORESEL], svs_readl(svs, CORESEL)), + pr_err("SVSEN(0x%x) = 0x%08x, INTSTS(0x%x) = 0x%08x\n", + svsp->regs[SVSEN], svs_readl(svs, SVSEN), + svsp->regs[INTSTS], svs_readl(svs, INTSTS)); + pr_err("SMSTATE0(0x%x) = 0x%08x, SMSTATE1(0x%x) = 0x%08x\n", + svsp->regs[SMSTATE0], svs_readl(svs, SMSTATE0), + svsp->regs[SMSTATE1], svs_readl(svs, SMSTATE1)); + + for (rg_i = TEMPMONCTL0; rg_i < reg_num; rg_i++) + svsb->reg_data[SVSB_PHASE_MON][rg_i] = svs_readl(svs, rg_i); + + svsb->mode_support = SVSB_MODE_ALL_DISABLE; + + if (svsb->phase == SVSB_PHASE_MON) + svsb->phase = SVSB_PHASE_INIT02; + + svs_writel(svs, SVSEN_OFF, SVSEN); + svs_writel(svs, INTSTS_CLEAN, INTSTS); +} + +static inline void svs_isr_handler(struct mtk_svs *svs) +{ + u32 intsts, svsen; + + svs_switch_bank(svs); + + intsts = svs_readl(svs, INTSTS); + svsen = svs_readl(svs, SVSEN); + + if (intsts == INTSTS_COMPLETE && + ((svsen & SVSEN_MASK) == SVSEN_INIT01)) + svs_init01_isr_handler(svs); + else if ((intsts == INTSTS_COMPLETE) && + ((svsen & SVSEN_MASK) == SVSEN_INIT02)) + svs_init02_isr_handler(svs); + else if (!!(intsts & INTSTS_MONVOP)) + svs_mon_mode_isr_handler(svs); + else + svs_error_isr_handler(svs); +} + +static irqreturn_t svs_isr(int irq, void *data) +{ + struct mtk_svs *svs = (struct mtk_svs *)data; + const struct svs_platform *svsp = svs->platform; + struct svs_bank *svsb = NULL; + unsigned long flags; + u32 idx; + + flags = claim_mtk_svs_lock(); + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + svs->bank = svsb; + + if (svsb->suspended) + continue; + else if (svsb->intst & svs_readl(svs, INTST)) + continue; + + svs_isr_handler(svs); + break; + } + release_mtk_svs_lock(flags); + + if (svsb->phase != SVSB_PHASE_INIT01) + svsb_set_volts(svsb, false); + + return IRQ_HANDLED; +} + +static void svs_mon_mode(struct mtk_svs *svs) +{ + const struct svs_platform *svsp = svs->platform; + struct svs_bank *svsb; + unsigned long flags; + u32 idx; + + flags = claim_mtk_svs_lock(); + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + svs->bank = svsb; + + if (!(svsb->mode_support & SVSB_MODE_MON)) + continue; + + svs_set_phase(svs, SVSB_PHASE_MON); + } + release_mtk_svs_lock(flags); +} + +static int svs_init02(struct mtk_svs *svs) +{ + const struct svs_platform *svsp = svs->platform; + struct svs_bank *svsb; + unsigned long flags, time_left; + u32 idx; + + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + svs->bank = svsb; + + if (!(svsb->mode_support & SVSB_MODE_INIT02)) + continue; + + reinit_completion(&svsb->init_completion); + flags = claim_mtk_svs_lock(); + svs_set_phase(svs, SVSB_PHASE_INIT02); + release_mtk_svs_lock(flags); + time_left = + wait_for_completion_timeout(&svsb->init_completion, + msecs_to_jiffies(2000)); + if (time_left == 0) { + pr_err("%s: init02 completion timeout\n", svsb->name); + return -EBUSY; + } + } + + return 0; +} + +static int svs_init01(struct mtk_svs *svs) +{ + const struct svs_platform *svsp = svs->platform; + struct svs_bank *svsb; + struct pm_qos_request *qos_request; + unsigned long flags, time_left; + bool search_done; + int ret = -EINVAL; + u32 opp_freqs, opp_vboot, buck_volt, idx, i; + + qos_request = kzalloc(sizeof(*qos_request), GFP_KERNEL); + if (!qos_request) + return -ENOMEM; + + /* Let CPUs leave idle-off state for initializing svs_init01. */ + pm_qos_add_request(qos_request, PM_QOS_CPU_DMA_LATENCY, 0); + + /* + * Sometimes two svs banks use the same buck. + * Therefore, we set each svs bank to vboot voltage first. + */ + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + search_done = false; + + if (!(svsb->mode_support & SVSB_MODE_INIT01)) + continue; + + ret = regulator_set_mode(svsb->buck, REGULATOR_MODE_FAST); + if (ret) + pr_notice("%s: fail to set fast mode: %d\n", + svsb->name, ret); + + if (svsb->mtcmos_request) { + ret = regulator_enable(svsb->buck); + if (ret) { + pr_err("%s: fail to enable %s power: %d\n", + svsb->name, svsb->buck_name, ret); + goto init01_finish; + } + + ret = dev_pm_domain_attach(svsb->dev, false); + if (ret) { + pr_err("%s: attach pm domain fail: %d\n", + svsb->name, ret); + goto init01_finish; + } + + pm_runtime_enable(svsb->dev); + ret = pm_runtime_get_sync(svsb->dev); + if (ret < 0) { + pr_err("%s: turn mtcmos on fail: %d\n", + svsb->name, ret); + goto init01_finish; + } + } + + /* + * Find the fastest freq that can be run at vboot and + * fix to that freq until svs_init01 is done. + */ + opp_vboot = svsb_volt_to_opp_volt(svsb->vboot, + svsb->volt_step, + svsb->volt_base); + + for (i = 0; i < svsb->opp_count; i++) { + opp_freqs = svsb->opp_freqs[i]; + if (!search_done && svsb->opp_volts[i] <= opp_vboot) { + ret = dev_pm_opp_adjust_voltage(svsb->dev, + opp_freqs, + opp_vboot, + opp_vboot, + opp_vboot); + if (ret) { + pr_err("%s: set voltage failed: %d\n", + svsb->name, ret); + goto init01_finish; + } + + search_done = true; + } else { + dev_pm_opp_disable(svsb->dev, + svsb->opp_freqs[i]); + } + } + } + + /* svs bank init01 begins */ + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + svs->bank = svsb; + + if (!(svsb->mode_support & SVSB_MODE_INIT01)) + continue; + + opp_vboot = svsb_volt_to_opp_volt(svsb->vboot, + svsb->volt_step, + svsb->volt_base); + + buck_volt = regulator_get_voltage(svsb->buck); + if (buck_volt != opp_vboot) { + pr_err("%s: buck voltage: %u, expected vboot: %u\n", + svsb->name, buck_volt, opp_vboot); + ret = -EPERM; + goto init01_finish; + } + + init_completion(&svsb->init_completion); + flags = claim_mtk_svs_lock(); + svs_set_phase(svs, SVSB_PHASE_INIT01); + release_mtk_svs_lock(flags); + time_left = + wait_for_completion_timeout(&svsb->init_completion, + msecs_to_jiffies(2000)); + if (time_left == 0) { + pr_err("%s: init01 completion timeout\n", svsb->name); + ret = -EBUSY; + goto init01_finish; + } + } + +init01_finish: + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + + if (!(svsb->mode_support & SVSB_MODE_INIT01)) + continue; + + for (i = 0; i < svsb->opp_count; i++) + dev_pm_opp_enable(svsb->dev, svsb->opp_freqs[i]); + + if (regulator_set_mode(svsb->buck, REGULATOR_MODE_NORMAL)) + pr_notice("%s: fail to set normal mode: %d\n", + svsb->name, ret); + + if (svsb->mtcmos_request) { + if (pm_runtime_put_sync(svsb->dev)) + pr_err("%s: turn mtcmos off fail: %d\n", + svsb->name, ret); + pm_runtime_disable(svsb->dev); + dev_pm_domain_detach(svsb->dev, 0); + if (regulator_disable(svsb->buck)) + pr_err("%s: fail to disable %s power: %d\n", + svsb->name, svsb->buck_name, ret); + } + } + + pm_qos_remove_request(qos_request); + kfree(qos_request); + + return ret; +} + +static int svs_start(struct mtk_svs *svs) +{ + int ret; + + ret = svs_init01(svs); + if (ret) + return ret; + + ret = svs_init02(svs); + if (ret) + return ret; + + svs_mon_mode(svs); + + return ret; +} + +static bool svs_mt8183_efuse_parsing(struct mtk_svs *svs) +{ + const struct svs_platform *svsp = svs->platform; + struct thermal_parameter tp; + struct svs_bank *svsb; + bool mon_mode_support = true; + int format[6], x_roomt[6], tb_roomt = 0; + u32 idx, i, ft_pgm, mts, temp0, temp1, temp2; + + if (svsp->fake_efuse) { + pr_notice("fake efuse\n"); + svs->efuse[0] = 0x00310080; + svs->efuse[1] = 0xabfbf757; + svs->efuse[2] = 0x47c747c7; + svs->efuse[3] = 0xabfbf757; + svs->efuse[4] = 0xe7fca0ec; + svs->efuse[5] = 0x47bf4b88; + svs->efuse[6] = 0xabfb8fa5; + svs->efuse[7] = 0xabfb217b; + svs->efuse[8] = 0x4bf34be1; + svs->efuse[9] = 0xabfb670d; + svs->efuse[16] = 0xabfbc653; + svs->efuse[17] = 0x47f347e1; + svs->efuse[18] = 0xabfbd848; + + svs->thermal_efuse[0] = 0x02873f69; + svs->thermal_efuse[1] = 0xa11d9142; + svs->thermal_efuse[2] = 0xa2526900; + } + + for (i = 0; i < svsp->efuse_num; i++) { + if (svs->efuse[i]) + pr_notice("M_HW_RES%d: 0x%08x\n", i, svs->efuse[i]); + } + + /* svs efuse parsing */ + ft_pgm = (svs->efuse[0] >> 4) & 0xf; + + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + if (ft_pgm <= 1) + svsb->init01_volt_flag = SVSB_INIT01_VOLT_IGNORE; + + switch (svsb->sw_id) { + case SVS_CPU_LITTLE: + svsb->bdes = svs->efuse[16] & GENMASK(7, 0); + svsb->mdes = (svs->efuse[16] >> 8) & GENMASK(7, 0); + svsb->dcbdet = (svs->efuse[16] >> 16) & GENMASK(7, 0); + svsb->dcmdet = (svs->efuse[16] >> 24) & GENMASK(7, 0); + svsb->mtdes = (svs->efuse[17] >> 16) & GENMASK(7, 0); + + if (ft_pgm <= 3) + svsb->volt_offset += 10; + else + svsb->volt_offset += 2; + break; + case SVS_CPU_BIG: + svsb->bdes = svs->efuse[18] & GENMASK(7, 0); + svsb->mdes = (svs->efuse[18] >> 8) & GENMASK(7, 0); + svsb->dcbdet = (svs->efuse[18] >> 16) & GENMASK(7, 0); + svsb->dcmdet = (svs->efuse[18] >> 24) & GENMASK(7, 0); + svsb->mtdes = svs->efuse[17] & GENMASK(7, 0); + + if (ft_pgm <= 3) + svsb->volt_offset += 15; + else + svsb->volt_offset += 12; + break; + case SVS_CCI: + svsb->bdes = svs->efuse[4] & GENMASK(7, 0); + svsb->mdes = (svs->efuse[4] >> 8) & GENMASK(7, 0); + svsb->dcbdet = (svs->efuse[4] >> 16) & GENMASK(7, 0); + svsb->dcmdet = (svs->efuse[4] >> 24) & GENMASK(7, 0); + svsb->mtdes = (svs->efuse[5] >> 16) & GENMASK(7, 0); + + if (ft_pgm <= 3) + svsb->volt_offset += 10; + else + svsb->volt_offset += 2; + break; + case SVS_GPU: + svsb->bdes = svs->efuse[6] & GENMASK(7, 0); + svsb->mdes = (svs->efuse[6] >> 8) & GENMASK(7, 0); + svsb->dcbdet = (svs->efuse[6] >> 16) & GENMASK(7, 0); + svsb->dcmdet = (svs->efuse[6] >> 24) & GENMASK(7, 0); + svsb->mtdes = svs->efuse[5] & GENMASK(7, 0); + + if (ft_pgm >= 2) { + svsb->freq_base = 800000000; /* 800MHz */ + svsb->dvt_fixed = 2; + } + break; + default: + break; + } + } + + /* Thermal efuse parsing */ + if (!svs->thermal_efuse) + return true; + + tp.adc_ge_t = (svs->thermal_efuse[1] >> 22) & GENMASK(9, 0); + tp.adc_oe_t = (svs->thermal_efuse[1] >> 12) & GENMASK(9, 0); + + tp.o_vtsmcu1 = (svs->thermal_efuse[0] >> 17) & GENMASK(8, 0); + tp.o_vtsmcu2 = (svs->thermal_efuse[0] >> 8) & GENMASK(8, 0); + tp.o_vtsmcu3 = svs->thermal_efuse[1] & GENMASK(8, 0); + tp.o_vtsmcu4 = (svs->thermal_efuse[2] >> 23) & GENMASK(8, 0); + tp.o_vtsmcu5 = (svs->thermal_efuse[2] >> 5) & GENMASK(8, 0); + tp.o_vtsabb = (svs->thermal_efuse[2] >> 14) & GENMASK(8, 0); + + tp.degc_cali = (svs->thermal_efuse[0] >> 1) & GENMASK(5, 0); + tp.adc_cali_en_t = svs->thermal_efuse[0] & BIT(0); + tp.o_slope_sign = (svs->thermal_efuse[0] >> 7) & BIT(0); + + tp.ts_id = (svs->thermal_efuse[1] >> 9) & BIT(0); + tp.o_slope = (svs->thermal_efuse[0] >> 26) & GENMASK(5, 0); + + if (tp.adc_cali_en_t == 1) { + if (tp.ts_id == 0) + tp.o_slope = 0; + + if ((tp.adc_ge_t < 265 || tp.adc_ge_t > 758) || + (tp.adc_oe_t < 265 || tp.adc_oe_t > 758) || + (tp.o_vtsmcu1 < -8 || tp.o_vtsmcu1 > 484) || + (tp.o_vtsmcu2 < -8 || tp.o_vtsmcu2 > 484) || + (tp.o_vtsmcu3 < -8 || tp.o_vtsmcu3 > 484) || + (tp.o_vtsmcu4 < -8 || tp.o_vtsmcu4 > 484) || + (tp.o_vtsmcu5 < -8 || tp.o_vtsmcu5 > 484) || + (tp.o_vtsabb < -8 || tp.o_vtsabb > 484) || + (tp.degc_cali < 1 || tp.degc_cali > 63)) { + pr_err("bad thermal efuse data. disable mon mode\n"); + mon_mode_support = false; + } + } else { + pr_err("no thermal efuse data. disable mon mode\n"); + mon_mode_support = false; + } + + if (!mon_mode_support) { + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + svsb->mode_support &= ~SVSB_MODE_MON; + } + + return true; + } + + tp.ge = ((tp.adc_ge_t - 512) * 10000) / 4096; + tp.oe = (tp.adc_oe_t - 512); + tp.gain = (10000 + tp.ge); + + format[0] = (tp.o_vtsmcu1 + 3350 - tp.oe); + format[1] = (tp.o_vtsmcu2 + 3350 - tp.oe); + format[2] = (tp.o_vtsmcu3 + 3350 - tp.oe); + format[3] = (tp.o_vtsmcu4 + 3350 - tp.oe); + format[4] = (tp.o_vtsmcu5 + 3350 - tp.oe); + format[5] = (tp.o_vtsabb + 3350 - tp.oe); + + for (i = 0; i < 6; i++) + x_roomt[i] = (((format[i] * 10000) / 4096) * 10000) / tp.gain; + + temp0 = (10000 * 100000 / tp.gain) * 15 / 18; + + if (tp.o_slope_sign == 0) + mts = (temp0 * 10) / (1534 + tp.o_slope * 10); + else + mts = (temp0 * 10) / (1534 - tp.o_slope * 10); + + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + svsb->mts = mts; + + switch (svsb->sw_id) { + case SVS_CPU_LITTLE: + tb_roomt = x_roomt[3]; + break; + case SVS_CPU_BIG: + tb_roomt = x_roomt[4]; + break; + case SVS_CCI: + tb_roomt = x_roomt[3]; + break; + case SVS_GPU: + tb_roomt = x_roomt[1]; + break; + default: + break; + } + + temp0 = (tp.degc_cali * 10 / 2); + temp1 = ((10000 * 100000 / 4096 / tp.gain) * + tp.oe + tb_roomt * 10) * 15 / 18; + + if (tp.o_slope_sign == 0) + temp2 = temp1 * 100 / (1534 + tp.o_slope * 10); + else + temp2 = temp1 * 100 / (1534 - tp.o_slope * 10); + + svsb->bts = (temp0 + temp2 - 250) * 4 / 10; + } + + return true; +} + +static bool svs_is_supported(struct mtk_svs *svs) +{ + const struct svs_platform *svsp = svs->platform; + struct svs_bank *svsb; + struct nvmem_cell *cell; + size_t len; + bool ret; + u32 idx; + + if (svsp->fake_efuse) { + len = svsp->efuse_num * 4; + svs->efuse = devm_kzalloc(svs->dev, len, GFP_KERNEL); + if (!svs->efuse) { + pr_err("no memory for allocating svs_efuse\n"); + return false; + } + + len = svsp->thermal_efuse_num * 4; + svs->thermal_efuse = devm_kzalloc(svs->dev, len, GFP_KERNEL); + if (!svs->thermal_efuse) { + pr_err("no memory for allocating svs_thermal_efuse\n"); + return false; + } + + goto svsp_efuse_parsing; + } + + /* Get svs efuse by nvmem */ + cell = nvmem_cell_get(svs->dev, "svs-calibration-data"); + if (IS_ERR(cell)) { + pr_err("no \"svs-calibration-data\" from dts? disable svs\n"); + return false; + } + + svs->efuse = (u32 *)nvmem_cell_read(cell, &len); + nvmem_cell_put(cell); + + if (svs->efuse[svsp->efuse_check] == 0) { + pr_err("svs_efuse[%u] is empty\n", svsp->efuse_check); + return false; + } + + /* Get thermal efuse by nvmem */ + cell = nvmem_cell_get(svs->dev, "calibration-data"); + if (IS_ERR(cell)) { + pr_err("no \"calibration-data\" from dts? disable mon mode\n"); + svs->thermal_efuse = NULL; + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + svsb->mode_support &= ~SVSB_MODE_MON; + } + goto svsp_efuse_parsing; + } + + svs->thermal_efuse = (u32 *)nvmem_cell_read(cell, &len); + nvmem_cell_put(cell); + +svsp_efuse_parsing: + ret = svsp->efuse_parsing(svs); + + return ret; +} + +static int svs_resource_setup(struct mtk_svs *svs) +{ + const struct svs_platform *svsp = svs->platform; + struct svs_bank *svsb; + struct platform_device *pdev; + struct device_node *np = NULL; + struct dev_pm_opp *opp; + unsigned long freq; + int count, ret; + u32 idx, i; + + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + + switch (svsb->sw_id) { + case SVS_CPU_LITTLE: + svsb->name = "SVS_CPU_LITTLE"; + break; + case SVS_CPU_BIG: + svsb->name = "SVS_CPU_BIG"; + break; + case SVS_CCI: + svsb->name = "SVS_CCI"; + break; + case SVS_GPU: + svsb->name = "SVS_GPU"; + break; + default: + WARN_ON(1); + return -EINVAL; + } + + /* Add svs bank device for opp-table/mtcmos/buck control */ + pdev = platform_device_alloc(svsb->name, 0); + if (!pdev) { + pr_err("%s: fail to alloc pdev for svs_bank\n", + svsb->name); + return -ENOMEM; + } + + for_each_child_of_node(svs->dev->of_node, np) { + if (of_device_is_compatible(np, svsb->of_compatible)) { + pdev->dev.of_node = np; + break; + } + } + + ret = platform_device_add(pdev); + if (ret) { + pr_err("%s: fail to add svs_bank device: %d\n", + svsb->name, ret); + return ret; + } + + svsb->dev = &pdev->dev; + dev_set_drvdata(svsb->dev, svs); + ret = dev_pm_opp_of_add_table(svsb->dev); + if (ret) { + pr_err("%s: fail to add opp table: %d\n", + svsb->name, ret); + return ret; + } + + mutex_init(&svsb->lock); + + svsb->buck = devm_regulator_get_optional(svsb->dev, + svsb->buck_name); + if (IS_ERR(svsb->buck)) { + pr_err("%s: cannot get regulator \"%s-supply\"\n", + svsb->name, svsb->buck_name); + return PTR_ERR(svsb->buck); + } + + count = dev_pm_opp_get_opp_count(svsb->dev); + if (svsb->opp_count != count) { + pr_err("%s: opp_count not \"%u\" but get \"%d\"?\n", + svsb->name, svsb->opp_count, count); + return count; + } + + for (i = 0, freq = (u32)-1; i < svsb->opp_count; i++, freq--) { + opp = dev_pm_opp_find_freq_floor(svsb->dev, &freq); + if (IS_ERR(opp)) { + pr_err("%s: error opp entry!!, err = %ld\n", + svsb->name, PTR_ERR(opp)); + return PTR_ERR(opp); + } + + svsb->opp_freqs[i] = freq; + svsb->opp_volts[i] = dev_pm_opp_get_voltage(opp); + svsb->freqs_pct[i] = percent(svsb->opp_freqs[i], + svsb->freq_base); + dev_pm_opp_put(opp); + } + } + + return 0; +} + +static int svs_suspend(struct device *dev) +{ + struct mtk_svs *svs = dev_get_drvdata(dev); + const struct svs_platform *svsp = svs->platform; + struct svs_bank *svsb; + unsigned long flags; + u32 idx; + + /* Wait if there is processing svs_isr(). Suspend all banks. */ + flags = claim_mtk_svs_lock(); + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + svs->bank = svsb; + svs_switch_bank(svs); + svs_writel(svs, SVSEN_OFF, SVSEN); + svs_writel(svs, INTSTS_CLEAN, INTSTS); + svsb->suspended = true; + } + release_mtk_svs_lock(flags); + + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + if (svsb->phase == SVSB_PHASE_MON) { + svsb->phase = SVSB_PHASE_INIT02; + svsb_set_volts(svsb, true); + } + } + + clk_disable_unprepare(svs->main_clk); + + return 0; +} + +static int svs_resume(struct device *dev) +{ + struct mtk_svs *svs = dev_get_drvdata(dev); + const struct svs_platform *svsp = svs->platform; + struct svs_bank *svsb; + int ret; + u32 idx; + + ret = clk_prepare_enable(svs->main_clk); + if (ret) + pr_err("%s(): cannot enable main_clk\n", __func__); + + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + svsb->suspended = false; + } + + ret = svs_init02(svs); + if (ret) + return ret; + + svs_mon_mode(svs); + + return 0; +} + +static int svs_debug_proc_show(struct seq_file *m, void *v) +{ + struct svs_bank *svsb = (struct svs_bank *)m->private; + + if (svsb->phase == SVSB_PHASE_INIT01) + seq_puts(m, "init1\n"); + else if (svsb->phase == SVSB_PHASE_INIT02) + seq_puts(m, "init2\n"); + else if (svsb->phase == SVSB_PHASE_MON) + seq_puts(m, "mon mode\n"); + else if (svsb->phase == SVSB_PHASE_ERROR) + seq_puts(m, "disabled\n"); + else + seq_puts(m, "unknown\n"); + + return 0; +} + +static ssize_t svs_debug_proc_write(struct file *file, + const char __user *buffer, + size_t count, loff_t *pos) +{ + struct svs_bank *svsb = (struct svs_bank *)PDE_DATA(file_inode(file)); + struct mtk_svs *svs = dev_get_drvdata(svsb->dev); + char *buf = (char *)__get_free_page(GFP_USER); + unsigned long flags; + int enabled, ret; + + if (svsb->phase == SVSB_PHASE_ERROR) + return count; + + if (!buf) + return -ENOMEM; + + if (count >= PAGE_SIZE) { + free_page((unsigned long)buf); + return -EINVAL; + } + + if (copy_from_user(buf, buffer, count)) { + free_page((unsigned long)buf); + return -EFAULT; + } + + buf[count] = '\0'; + + ret = kstrtoint(buf, 10, &enabled); + if (ret) + return ret; + + if (!enabled) { + flags = claim_mtk_svs_lock(); + svs->bank = svsb; + svsb->mode_support = SVSB_MODE_ALL_DISABLE; + svs_switch_bank(svs); + svs_writel(svs, SVSEN_OFF, SVSEN); + svs_writel(svs, INTSTS_CLEAN, INTSTS); + release_mtk_svs_lock(flags); + } + + svsb->phase = SVSB_PHASE_ERROR; + svsb_set_volts(svsb, true); + + return count; +} + +proc_fops_rw(svs_debug); + +static int svs_dump_proc_show(struct seq_file *m, void *v) +{ + struct mtk_svs *svs = (struct mtk_svs *)m->private; + const struct svs_platform *svsp = svs->platform; + struct svs_bank *svsb; + unsigned long svs_reg_addr; + u32 idx, i, j; + + for (i = 0; i < svsp->efuse_num; i++) { + if (svs->efuse[i]) + seq_printf(m, "M_HW_RES%d = 0x%08x\n", + i, svs->efuse[i]); + } + + for (i = 0; i < svsp->thermal_efuse_num; i++) { + if (svs->thermal_efuse && svs->thermal_efuse[i]) + seq_printf(m, "THERMAL_EFUSE%d = 0x%08x\n", + i, svs->thermal_efuse[i]); + } + + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + + for (i = SVSB_PHASE_INIT01; i <= SVSB_PHASE_MON; i++) { + seq_printf(m, "Bank_number = %u\n", svsb->bank_id); + + if (i < SVSB_PHASE_MON) + seq_printf(m, "mode = init%d\n", i + 1); + else + seq_puts(m, "mode = mon\n"); + + for (j = TEMPMONCTL0; j < reg_num; j++) { + svs_reg_addr = (unsigned long)(svs->base + + svsp->regs[j]); + seq_printf(m, "0x%08lx = 0x%08x\n", + svs_reg_addr, svsb->reg_data[i][j]); + } + } + } + + return 0; +} + +proc_fops_ro(svs_dump); + +static int svs_status_proc_show(struct seq_file *m, void *v) +{ + struct svs_bank *svsb = (struct svs_bank *)m->private; + struct dev_pm_opp *opp; + unsigned long freq; + int zone_temp, ret; + u32 i; + + ret = svsb_get_zone_temperature(svsb, &zone_temp); + if (ret) + seq_printf(m, "%s: cannot get zone \"%s\" temperature\n", + svsb->name, svsb->zone_name); + else + seq_printf(m, "%s: temperature = %d\n", svsb->name, zone_temp); + + for (i = 0, freq = (u32)-1; i < svsb->opp_count; i++, freq--) { + opp = dev_pm_opp_find_freq_floor(svsb->dev, &freq); + if (IS_ERR(opp)) { + seq_printf(m, "%s: error opp entry!!, err = %ld\n", + svsb->name, PTR_ERR(opp)); + return PTR_ERR(opp); + } + + seq_printf(m, "opp_freqs[%02u]: %lu, volts[%02u]: %lu, ", + i, freq, i, dev_pm_opp_get_voltage(opp)); + seq_printf(m, "svsb_volts[%02u]: 0x%x, freqs_pct[%02u]: %u\n", + i, svsb->volts[i], i, svsb->freqs_pct[i]); + dev_pm_opp_put(opp); + } + + return 0; +} + +proc_fops_ro(svs_status); + +static int svs_volt_offset_proc_show(struct seq_file *m, void *v) +{ + struct svs_bank *svsb = (struct svs_bank *)m->private; + + seq_printf(m, "%d\n", svsb->volt_offset); + + return 0; +} + +static ssize_t svs_volt_offset_proc_write(struct file *file, + const char __user *buffer, + size_t count, loff_t *pos) +{ + struct svs_bank *svsb = (struct svs_bank *)PDE_DATA(file_inode(file)); + char *buf = (char *)__get_free_page(GFP_USER); + int ret, volt_offset; + + if (!buf) + return -ENOMEM; + + if (count >= PAGE_SIZE) { + free_page((unsigned long)buf); + return -EINVAL; + } + + if (copy_from_user(buf, buffer, count)) { + free_page((unsigned long)buf); + return -EFAULT; + } + + buf[count] = '\0'; + + if (!kstrtoint(buf, 10, &volt_offset)) { + svsb->volt_offset = volt_offset; + ret = svsb_set_volts(svsb, true); + if (ret) + return ret; + } + + return count; +} + +proc_fops_rw(svs_volt_offset); + +static int svs_create_svs_procfs(struct mtk_svs *svs) +{ + const struct svs_platform *svsp = svs->platform; + struct svs_bank *svsb; + struct proc_dir_entry *svs_dir, *bank_dir; + u32 idx, i; + + struct pentry { + const char *name; + const struct file_operations *fops; + }; + + struct pentry svs_entries[] = { + proc_entry(svs_dump), + }; + + struct pentry bank_entries[] = { + proc_entry(svs_debug), + proc_entry(svs_status), + proc_entry(svs_volt_offset), + }; + + svs_dir = proc_mkdir("svs", NULL); + if (!svs_dir) { + pr_err("mkdir /proc/svs failed\n"); + return -EPERM; + } + + for (i = 0; i < ARRAY_SIZE(svs_entries); i++) { + if (!proc_create_data(svs_entries[i].name, 0664, + svs_dir, svs_entries[i].fops, svs)) { + pr_err("create /proc/svs/%s failed\n", + svs_entries[i].name); + return -EPERM; + } + } + + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + + if (svsb->mode_support == SVSB_MODE_ALL_DISABLE) + continue; + + bank_dir = proc_mkdir(svsb->name, svs_dir); + if (!bank_dir) { + pr_err("mkdir /proc/svs/%s failed\n", svsb->name); + return -EPERM; + } + + for (i = 0; i < ARRAY_SIZE(bank_entries); i++) { + if (!proc_create_data(bank_entries[i].name, 0664, + bank_dir, bank_entries[i].fops, + svsb)) { + pr_err("create /proc/svs/%s/%s failed\n", + svsb->name, bank_entries[i].name); + return -EPERM; + } + } + } + + return 0; +} + +static struct svs_bank_ops svs_mt8183_banks_ops = { + .set_freqs_pct = svs_set_freqs_pct_v2, + .get_vops = svs_get_vops_v2, +}; + +static struct svs_bank svs_mt8183_banks[4] = { + { + .of_compatible = "mediatek,mt8183-svs-cpu-little", + .sw_id = SVS_CPU_LITTLE, + .bank_id = 0, + .ops = &svs_mt8183_banks_ops, + .zone_name = "tzts4", + .buck_name = "vcpu-little", + .mtcmos_request = false, + .init01_volt_flag = SVSB_INIT01_VOLT_INC_ONLY, + .mode_support = SVSB_MODE_INIT01 | SVSB_MODE_INIT02, + .opp_count = 16, + .freq_base = 1989000000, + .vboot = 0x30, + .volt_step = 6250, + .volt_base = 500000, + .volt_offset = 0, + .vmax = 0x64, + .vmin = 0x18, + .dthi = 0x1, + .dtlo = 0xfe, + .det_window = 0xa28, + .det_max = 0xffff, + .age_config = 0x555555, + .agem = 0x0, + .dc_config = 0x555555, + .dvt_fixed = 0x7, + .vco = 0x10, + .chkshift = 0x77, + .upper_temp_bound = 0x64, + .lower_temp_bound = 0xb2, + .low_temp_threashold = 25000, + .low_temp_offset = 0, + .coresel = 0x8fff0000, + .systemclk_en = BIT(31), + .intst = BIT(0), + .ctl0 = 0x00010001, + }, + { + .of_compatible = "mediatek,mt8183-svs-cpu-big", + .sw_id = SVS_CPU_BIG, + .bank_id = 1, + .ops = &svs_mt8183_banks_ops, + .zone_name = "tzts5", + .buck_name = "vcpu-big", + .mtcmos_request = false, + .init01_volt_flag = SVSB_INIT01_VOLT_INC_ONLY, + .mode_support = SVSB_MODE_INIT01 | SVSB_MODE_INIT02, + .opp_count = 16, + .freq_base = 1989000000, + .vboot = 0x30, + .volt_step = 6250, + .volt_base = 500000, + .volt_offset = 0, + .vmax = 0x58, + .vmin = 0x10, + .dthi = 0x1, + .dtlo = 0xfe, + .det_window = 0xa28, + .det_max = 0xffff, + .age_config = 0x555555, + .agem = 0x0, + .dc_config = 0x555555, + .dvt_fixed = 0x7, + .vco = 0x10, + .chkshift = 0x77, + .upper_temp_bound = 0x64, + .lower_temp_bound = 0xb2, + .low_temp_threashold = 25000, + .low_temp_offset = 0, + .coresel = 0x8fff0001, + .systemclk_en = BIT(31), + .intst = BIT(1), + .ctl0 = 0x00000001, + }, + { + .of_compatible = "mediatek,mt8183-svs-cci", + .sw_id = SVS_CCI, + .bank_id = 2, + .ops = &svs_mt8183_banks_ops, + .zone_name = "tzts4", + .buck_name = "vcci", + .mtcmos_request = false, + .init01_volt_flag = SVSB_INIT01_VOLT_INC_ONLY, + .mode_support = SVSB_MODE_INIT01 | SVSB_MODE_INIT02, + .opp_count = 16, + .freq_base = 1196000000, + .vboot = 0x30, + .volt_step = 6250, + .volt_base = 500000, + .volt_offset = 0, + .vmax = 0x64, + .vmin = 0x18, + .dthi = 0x1, + .dtlo = 0xfe, + .det_window = 0xa28, + .det_max = 0xffff, + .age_config = 0x555555, + .agem = 0x0, + .dc_config = 0x555555, + .dvt_fixed = 0x7, + .vco = 0x10, + .chkshift = 0x77, + .upper_temp_bound = 0x64, + .lower_temp_bound = 0xb2, + .low_temp_threashold = 25000, + .low_temp_offset = 0, + .coresel = 0x8fff0002, + .systemclk_en = BIT(31), + .intst = BIT(2), + .ctl0 = 0x00100003, + }, + { + .of_compatible = "mediatek,mt8183-svs-gpu", + .sw_id = SVS_GPU, + .bank_id = 3, + .ops = &svs_mt8183_banks_ops, + .zone_name = "tzts2", + .buck_name = "vgpu", + .mtcmos_request = true, + .init01_volt_flag = SVSB_INIT01_VOLT_INC_ONLY, + .mode_support = SVSB_MODE_INIT01 | SVSB_MODE_INIT02 | + SVSB_MODE_MON, + .opp_count = 16, + .freq_base = 900000000, + .vboot = 0x30, + .volt_step = 6250, + .volt_base = 500000, + .volt_offset = 0, + .vmax = 0x40, + .vmin = 0x14, + .dthi = 0x1, + .dtlo = 0xfe, + .det_window = 0xa28, + .det_max = 0xffff, + .age_config = 0x555555, + .agem = 0x0, + .dc_config = 0x555555, + .dvt_fixed = 0x3, + .vco = 0x10, + .chkshift = 0x77, + .upper_temp_bound = 0x64, + .lower_temp_bound = 0xb2, + .low_temp_threashold = 25000, + .low_temp_offset = 3, + .coresel = 0x8fff0003, + .systemclk_en = BIT(31), + .intst = BIT(3), + .ctl0 = 0x00050001, + }, +}; + +static const struct svs_platform svs_mt8183_platform = { + .name = "mt8183-svs", + .banks = svs_mt8183_banks, + .efuse_parsing = svs_mt8183_efuse_parsing, + .regs = svs_regs_v2, + .fake_efuse = false, + .bank_num = 4, + .efuse_num = 25, + .efuse_check = 2, + .thermal_efuse_num = 3, +}; + +static const struct of_device_id mtk_svs_of_match[] = { + { + .compatible = "mediatek,mt8183-svs", + .data = &svs_mt8183_platform, + }, { + /* Sentinel */ + }, +}; + +static int svs_probe(struct platform_device *pdev) +{ + const struct of_device_id *of_dev_id; + struct mtk_svs *svs; + int ret; + u32 svs_irq; + + svs = devm_kzalloc(&pdev->dev, sizeof(*svs), GFP_KERNEL); + if (!svs) + return -ENOMEM; + + svs->dev = &pdev->dev; + if (!svs->dev->of_node) { + pr_err("cannot find device node\n"); + return -ENODEV; + } + + svs->base = of_iomap(svs->dev->of_node, 0); + if (IS_ERR(svs->base)) { + pr_err("cannot find svs register base\n"); + return PTR_ERR(svs->base); + } + + svs_irq = irq_of_parse_and_map(svs->dev->of_node, 0); + ret = devm_request_threaded_irq(svs->dev, svs_irq, NULL, svs_isr, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "mtk-svs", svs); + if (ret) { + pr_err("register irq(%d) failed: %d\n", svs_irq, ret); + return ret; + } + + of_dev_id = of_match_node(mtk_svs_of_match, svs->dev->of_node); + if (!of_dev_id || !of_dev_id->data) + return -EINVAL; + + svs->platform = of_dev_id->data; + dev_set_drvdata(svs->dev, svs); + + svs->main_clk = devm_clk_get(svs->dev, "main"); + if (IS_ERR(svs->main_clk)) { + pr_err("failed to get clock: %ld\n", PTR_ERR(svs->main_clk)); + return PTR_ERR(svs->main_clk); + } + + ret = clk_prepare_enable(svs->main_clk); + if (ret) { + pr_err("cannot enable main clk: %d\n", ret); + return ret; + } + + if (!svs_is_supported(svs)) { + pr_notice("svs is not supported\n"); + goto svs_probe_fail; + } + + ret = svs_resource_setup(svs); + if (ret) + goto svs_probe_fail; + + ret = svs_start(svs); + if (ret) + goto svs_probe_fail; + + ret = svs_create_svs_procfs(svs); + if (ret) + goto svs_probe_fail; + + return 0; + +svs_probe_fail: + clk_disable_unprepare(svs->main_clk); + + return ret; +} + +static const struct dev_pm_ops svs_pm_ops = { + .suspend = svs_suspend, + .resume = svs_resume, +}; + +static struct platform_driver svs_driver = { + .probe = svs_probe, + .driver = { + .name = "mtk-svs", + .pm = &svs_pm_ops, + .of_match_table = of_match_ptr(mtk_svs_of_match), + }, +}; + +static int __init svs_init(void) +{ + int ret; + + ret = platform_driver_register(&svs_driver); + if (ret) { + pr_err("svs platform driver register failed: %d\n", ret); + return ret; + } + + return 0; +} + +late_initcall_sync(svs_init); + +MODULE_DESCRIPTION("MediaTek SVS Driver v1.0"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/power/mtk_svs.h b/include/linux/power/mtk_svs.h new file mode 100644 index 000000000000..5c03982e3576 --- /dev/null +++ b/include/linux/power/mtk_svs.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2020 MediaTek Inc. + */ + +#ifndef __MTK_SVS_H__ +#define __MTK_SVS_H__ + +#if IS_ENABLED(CONFIG_MTK_SVS) +unsigned long claim_mtk_svs_lock(void); +void release_mtk_svs_lock(unsigned long flags); +#else +static inline unsigned long claim_mtk_svs_lock(void) +{ + return 0; +} + +static inline void release_mtk_svs_lock(unsigned long flags) +{ +} +#endif /* CONFIG_MTK_SVS */ + +#endif /* __MTK_SVS_H__ */ -- 2.18.0 _______________________________________________ Linux-mediatek mailing list Linux-mediatek@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-mediatek From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI, MIME_BASE64_TEXT,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,UNPARSEABLE_RELAY, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4137BC33C9B for ; Tue, 7 Jan 2020 07:02:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id D9E212087F for ; Tue, 7 Jan 2020 07:02:40 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=mediatek.com header.i=@mediatek.com header.b="Kni1eguz" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727584AbgAGHCg (ORCPT ); Tue, 7 Jan 2020 02:02:36 -0500 Received: from mailgw02.mediatek.com ([210.61.82.184]:50048 "EHLO mailgw02.mediatek.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S1725920AbgAGHCf (ORCPT ); Tue, 7 Jan 2020 02:02:35 -0500 X-UUID: d6dbfff413af4dccb61f88a8ad2c6fdc-20200107 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mediatek.com; s=dk; h=Content-Transfer-Encoding:Content-Type:MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:CC:To:From; bh=VjjSHPgZ0wy2flMgNuu3m046BjiufOod+qrlXQt1vkw=; b=Kni1eguztIKmj7GAGBPZKWoE696/yHhyP3CAeUhW4kqvRtiDe68ANEJwnO2i/7u6zmDVTfTZiO7HNzHT8fBY5wGOENT+V/2s5ex2ti+2ilu/QOVkyXVausWhq8y7L2NID6SLX33Idll4oVUkbnQqtywnXrOUyLO2Y7Z/MxAG5fs=; X-UUID: d6dbfff413af4dccb61f88a8ad2c6fdc-20200107 Received: from mtkcas08.mediatek.inc [(172.21.101.126)] by mailgw02.mediatek.com (envelope-from ) (Cellopoint E-mail Firewall v4.1.10 Build 0809 with TLS) with ESMTP id 500436095; Tue, 07 Jan 2020 15:02:16 +0800 Received: from mtkcas07.mediatek.inc (172.21.101.84) by mtkmbs05n1.mediatek.inc (172.21.101.15) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Tue, 7 Jan 2020 15:01:45 +0800 Received: from mtksdaap41.mediatek.inc (172.21.77.4) by mtkcas07.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1395.4 via Frontend Transport; Tue, 7 Jan 2020 15:01:09 +0800 From: Roger Lu To: Kevin Hilman , Rob Herring , Nicolas Boichat , Stephen Boyd CC: Fan Chen , HenryC Chen , YT Lee , Xiaoqing Liu , Charles Yang , Angus Lin , Mark Rutland , Matthias Brugger , Nishanth Menon , Roger Lu , , , , , Subject: [PATCH v6 3/3] PM / AVS: SVS: Introduce SVS engine Date: Tue, 7 Jan 2020 15:01:54 +0800 Message-ID: <20200107070154.1574-4-roger.lu@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20200107070154.1574-1-roger.lu@mediatek.com> References: <20200107070154.1574-1-roger.lu@mediatek.com> MIME-Version: 1.0 Content-Type: text/plain X-MTK: N Content-Transfer-Encoding: base64 Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org VGhlIFNWUyAoU21hcnQgVm9sdGFnZSBTY2FsaW5nKSBlbmdpbmUgaXMgYSBwaWVjZQ0Kb2YgaGFy ZHdhcmUgd2hpY2ggaXMgdXNlZCB0byBjYWxjdWxhdGUgb3B0aW1pemVkDQp2b2x0YWdlIHZhbHVl cyBvZiBzZXZlcmFsIHBvd2VyIGRvbWFpbnMsDQplLmcuIENQVS9HUFUvQ0NJLCBhY2NvcmRpbmcg dG8gY2hpcCBwcm9jZXNzIGNvcm5lciwNCnRlbXBlcmF0dXJlcywgYW5kIG90aGVyIGZhY3RvcnMu IFRoZW4gRFZGUyBkcml2ZXINCmNvdWxkIGFwcGx5IHRob3NlIG9wdGltaXplZCB2b2x0YWdlIHZh bHVlcyB0byByZWR1Y2UNCnBvd2VyIGNvbnN1bXB0aW9uLg0KDQpTaWduZWQtb2ZmLWJ5OiBSb2dl ciBMdSA8cm9nZXIubHVAbWVkaWF0ZWsuY29tPg0KLS0tDQogZHJpdmVycy9wb3dlci9hdnMvS2Nv bmZpZyAgICAgfCAgIDEwICsNCiBkcml2ZXJzL3Bvd2VyL2F2cy9NYWtlZmlsZSAgICB8ICAgIDEg Kw0KIGRyaXZlcnMvcG93ZXIvYXZzL210a19zdnMuYyAgIHwgMjA3NSArKysrKysrKysrKysrKysr KysrKysrKysrKysrKysrKysNCiBpbmNsdWRlL2xpbnV4L3Bvd2VyL210a19zdnMuaCB8ICAgMjMg Kw0KIDQgZmlsZXMgY2hhbmdlZCwgMjEwOSBpbnNlcnRpb25zKCspDQogY3JlYXRlIG1vZGUgMTAw NjQ0IGRyaXZlcnMvcG93ZXIvYXZzL210a19zdnMuYw0KIGNyZWF0ZSBtb2RlIDEwMDY0NCBpbmNs dWRlL2xpbnV4L3Bvd2VyL210a19zdnMuaA0KDQpkaWZmIC0tZ2l0IGEvZHJpdmVycy9wb3dlci9h dnMvS2NvbmZpZyBiL2RyaXZlcnMvcG93ZXIvYXZzL0tjb25maWcNCmluZGV4IDA4OWI2MjQ0Yjcx Ni4uYmE5ZDI5NmJiZGMxIDEwMDY0NA0KLS0tIGEvZHJpdmVycy9wb3dlci9hdnMvS2NvbmZpZw0K KysrIGIvZHJpdmVycy9wb3dlci9hdnMvS2NvbmZpZw0KQEAgLTE5LDMgKzE5LDEzIEBAIGNvbmZp ZyBST0NLQ0hJUF9JT0RPTUFJTg0KIAkgIFNheSB5IGhlcmUgdG8gZW5hYmxlIHN1cHBvcnQgaW8g ZG9tYWlucyBvbiBSb2NrY2hpcCBTb0NzLiBJdCBpcw0KIAkgIG5lY2Vzc2FyeSBmb3IgdGhlIGlv IGRvbWFpbiBzZXR0aW5nIG9mIHRoZSBTb0MgdG8gbWF0Y2ggdGhlDQogCSAgdm9sdGFnZSBzdXBw bGllZCBieSB0aGUgcmVndWxhdG9ycy4NCisNCitjb25maWcgTVRLX1NWUw0KKwlib29sICJNZWRp YVRlayBTbWFydCBWb2x0YWdlIFNjYWxpbmcoU1ZTKSINCisJZGVwZW5kcyBvbiBQT1dFUl9BVlMg JiYgTVRLX0VGVVNFICYmIE5WTUVNDQorCWhlbHANCisJICBUaGUgU1ZTIGVuZ2luZSBpcyBhIHBp ZWNlIG9mIGhhcmR3YXJlIHdoaWNoIGlzIHVzZWQgdG8gY2FsY3VsYXRlDQorCSAgb3B0aW1pemVk IHZvbHRhZ2UgdmFsdWVzIG9mIHNldmVyYWwgcG93ZXIgZG9tYWlucywgZS5nLg0KKwkgIENQVSBj bHVzdGVycy9HUFUvQ0NJLCBhY2NvcmRpbmcgdG8gY2hpcCBwcm9jZXNzIGNvcm5lciwgdGVtcGVy YXR1cmVzLA0KKwkgIGFuZCBvdGhlciBmYWN0b3JzLiBUaGVuIERWRlMgZHJpdmVyIGNvdWxkIGFw cGx5IHRob3NlIG9wdGltaXplZCB2b2x0YWdlDQorCSAgdmFsdWVzIHRvIHJlZHVjZSBwb3dlciBj b25zdW1wdGlvbi4NCmRpZmYgLS1naXQgYS9kcml2ZXJzL3Bvd2VyL2F2cy9NYWtlZmlsZSBiL2Ry aXZlcnMvcG93ZXIvYXZzL01ha2VmaWxlDQppbmRleCBhMWI4Y2Q0NTNmMTkuLjU3MjQ2Yjk3N2E5 MyAxMDA2NDQNCi0tLSBhL2RyaXZlcnMvcG93ZXIvYXZzL01ha2VmaWxlDQorKysgYi9kcml2ZXJz L3Bvd2VyL2F2cy9NYWtlZmlsZQ0KQEAgLTEsMyArMSw0IEBADQogIyBTUERYLUxpY2Vuc2UtSWRl bnRpZmllcjogR1BMLTIuMC1vbmx5DQogb2JqLSQoQ09ORklHX1BPV0VSX0FWU19PTUFQKQkJKz0g c21hcnRyZWZsZXgubw0KIG9iai0kKENPTkZJR19ST0NLQ0hJUF9JT0RPTUFJTikJCSs9IHJvY2tj aGlwLWlvLWRvbWFpbi5vDQorb2JqLSQoQ09ORklHX01US19TVlMpCQkJKz0gbXRrX3N2cy5vDQpk aWZmIC0tZ2l0IGEvZHJpdmVycy9wb3dlci9hdnMvbXRrX3N2cy5jIGIvZHJpdmVycy9wb3dlci9h dnMvbXRrX3N2cy5jDQpuZXcgZmlsZSBtb2RlIDEwMDY0NA0KaW5kZXggMDAwMDAwMDAwMDAwLi5j NDYyMTFhMTVmY2QNCi0tLSAvZGV2L251bGwNCisrKyBiL2RyaXZlcnMvcG93ZXIvYXZzL210a19z dnMuYw0KQEAgLTAsMCArMSwyMDc1IEBADQorLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEdQ TC0yLjANCisvKg0KKyAqIENvcHlyaWdodCAoQykgMjAyMCBNZWRpYVRlayBJbmMuDQorICovDQor DQorI2RlZmluZSBwcl9mbXQoZm10KQkiW210a19zdnNdICIgZm10DQorDQorI2luY2x1ZGUgPGxp bnV4L2JpdHMuaD4NCisjaW5jbHVkZSA8bGludXgvY2xrLmg+DQorI2luY2x1ZGUgPGxpbnV4L2Nv bXBsZXRpb24uaD4NCisjaW5jbHVkZSA8bGludXgvaW5pdC5oPg0KKyNpbmNsdWRlIDxsaW51eC9p bnRlcnJ1cHQuaD4NCisjaW5jbHVkZSA8bGludXgva3RocmVhZC5oPg0KKyNpbmNsdWRlIDxsaW51 eC9tb2R1bGUuaD4NCisjaW5jbHVkZSA8bGludXgvbXV0ZXguaD4NCisjaW5jbHVkZSA8bGludXgv bnZtZW0tY29uc3VtZXIuaD4NCisjaW5jbHVkZSA8bGludXgvb2ZfYWRkcmVzcy5oPg0KKyNpbmNs dWRlIDxsaW51eC9vZl9pcnEuaD4NCisjaW5jbHVkZSA8bGludXgvcGxhdGZvcm1fZGV2aWNlLmg+ DQorI2luY2x1ZGUgPGxpbnV4L3BtX2RvbWFpbi5oPg0KKyNpbmNsdWRlIDxsaW51eC9wbV9vcHAu aD4NCisjaW5jbHVkZSA8bGludXgvcG1fcW9zLmg+DQorI2luY2x1ZGUgPGxpbnV4L3BtX3J1bnRp bWUuaD4NCisjaW5jbHVkZSA8bGludXgvcG93ZXIvbXRrX3N2cy5oPg0KKyNpbmNsdWRlIDxsaW51 eC9wcm9jX2ZzLmg+DQorI2luY2x1ZGUgPGxpbnV4L3JlZ3VsYXRvci9jb25zdW1lci5oPg0KKyNp bmNsdWRlIDxsaW51eC9zZXFfZmlsZS5oPg0KKyNpbmNsdWRlIDxsaW51eC9zcGlubG9jay5oPg0K KyNpbmNsdWRlIDxsaW51eC90aGVybWFsLmg+DQorI2luY2x1ZGUgPGxpbnV4L3VhY2Nlc3MuaD4N CisNCisvKiBzdnMgc3cgaWQgKi8NCisjZGVmaW5lIFNWU19DUFVfTElUVExFCQkJQklUKDApDQor I2RlZmluZSBTVlNfQ1BVX0JJRwkJCUJJVCgxKQ0KKyNkZWZpbmUgU1ZTX0NDSQkJCQlCSVQoMikN CisjZGVmaW5lIFNWU19HUFUJCQkJQklUKDMpDQorDQorLyogc3ZzIGJhbmsgbW9kZSBzdXBwb3J0 ICovDQorI2RlZmluZSBTVlNCX01PREVfQUxMX0RJU0FCTEUJCSgwKQ0KKyNkZWZpbmUgU1ZTQl9N T0RFX0lOSVQwMQkJQklUKDEpDQorI2RlZmluZSBTVlNCX01PREVfSU5JVDAyCQlCSVQoMikNCisj ZGVmaW5lIFNWU0JfTU9ERV9NT04JCQlCSVQoMykNCisNCisvKiBzdnMgYmFuayBpbml0MDEgY29u ZGl0aW9uICovDQorI2RlZmluZSBTVlNCX0lOSVQwMV9WT0xUX0lHTk9SRQkJQklUKDEpDQorI2Rl ZmluZSBTVlNCX0lOSVQwMV9WT0xUX0lOQ19PTkxZCUJJVCgyKQ0KKw0KKy8qIHN2cyBiYW5rIGNv bW1vbiBzZXR0aW5nICovDQorI2RlZmluZSBSVU5DT05GSUdfREVGQVVMVAkJKDB4ODAwMDAwMDAp DQorI2RlZmluZSBEQ19TSUdORURfQklUCQkJKDB4ODAwMCkNCisjZGVmaW5lIElOVEVOX0lOSVQw eAkJCSgweDAwMDA1ZjAxKQ0KKyNkZWZpbmUgSU5URU5fTU9OVk9QRU4JCQkoMHgwMGZmMDAwMCkN CisjZGVmaW5lIFNWU0VOX09GRgkJCSgweDApDQorI2RlZmluZSBTVlNFTl9NQVNLCQkJKDB4NykN CisjZGVmaW5lIFNWU0VOX0lOSVQwMQkJCSgweDEpDQorI2RlZmluZSBTVlNFTl9JTklUMDIJCQko MHg1KQ0KKyNkZWZpbmUgU1ZTRU5fTU9OCQkJKDB4MikNCisjZGVmaW5lIElOVFNUU19NT05WT1AJ CQkoMHgwMGZmMDAwMCkNCisjZGVmaW5lIElOVFNUU19DT01QTEVURQkJCSgweDEpDQorI2RlZmlu ZSBJTlRTVFNfQ0xFQU4JCQkoMHgwMGZmZmZmZikNCisNCisjZGVmaW5lIHByb2NfZm9wc19ydyhu YW1lKSBcDQorCXN0YXRpYyBpbnQgbmFtZSAjIyBfcHJvY19vcGVuKHN0cnVjdCBpbm9kZSAqaW5v ZGUsCVwNCisJCXN0cnVjdCBmaWxlICpmaWxlKQkJCQlcDQorCXsJCQkJCQkJXA0KKwkJcmV0dXJu IHNpbmdsZV9vcGVuKGZpbGUsIG5hbWUgIyMgX3Byb2Nfc2hvdywJXA0KKwkJCVBERV9EQVRBKGlu b2RlKSk7CQkJXA0KKwl9CQkJCQkJCVwNCisJc3RhdGljIGNvbnN0IHN0cnVjdCBmaWxlX29wZXJh dGlvbnMgbmFtZSAjIyBfcHJvY19mb3BzID0gewlcDQorCQkub3duZXIgICAgICAgICAgPSBUSElT X01PRFVMRSwJCQkJXA0KKwkJLm9wZW4gICAgICAgICAgID0gbmFtZSAjIyBfcHJvY19vcGVuLAkJ CVwNCisJCS5yZWFkICAgICAgICAgICA9IHNlcV9yZWFkLAkJCQlcDQorCQkubGxzZWVrICAgICAg ICAgPSBzZXFfbHNlZWssCQkJCVwNCisJCS5yZWxlYXNlICAgICAgICA9IHNpbmdsZV9yZWxlYXNl LAkJCVwNCisJCS53cml0ZSAgICAgICAgICA9IG5hbWUgIyMgX3Byb2Nfd3JpdGUsCQkJXA0KKwl9 DQorDQorI2RlZmluZSBwcm9jX2ZvcHNfcm8obmFtZSkgXA0KKwlzdGF0aWMgaW50IG5hbWUgIyMg X3Byb2Nfb3BlbihzdHJ1Y3QgaW5vZGUgKmlub2RlLAlcDQorCQlzdHJ1Y3QgZmlsZSAqZmlsZSkJ CQkJXA0KKwl7CQkJCQkJCVwNCisJCXJldHVybiBzaW5nbGVfb3BlbihmaWxlLCBuYW1lICMjIF9w cm9jX3Nob3csCVwNCisJCQlQREVfREFUQShpbm9kZSkpOwkJCVwNCisJfQkJCQkJCQlcDQorCXN0 YXRpYyBjb25zdCBzdHJ1Y3QgZmlsZV9vcGVyYXRpb25zIG5hbWUgIyMgX3Byb2NfZm9wcyA9IHsJ XA0KKwkJLm93bmVyICAgICAgICAgID0gVEhJU19NT0RVTEUsCQkJCVwNCisJCS5vcGVuICAgICAg ICAgICA9IG5hbWUgIyMgX3Byb2Nfb3BlbiwJCQlcDQorCQkucmVhZCAgICAgICAgICAgPSBzZXFf cmVhZCwJCQkJXA0KKwkJLmxsc2VlayAgICAgICAgID0gc2VxX2xzZWVrLAkJCQlcDQorCQkucmVs ZWFzZSAgICAgICAgPSBzaW5nbGVfcmVsZWFzZSwJCQlcDQorCX0NCisNCisjZGVmaW5lIHByb2Nf ZW50cnkobmFtZSkJe19fc3RyaW5naWZ5KG5hbWUpLCAmbmFtZSAjIyBfcHJvY19mb3BzfQ0KKw0K K3N0YXRpYyBERUZJTkVfU1BJTkxPQ0sobXRrX3N2c19sb2NrKTsNCitzdHJ1Y3QgbXRrX3N2czsN CisNCitlbnVtIHN2c2JfcGhhc2Ugew0KKwlTVlNCX1BIQVNFX0lOSVQwMSA9IDAsDQorCVNWU0Jf UEhBU0VfSU5JVDAyLA0KKwlTVlNCX1BIQVNFX01PTiwNCisJU1ZTQl9QSEFTRV9FUlJPUiwNCit9 Ow0KKw0KK2VudW0gcmVnX2luZGV4IHsNCisJVEVNUE1PTkNUTDAgPSAwLA0KKwlURU1QTU9OQ1RM MSwNCisJVEVNUE1PTkNUTDIsDQorCVRFTVBNT05JTlQsDQorCVRFTVBNT05JTlRTVFMsDQorCVRF TVBNT05JREVUMCwNCisJVEVNUE1PTklERVQxLA0KKwlURU1QTU9OSURFVDIsDQorCVRFTVBIMk5U SFJFLA0KKwlURU1QSFRIUkUsDQorCVRFTVBDVEhSRSwNCisJVEVNUE9GRlNFVEgsDQorCVRFTVBP RkZTRVRMLA0KKwlURU1QTVNSQ1RMMCwNCisJVEVNUE1TUkNUTDEsDQorCVRFTVBBSEJQT0xMLA0K KwlURU1QQUhCVE8sDQorCVRFTVBBRENQTlAwLA0KKwlURU1QQURDUE5QMSwNCisJVEVNUEFEQ1BO UDIsDQorCVRFTVBBRENNVVgsDQorCVRFTVBBRENFWFQsDQorCVRFTVBBRENFWFQxLA0KKwlURU1Q QURDRU4sDQorCVRFTVBQTlBNVVhBRERSLA0KKwlURU1QQURDTVVYQUREUiwNCisJVEVNUEFEQ0VY VEFERFIsDQorCVRFTVBBRENFWFQxQUREUiwNCisJVEVNUEFEQ0VOQUREUiwNCisJVEVNUEFEQ1ZB TElEQUREUiwNCisJVEVNUEFEQ1ZPTFRBRERSLA0KKwlURU1QUkRDVFJMLA0KKwlURU1QQURDVkFM SURNQVNLLA0KKwlURU1QQURDVk9MVEFHRVNISUZULA0KKwlURU1QQURDV1JJVEVDVFJMLA0KKwlU RU1QTVNSMCwNCisJVEVNUE1TUjEsDQorCVRFTVBNU1IyLA0KKwlURU1QQURDSEFERFIsDQorCVRF TVBJTU1EMCwNCisJVEVNUElNTUQxLA0KKwlURU1QSU1NRDIsDQorCVRFTVBNT05JREVUMywNCisJ VEVNUEFEQ1BOUDMsDQorCVRFTVBNU1IzLA0KKwlURU1QSU1NRDMsDQorCVRFTVBQUk9UQ1RMLA0K KwlURU1QUFJPVFRBLA0KKwlURU1QUFJPVFRCLA0KKwlURU1QUFJPVFRDLA0KKwlURU1QU1BBUkUw LA0KKwlURU1QU1BBUkUxLA0KKwlURU1QU1BBUkUyLA0KKwlURU1QU1BBUkUzLA0KKwlURU1QTVNS MF8xLA0KKwlURU1QTVNSMV8xLA0KKwlURU1QTVNSMl8xLA0KKwlURU1QTVNSM18xLA0KKwlERVND SEFSLA0KKwlURU1QQ0hBUiwNCisJREVUQ0hBUiwNCisJQUdFQ0hBUiwNCisJRENDT05GSUcsDQor CUFHRUNPTkZJRywNCisJRlJFUVBDVDMwLA0KKwlGUkVRUENUNzQsDQorCUxJTUlUVkFMUywNCisJ VkJPT1QsDQorCURFVFdJTkRPVywNCisJQ09ORklHLA0KKwlUU0NBTENTLA0KKwlSVU5DT05GSUcs DQorCVNWU0VOLA0KKwlJTklUMlZBTFMsDQorCURDVkFMVUVTLA0KKwlBR0VWQUxVRVMsDQorCVZP UDMwLA0KKwlWT1A3NCwNCisJVEVNUCwNCisJSU5UU1RTLA0KKwlJTlRTVFNSQVcsDQorCUlOVEVO LA0KKwlDSEtJTlQsDQorCUNIS1NISUZULA0KKwlTVEFUVVMsDQorCVZERVNJR04zMCwNCisJVkRF U0lHTjc0LA0KKwlEVlQzMCwNCisJRFZUNzQsDQorCUFHRUNPVU5ULA0KKwlTTVNUQVRFMCwNCisJ U01TVEFURTEsDQorCUNUTDAsDQorCURFU0RFVFNFQywNCisJVEVNUEFHRVNFQywNCisJQ1RSTFNQ QVJFMCwNCisJQ1RSTFNQQVJFMSwNCisJQ1RSTFNQQVJFMiwNCisJQ1RSTFNQQVJFMywNCisJQ09S RVNFTCwNCisJVEhFUk1JTlRTVCwNCisJSU5UU1QsDQorCVRIU1RBR0UwU1QsDQorCVRIU1RBR0Ux U1QsDQorCVRIU1RBR0UyU1QsDQorCVRIQUhCU1QwLA0KKwlUSEFIQlNUMSwNCisJU1BBUkUwLA0K KwlTUEFSRTEsDQorCVNQQVJFMiwNCisJU1BBUkUzLA0KKwlUSFNMUEVWRUIsDQorCXJlZ19udW0s DQorfTsNCisNCitzdGF0aWMgY29uc3QgdTMyIHN2c19yZWdzX3YyW10gPSB7DQorCVtURU1QTU9O Q1RMMF0JCT0gMHgwMDAsDQorCVtURU1QTU9OQ1RMMV0JCT0gMHgwMDQsDQorCVtURU1QTU9OQ1RM Ml0JCT0gMHgwMDgsDQorCVtURU1QTU9OSU5UXQkJPSAweDAwYywNCisJW1RFTVBNT05JTlRTVFNd CQk9IDB4MDEwLA0KKwlbVEVNUE1PTklERVQwXQkJPSAweDAxNCwNCisJW1RFTVBNT05JREVUMV0J CT0gMHgwMTgsDQorCVtURU1QTU9OSURFVDJdCQk9IDB4MDFjLA0KKwlbVEVNUEgyTlRIUkVdCQk9 IDB4MDI0LA0KKwlbVEVNUEhUSFJFXQkJPSAweDAyOCwNCisJW1RFTVBDVEhSRV0JCT0gMHgwMmMs DQorCVtURU1QT0ZGU0VUSF0JCT0gMHgwMzAsDQorCVtURU1QT0ZGU0VUTF0JCT0gMHgwMzQsDQor CVtURU1QTVNSQ1RMMF0JCT0gMHgwMzgsDQorCVtURU1QTVNSQ1RMMV0JCT0gMHgwM2MsDQorCVtU RU1QQUhCUE9MTF0JCT0gMHgwNDAsDQorCVtURU1QQUhCVE9dCQk9IDB4MDQ0LA0KKwlbVEVNUEFE Q1BOUDBdCQk9IDB4MDQ4LA0KKwlbVEVNUEFEQ1BOUDFdCQk9IDB4MDRjLA0KKwlbVEVNUEFEQ1BO UDJdCQk9IDB4MDUwLA0KKwlbVEVNUEFEQ01VWF0JCT0gMHgwNTQsDQorCVtURU1QQURDRVhUXQkJ PSAweDA1OCwNCisJW1RFTVBBRENFWFQxXQkJPSAweDA1YywNCisJW1RFTVBBRENFTl0JCT0gMHgw NjAsDQorCVtURU1QUE5QTVVYQUREUl0JPSAweDA2NCwNCisJW1RFTVBBRENNVVhBRERSXQk9IDB4 MDY4LA0KKwlbVEVNUEFEQ0VYVEFERFJdCT0gMHgwNmMsDQorCVtURU1QQURDRVhUMUFERFJdCT0g MHgwNzAsDQorCVtURU1QQURDRU5BRERSXQkJPSAweDA3NCwNCisJW1RFTVBBRENWQUxJREFERFJd CT0gMHgwNzgsDQorCVtURU1QQURDVk9MVEFERFJdCT0gMHgwN2MsDQorCVtURU1QUkRDVFJMXQkJ PSAweDA4MCwNCisJW1RFTVBBRENWQUxJRE1BU0tdCT0gMHgwODQsDQorCVtURU1QQURDVk9MVEFH RVNISUZUXQk9IDB4MDg4LA0KKwlbVEVNUEFEQ1dSSVRFQ1RSTF0JPSAweDA4YywNCisJW1RFTVBN U1IwXQkJPSAweDA5MCwNCisJW1RFTVBNU1IxXQkJPSAweDA5NCwNCisJW1RFTVBNU1IyXQkJPSAw eDA5OCwNCisJW1RFTVBBRENIQUREUl0JCT0gMHgwOWMsDQorCVtURU1QSU1NRDBdCQk9IDB4MGEw LA0KKwlbVEVNUElNTUQxXQkJPSAweDBhNCwNCisJW1RFTVBJTU1EMl0JCT0gMHgwYTgsDQorCVtU RU1QTU9OSURFVDNdCQk9IDB4MGIwLA0KKwlbVEVNUEFEQ1BOUDNdCQk9IDB4MGI0LA0KKwlbVEVN UE1TUjNdCQk9IDB4MGI4LA0KKwlbVEVNUElNTUQzXQkJPSAweDBiYywNCisJW1RFTVBQUk9UQ1RM XQkJPSAweDBjMCwNCisJW1RFTVBQUk9UVEFdCQk9IDB4MGM0LA0KKwlbVEVNUFBST1RUQl0JCT0g MHgwYzgsDQorCVtURU1QUFJPVFRDXQkJPSAweDBjYywNCisJW1RFTVBTUEFSRTBdCQk9IDB4MGYw LA0KKwlbVEVNUFNQQVJFMV0JCT0gMHgwZjQsDQorCVtURU1QU1BBUkUyXQkJPSAweDBmOCwNCisJ W1RFTVBTUEFSRTNdCQk9IDB4MGZjLA0KKwlbVEVNUE1TUjBfMV0JCT0gMHgxOTAsDQorCVtURU1Q TVNSMV8xXQkJPSAweDE5NCwNCisJW1RFTVBNU1IyXzFdCQk9IDB4MTk4LA0KKwlbVEVNUE1TUjNf MV0JCT0gMHgxYjgsDQorCVtERVNDSEFSXQkJPSAweGMwMCwNCisJW1RFTVBDSEFSXQkJPSAweGMw NCwNCisJW0RFVENIQVJdCQk9IDB4YzA4LA0KKwlbQUdFQ0hBUl0JCT0gMHhjMGMsDQorCVtEQ0NP TkZJR10JCT0gMHhjMTAsDQorCVtBR0VDT05GSUddCQk9IDB4YzE0LA0KKwlbRlJFUVBDVDMwXQkJ PSAweGMxOCwNCisJW0ZSRVFQQ1Q3NF0JCT0gMHhjMWMsDQorCVtMSU1JVFZBTFNdCQk9IDB4YzIw LA0KKwlbVkJPT1RdCQkJPSAweGMyNCwNCisJW0RFVFdJTkRPV10JCT0gMHhjMjgsDQorCVtDT05G SUddCQk9IDB4YzJjLA0KKwlbVFNDQUxDU10JCT0gMHhjMzAsDQorCVtSVU5DT05GSUddCQk9IDB4 YzM0LA0KKwlbU1ZTRU5dCQkJPSAweGMzOCwNCisJW0lOSVQyVkFMU10JCT0gMHhjM2MsDQorCVtE Q1ZBTFVFU10JCT0gMHhjNDAsDQorCVtBR0VWQUxVRVNdCQk9IDB4YzQ0LA0KKwlbVk9QMzBdCQkJ PSAweGM0OCwNCisJW1ZPUDc0XQkJCT0gMHhjNGMsDQorCVtURU1QXQkJCT0gMHhjNTAsDQorCVtJ TlRTVFNdCQk9IDB4YzU0LA0KKwlbSU5UU1RTUkFXXQkJPSAweGM1OCwNCisJW0lOVEVOXQkJCT0g MHhjNWMsDQorCVtDSEtJTlRdCQk9IDB4YzYwLA0KKwlbQ0hLU0hJRlRdCQk9IDB4YzY0LA0KKwlb U1RBVFVTXQkJPSAweGM2OCwNCisJW1ZERVNJR04zMF0JCT0gMHhjNmMsDQorCVtWREVTSUdONzRd CQk9IDB4YzcwLA0KKwlbRFZUMzBdCQkJPSAweGM3NCwNCisJW0RWVDc0XQkJCT0gMHhjNzgsDQor CVtBR0VDT1VOVF0JCT0gMHhjN2MsDQorCVtTTVNUQVRFMF0JCT0gMHhjODAsDQorCVtTTVNUQVRF MV0JCT0gMHhjODQsDQorCVtDVEwwXQkJCT0gMHhjODgsDQorCVtERVNERVRTRUNdCQk9IDB4Y2Uw LA0KKwlbVEVNUEFHRVNFQ10JCT0gMHhjZTQsDQorCVtDVFJMU1BBUkUwXQkJPSAweGNmMCwNCisJ W0NUUkxTUEFSRTFdCQk9IDB4Y2Y0LA0KKwlbQ1RSTFNQQVJFMl0JCT0gMHhjZjgsDQorCVtDVFJM U1BBUkUzXQkJPSAweGNmYywNCisJW0NPUkVTRUxdCQk9IDB4ZjAwLA0KKwlbVEhFUk1JTlRTVF0J CT0gMHhmMDQsDQorCVtJTlRTVF0JCQk9IDB4ZjA4LA0KKwlbVEhTVEFHRTBTVF0JCT0gMHhmMGMs DQorCVtUSFNUQUdFMVNUXQkJPSAweGYxMCwNCisJW1RIU1RBR0UyU1RdCQk9IDB4ZjE0LA0KKwlb VEhBSEJTVDBdCQk9IDB4ZjE4LA0KKwlbVEhBSEJTVDFdCQk9IDB4ZjFjLA0KKwlbU1BBUkUwXQkJ PSAweGYyMCwNCisJW1NQQVJFMV0JCT0gMHhmMjQsDQorCVtTUEFSRTJdCQk9IDB4ZjI4LA0KKwlb U1BBUkUzXQkJPSAweGYyYywNCisJW1RIU0xQRVZFQl0JCT0gMHhmMzAsDQorfTsNCisNCitzdHJ1 Y3QgdGhlcm1hbF9wYXJhbWV0ZXIgew0KKwlpbnQgYWRjX2dlX3Q7DQorCWludCBhZGNfb2VfdDsN CisJaW50IGdlOw0KKwlpbnQgb2U7DQorCWludCBnYWluOw0KKwlpbnQgb192dHNhYmI7DQorCWlu dCBvX3Z0c21jdTE7DQorCWludCBvX3Z0c21jdTI7DQorCWludCBvX3Z0c21jdTM7DQorCWludCBv X3Z0c21jdTQ7DQorCWludCBvX3Z0c21jdTU7DQorCWludCBkZWdjX2NhbGk7DQorCWludCBhZGNf Y2FsaV9lbl90Ow0KKwlpbnQgb19zbG9wZTsNCisJaW50IG9fc2xvcGVfc2lnbjsNCisJaW50IHRz X2lkOw0KK307DQorDQorc3RydWN0IHN2c19iYW5rX29wcyB7DQorCXZvaWQgKCpzZXRfZnJlcXNf cGN0KShzdHJ1Y3QgbXRrX3N2cyAqc3ZzKTsNCisJdm9pZCAoKmdldF92b3BzKShzdHJ1Y3QgbXRr X3N2cyAqc3ZzKTsNCit9Ow0KKw0KK3N0cnVjdCBzdnNfYmFuayB7DQorCXN0cnVjdCBzdnNfYmFu a19vcHMgKm9wczsNCisJc3RydWN0IGNvbXBsZXRpb24gaW5pdF9jb21wbGV0aW9uOw0KKwlzdHJ1 Y3QgZGV2aWNlICpkZXY7DQorCXN0cnVjdCByZWd1bGF0b3IgKmJ1Y2s7DQorCXN0cnVjdCBtdXRl eCBsb2NrOwkvKiBMb2NrIHRvIHByb3RlY3QgdXBkYXRlIHZvbHRhZ2UgcHJvY2VzcyAqLw0KKwli b29sIHN1c3BlbmRlZDsNCisJYm9vbCBtdGNtb3NfcmVxdWVzdDsNCisJczMyIHZvbHRfb2Zmc2V0 Ow0KKwl1MzIgbW9kZV9zdXBwb3J0Ow0KKwl1MzIgb3BwX2ZyZXFzWzE2XTsNCisJdTMyIGZyZXFz X3BjdFsxNl07DQorCXUzMiBvcHBfdm9sdHNbMTZdOw0KKwl1MzIgaW5pdDAyX3ZvbHRzWzE2XTsN CisJdTMyIHZvbHRzWzE2XTsNCisJdTMyIHJlZ19kYXRhWzNdW3JlZ19udW1dOw0KKwl1MzIgZnJl cV9iYXNlOw0KKwl1MzIgdmJvb3Q7DQorCXUzMiB2b2x0X3N0ZXA7DQorCXUzMiB2b2x0X2Jhc2U7 DQorCXUzMiBpbml0MDFfdm9sdF9mbGFnOw0KKwl1MzIgcGhhc2U7DQorCXUzMiB2bWF4Ow0KKwl1 MzIgdm1pbjsNCisJdTMyIGJ0czsNCisJdTMyIG10czsNCisJdTMyIGJkZXM7DQorCXUzMiBtZGVz Ow0KKwl1MzIgbXRkZXM7DQorCXUzMiBkY2JkZXQ7DQorCXUzMiBkY21kZXQ7DQorCXUzMiBkdGhp Ow0KKwl1MzIgZHRsbzsNCisJdTMyIGRldF93aW5kb3c7DQorCXUzMiBkZXRfbWF4Ow0KKwl1MzIg YWdlX2NvbmZpZzsNCisJdTMyIGFnZV92b2Zmc2V0X2luOw0KKwl1MzIgYWdlbTsNCisJdTMyIGRj X2NvbmZpZzsNCisJdTMyIGRjX3ZvZmZzZXRfaW47DQorCXUzMiBkdnRfZml4ZWQ7DQorCXUzMiB2 Y287DQorCXUzMiBjaGtzaGlmdDsNCisJdTMyIHN2c190ZW1wOw0KKwl1MzIgdXBwZXJfdGVtcF9i b3VuZDsNCisJdTMyIGxvd2VyX3RlbXBfYm91bmQ7DQorCXUzMiBsb3dfdGVtcF90aHJlYXNob2xk Ow0KKwl1MzIgbG93X3RlbXBfb2Zmc2V0Ow0KKwl1MzIgY29yZXNlbDsNCisJdTMyIG9wcF9jb3Vu dDsNCisJdTMyIGludHN0Ow0KKwl1MzIgc3lzdGVtY2xrX2VuOw0KKwl1MzIgc3dfaWQ7DQorCXUz MiBiYW5rX2lkOw0KKwl1MzIgY3RsMDsNCisJdTggKm9mX2NvbXBhdGlibGU7DQorCXU4ICpuYW1l Ow0KKwl1OCAqem9uZV9uYW1lOw0KKwl1OCAqYnVja19uYW1lOw0KK307DQorDQorc3RydWN0IHN2 c19wbGF0Zm9ybSB7DQorCXN0cnVjdCBzdnNfYmFuayAqYmFua3M7DQorCWJvb2wgKCplZnVzZV9w YXJzaW5nKShzdHJ1Y3QgbXRrX3N2cyAqc3ZzKTsNCisJYm9vbCBmYWtlX2VmdXNlOw0KKwljb25z dCB1MzIgKnJlZ3M7DQorCXUzMiBiYW5rX251bTsNCisJdTMyIGVmdXNlX251bTsNCisJdTMyIGVm dXNlX2NoZWNrOw0KKwl1MzIgdGhlcm1hbF9lZnVzZV9udW07DQorCXU4ICpuYW1lOw0KK307DQor DQorc3RydWN0IG10a19zdnMgew0KKwljb25zdCBzdHJ1Y3Qgc3ZzX3BsYXRmb3JtICpwbGF0Zm9y bTsNCisJc3RydWN0IHN2c19iYW5rICpiYW5rOw0KKwlzdHJ1Y3QgZGV2aWNlICpkZXY7DQorCXZv aWQgX19pb21lbSAqYmFzZTsNCisJc3RydWN0IGNsayAqbWFpbl9jbGs7DQorCXUzMiAqZWZ1c2U7 DQorCXUzMiAqdGhlcm1hbF9lZnVzZTsNCit9Ow0KKw0KK3Vuc2lnbmVkIGxvbmcgY2xhaW1fbXRr X3N2c19sb2NrKHZvaWQpDQorCV9fYWNxdWlyZXMoJm10a19zdnNfbG9jaykNCit7DQorCXVuc2ln bmVkIGxvbmcgZmxhZ3M7DQorDQorCXNwaW5fbG9ja19pcnFzYXZlKCZtdGtfc3ZzX2xvY2ssIGZs YWdzKTsNCisNCisJcmV0dXJuIGZsYWdzOw0KK30NCitFWFBPUlRfU1lNQk9MX0dQTChjbGFpbV9t dGtfc3ZzX2xvY2spOw0KKw0KK3ZvaWQgcmVsZWFzZV9tdGtfc3ZzX2xvY2sodW5zaWduZWQgbG9u ZyBmbGFncykNCisJX19yZWxlYXNlcygmbXRrX3N2c19sb2NrKQ0KK3sNCisJc3Bpbl91bmxvY2tf aXJxcmVzdG9yZSgmbXRrX3N2c19sb2NrLCBmbGFncyk7DQorfQ0KK0VYUE9SVF9TWU1CT0xfR1BM KHJlbGVhc2VfbXRrX3N2c19sb2NrKTsNCisNCitzdGF0aWMgdTMyIHBlcmNlbnQodTMyIG51bWVy YXRvciwgdTMyIGRlbm9taW5hdG9yKQ0KK3sNCisJdTMyIHBlcmNlbnQ7DQorDQorCS8qIElmIG5v dCBkaXZpZGUgMTAwMCwgIm51bWVyYXRvciAqIDEwMCIgd291bGQgYmUgZGF0YSBvdmVyZmxvdy4g Ki8NCisJbnVtZXJhdG9yIC89IDEwMDA7DQorCWRlbm9taW5hdG9yIC89IDEwMDA7DQorCXBlcmNl bnQgPSAoKG51bWVyYXRvciAqIDEwMCkgKyBkZW5vbWluYXRvciAtIDEpIC8gZGVub21pbmF0b3I7 DQorCXBlcmNlbnQgJj0gR0VOTUFTSyg3LCAwKTsNCisNCisJcmV0dXJuIHBlcmNlbnQ7DQorfQ0K Kw0KK3N0YXRpYyB1MzIgc3ZzX3JlYWRsKHN0cnVjdCBtdGtfc3ZzICpzdnMsIGVudW0gcmVnX2lu ZGV4IHJnX2kpDQorew0KKwlyZXR1cm4gcmVhZGwoc3ZzLT5iYXNlICsgc3ZzLT5wbGF0Zm9ybS0+ cmVnc1tyZ19pXSk7DQorfQ0KKw0KK3N0YXRpYyB2b2lkIHN2c193cml0ZWwoc3RydWN0IG10a19z dnMgKnN2cywgdTMyIHZhbCwgZW51bSByZWdfaW5kZXggcmdfaSkNCit7DQorCXdyaXRlbCh2YWws IHN2cy0+YmFzZSArIHN2cy0+cGxhdGZvcm0tPnJlZ3NbcmdfaV0pOw0KK30NCisNCitzdGF0aWMg dm9pZCBzdnNfc3dpdGNoX2Jhbmsoc3RydWN0IG10a19zdnMgKnN2cykNCit7DQorCXN0cnVjdCBz dnNfYmFuayAqc3ZzYiA9IHN2cy0+YmFuazsNCisNCisJc3ZzX3dyaXRlbChzdnMsIHN2c2ItPmNv cmVzZWwsIENPUkVTRUwpOw0KK30NCisNCitzdGF0aWMgdTMyIHN2c2Jfdm9sdF90b19vcHBfdm9s dCh1MzIgc3ZzYl92b2x0LCB1MzIgc3ZzYl92b2x0X3N0ZXAsDQorCQkJCSB1MzIgc3ZzYl92b2x0 X2Jhc2UpDQorew0KKwl1MzIgdV92b2x0Ow0KKw0KKwl1X3ZvbHQgPSAoc3ZzYl92b2x0ICogc3Zz Yl92b2x0X3N0ZXApICsgc3ZzYl92b2x0X2Jhc2U7DQorDQorCXJldHVybiB1X3ZvbHQ7DQorfQ0K Kw0KK3N0YXRpYyBpbnQgc3ZzYl9nZXRfem9uZV90ZW1wZXJhdHVyZShzdHJ1Y3Qgc3ZzX2Jhbmsg KnN2c2IsIGludCAqem9uZV90ZW1wKQ0KK3sNCisJc3RydWN0IHRoZXJtYWxfem9uZV9kZXZpY2Ug KnR6ZDsNCisNCisJdHpkID0gdGhlcm1hbF96b25lX2dldF96b25lX2J5X25hbWUoc3ZzYi0+em9u ZV9uYW1lKTsNCisJaWYgKElTX0VSUih0emQpKQ0KKwkJcmV0dXJuIFBUUl9FUlIodHpkKTsNCisN CisJcmV0dXJuIHRoZXJtYWxfem9uZV9nZXRfdGVtcCh0emQsIHpvbmVfdGVtcCk7DQorfQ0KKw0K K3N0YXRpYyBpbnQgc3ZzYl9zZXRfdm9sdHMoc3RydWN0IHN2c19iYW5rICpzdnNiLCBib29sIGZv cmNlX3VwZGF0ZSkNCit7DQorCXUzMiBpLCBzdnNiX3ZvbHQsIG9wcF92b2x0LCBsb3dfdGVtcF9v ZmZzZXQgPSAwOw0KKwlpbnQgem9uZV90ZW1wLCByZXQ7DQorDQorCW11dGV4X2xvY2soJnN2c2It PmxvY2spOw0KKw0KKwkvKg0KKwkgKiBJZiBiYW5rIGlzIHN1c3BlbmRlZCwgaXQgbWVhbnMgaW5p dDAyIHZvbHRhZ2UgaXMgYXBwbGllZC4NCisJICogRG9uJ3QgbmVlZCB0byB1cGRhdGUgb3BwIHZv bHRhZ2UgYW55bW9yZS4NCisJICovDQorCWlmIChzdnNiLT5zdXNwZW5kZWQgJiYgIWZvcmNlX3Vw ZGF0ZSkgew0KKwkJcHJfbm90aWNlKCIlczogYmFuayBpcyBzdXNwZW5kZWRcbiIsIHN2c2ItPm5h bWUpOw0KKwkJbXV0ZXhfdW5sb2NrKCZzdnNiLT5sb2NrKTsNCisJCXJldHVybiAtRVBFUk07DQor CX0NCisNCisJLyogR2V0IHRoZXJtYWwgZWZmZWN0ICovDQorCWlmIChzdnNiLT5waGFzZSA9PSBT VlNCX1BIQVNFX01PTikgew0KKwkJaWYgKHN2c2ItPnN2c190ZW1wID4gc3ZzYi0+dXBwZXJfdGVt cF9ib3VuZCAmJg0KKwkJICAgIHN2c2ItPnN2c190ZW1wIDwgc3ZzYi0+bG93ZXJfdGVtcF9ib3Vu ZCkgew0KKwkJCXByX2VycigiJXM6IHN2c190ZW1wIGlzIGFibm9ybWFsICgweCV4KT9cbiIsDQor CQkJICAgICAgIHN2c2ItPm5hbWUsIHN2c2ItPnN2c190ZW1wKTsNCisJCQltdXRleF91bmxvY2so JnN2c2ItPmxvY2spOw0KKwkJCXJldHVybiAtRUlOVkFMOw0KKwkJfQ0KKw0KKwkJcmV0ID0gc3Zz Yl9nZXRfem9uZV90ZW1wZXJhdHVyZShzdnNiLCAmem9uZV90ZW1wKTsNCisJCWlmIChyZXQpIHsN CisJCQlwcl9lcnIoIiVzOiBjYW5ub3QgZ2V0IHpvbmUgXCIlc1wiIHRlbXBlcmF0dXJlKCVkKVxu IiwNCisJCQkgICAgICAgc3ZzYi0+bmFtZSwgc3ZzYi0+em9uZV9uYW1lLCByZXQpOw0KKwkJCXBy X2VycigiJXM6IGFkZCBsb3dfdGVtcF9vZmZzZXQgPSAldVxuIiwNCisJCQkgICAgICAgc3ZzYi0+ bmFtZSwgc3ZzYi0+bG93X3RlbXBfb2Zmc2V0KTsNCisJCQl6b25lX3RlbXAgPSBzdnNiLT5sb3df dGVtcF90aHJlYXNob2xkOw0KKwkJfQ0KKw0KKwkJaWYgKHpvbmVfdGVtcCA8PSBzdnNiLT5sb3df dGVtcF90aHJlYXNob2xkKQ0KKwkJCWxvd190ZW1wX29mZnNldCA9IHN2c2ItPmxvd190ZW1wX29m ZnNldDsNCisJfQ0KKw0KKwkvKiB2bWluIDw9IHN2c2Jfdm9sdCAob3BwX3ZvbHQpIDw9IHNpZ25l ZC1vZmYgdm9sdGFnZSAqLw0KKwlmb3IgKGkgPSAwOyBpIDwgc3ZzYi0+b3BwX2NvdW50OyBpKysp IHsNCisJCWlmIChzdnNiLT5waGFzZSA9PSBTVlNCX1BIQVNFX01PTikgew0KKwkJCXN2c2Jfdm9s dCA9IG1heCgoc3ZzYi0+dm9sdHNbaV0gKyBzdnNiLT52b2x0X29mZnNldCArDQorCQkJCQkgbG93 X3RlbXBfb2Zmc2V0KSwgc3ZzYi0+dm1pbik7DQorCQkJb3BwX3ZvbHQgPSBzdnNiX3ZvbHRfdG9f b3BwX3ZvbHQoc3ZzYl92b2x0LA0KKwkJCQkJCQkgc3ZzYi0+dm9sdF9zdGVwLA0KKwkJCQkJCQkg c3ZzYi0+dm9sdF9iYXNlKTsNCisJCX0gZWxzZSBpZiAoc3ZzYi0+cGhhc2UgPT0gU1ZTQl9QSEFT RV9JTklUMDIpIHsNCisJCQlzdnNiX3ZvbHQgPSBtYXgoKHN2c2ItPmluaXQwMl92b2x0c1tpXSAr DQorCQkJCQkgc3ZzYi0+dm9sdF9vZmZzZXQpLCBzdnNiLT52bWluKTsNCisJCQlvcHBfdm9sdCA9 IHN2c2Jfdm9sdF90b19vcHBfdm9sdChzdnNiX3ZvbHQsDQorCQkJCQkJCSBzdnNiLT52b2x0X3N0 ZXAsDQorCQkJCQkJCSBzdnNiLT52b2x0X2Jhc2UpOw0KKwkJfSBlbHNlIGlmIChzdnNiLT5waGFz ZSA9PSBTVlNCX1BIQVNFX0VSUk9SKSB7DQorCQkJb3BwX3ZvbHQgPSBzdnNiLT5vcHBfdm9sdHNb aV07DQorCQl9IGVsc2Ugew0KKwkJCXByX2VycigiJXM6IHVua25vd24gcGhhc2U6ICV1P1xuIiwN CisJCQkgICAgICAgc3ZzYi0+bmFtZSwgc3ZzYi0+cGhhc2UpOw0KKwkJCW11dGV4X3VubG9jaygm c3ZzYi0+bG9jayk7DQorCQkJcmV0dXJuIC1FSU5WQUw7DQorCQl9DQorDQorCQlvcHBfdm9sdCA9 IG1pbihvcHBfdm9sdCwgc3ZzYi0+b3BwX3ZvbHRzW2ldKTsNCisJCXJldCA9IGRldl9wbV9vcHBf YWRqdXN0X3ZvbHRhZ2Uoc3ZzYi0+ZGV2LCBzdnNiLT5vcHBfZnJlcXNbaV0sDQorCQkJCQkJb3Bw X3ZvbHQsIG9wcF92b2x0LA0KKwkJCQkJCXN2c2ItPm9wcF92b2x0c1tpXSk7DQorCQlpZiAocmV0 KSB7DQorCQkJcHJfZXJyKCIlczogc2V0IHZvbHRhZ2UgZmFpbGVkOiAlZFxuIiwgc3ZzYi0+bmFt ZSwgcmV0KTsNCisJCQltdXRleF91bmxvY2soJnN2c2ItPmxvY2spOw0KKwkJCXJldHVybiByZXQ7 DQorCQl9DQorCX0NCisNCisJbXV0ZXhfdW5sb2NrKCZzdnNiLT5sb2NrKTsNCisNCisJcmV0dXJu IDA7DQorfQ0KKw0KK3N0YXRpYyB1MzIgaW50ZXJwb2xhdGUodTMyIGYwLCB1MzIgZjEsIHUzMiB2 MCwgdTMyIHYxLCB1MzIgZngpDQorew0KKwl1MzIgdnk7DQorDQorCWlmICh2MCA9PSB2MSB8fCBm MCA9PSBmMSkNCisJCXJldHVybiB2MDsNCisNCisJLyogKjEwMCB0byBoYXZlIGRlY2ltYWwgZnJh Y3Rpb24gZmFjdG9yLCArOTkgZm9yIHJvdW5kaW5nIHVwLiAqLw0KKwl2eSA9ICh2MCAqIDEwMCkg LSAoKCgodjAgLSB2MSkgKiAxMDApIC8gKGYwIC0gZjEpKSAqIChmMCAtIGZ4KSk7DQorCXZ5ID0g KHZ5ICsgOTkpIC8gMTAwOw0KKw0KKwlyZXR1cm4gdnk7DQorfQ0KKw0KK3N0YXRpYyB2b2lkIHN2 c19nZXRfdm9wc192MihzdHJ1Y3QgbXRrX3N2cyAqc3ZzKQ0KK3sNCisJc3RydWN0IHN2c19iYW5r ICpzdnNiID0gc3ZzLT5iYW5rOw0KKwl1MzIgdGVtcCwgaTsNCisNCisJdGVtcCA9IHN2c19yZWFk bChzdnMsIFZPUDMwKTsNCisJc3ZzYi0+dm9sdHNbNl0gPSAodGVtcCA+PiAyNCkgJiBHRU5NQVNL KDcsIDApOw0KKwlzdnNiLT52b2x0c1s0XSA9ICh0ZW1wID4+IDE2KSAmIEdFTk1BU0soNywgMCk7 DQorCXN2c2ItPnZvbHRzWzJdID0gKHRlbXAgPj4gOCkgICYgR0VOTUFTSyg3LCAwKTsNCisJc3Zz Yi0+dm9sdHNbMF0gPSAodGVtcCAmIEdFTk1BU0soNywgMCkpOw0KKw0KKwl0ZW1wID0gc3ZzX3Jl YWRsKHN2cywgVk9QNzQpOw0KKwlzdnNiLT52b2x0c1sxNF0gPSAodGVtcCA+PiAyNCkgJiBHRU5N QVNLKDcsIDApOw0KKwlzdnNiLT52b2x0c1sxMl0gPSAodGVtcCA+PiAxNikgJiBHRU5NQVNLKDcs IDApOw0KKwlzdnNiLT52b2x0c1sxMF0gPSAodGVtcCA+PiA4KSAgJiBHRU5NQVNLKDcsIDApOw0K KwlzdnNiLT52b2x0c1s4XSA9ICh0ZW1wICYgR0VOTUFTSyg3LCAwKSk7DQorDQorCWZvciAoaSA9 IDA7IGkgPD0gNzsgaSsrKSB7DQorCQlpZiAoaSA8IDcpIHsNCisJCQlzdnNiLT52b2x0c1soaSAq IDIpICsgMV0gPQ0KKwkJCQlpbnRlcnBvbGF0ZShzdnNiLT5mcmVxc19wY3RbaSAqIDJdLA0KKwkJ CQkJICAgIHN2c2ItPmZyZXFzX3BjdFsoaSArIDEpICogMl0sDQorCQkJCQkgICAgc3ZzYi0+dm9s dHNbaSAqIDJdLA0KKwkJCQkJICAgIHN2c2ItPnZvbHRzWyhpICsgMSkgKiAyXSwNCisJCQkJCSAg ICBzdnNiLT5mcmVxc19wY3RbKGkgKiAyKSArIDFdKTsNCisJCX0gZWxzZSBpZiAoaSA9PSA3KSB7 DQorCQkJc3ZzYi0+dm9sdHNbKGkgKiAyKSArIDFdID0NCisJCQkJaW50ZXJwb2xhdGUoc3ZzYi0+ ZnJlcXNfcGN0WyhpIC0gMSkgKiAyXSwNCisJCQkJCSAgICBzdnNiLT5mcmVxc19wY3RbaSAqIDJd LA0KKwkJCQkJICAgIHN2c2ItPnZvbHRzWyhpIC0gMSkgKiAyXSwNCisJCQkJCSAgICBzdnNiLT52 b2x0c1tpICogMl0sDQorCQkJCQkgICAgc3ZzYi0+ZnJlcXNfcGN0WyhpICogMikgKyAxXSk7DQor CQl9DQorCX0NCit9DQorDQorc3RhdGljIHZvaWQgc3ZzX3NldF9mcmVxc19wY3RfdjIoc3RydWN0 IG10a19zdnMgKnN2cykNCit7DQorCXN0cnVjdCBzdnNfYmFuayAqc3ZzYiA9IHN2cy0+YmFuazsN CisNCisJc3ZzX3dyaXRlbChzdnMsDQorCQkgICAoKHN2c2ItPmZyZXFzX3BjdFs2XSA8PCAyNCkg JiBHRU5NQVNLKDMxLCAyNCkpIHwNCisJCSAgICgoc3ZzYi0+ZnJlcXNfcGN0WzRdIDw8IDE2KSAm IEdFTk1BU0soMjMsIDE2KSkgfA0KKwkJICAgKChzdnNiLT5mcmVxc19wY3RbMl0gPDwgOCkgJiBH RU5NQVNLKDE1LCA4KSkgfA0KKwkJICAgKHN2c2ItPmZyZXFzX3BjdFswXSAmIEdFTk1BU0soNywg MCkpLA0KKwkJICAgRlJFUVBDVDMwKTsNCisJc3ZzX3dyaXRlbChzdnMsDQorCQkgICAoKHN2c2It PmZyZXFzX3BjdFsxNF0gPDwgMjQpICYgR0VOTUFTSygzMSwgMjQpKSB8DQorCQkgICAoKHN2c2It PmZyZXFzX3BjdFsxMl0gPDwgMTYpICYgR0VOTUFTSygyMywgMTYpKSB8DQorCQkgICAoKHN2c2It PmZyZXFzX3BjdFsxMF0gPDwgOCkgJiBHRU5NQVNLKDE1LCA4KSkgfA0KKwkJICAgKChzdnNiLT5m cmVxc19wY3RbOF0pICYgR0VOTUFTSyg3LCAwKSksDQorCQkgICBGUkVRUENUNzQpOw0KK30NCisN CitzdGF0aWMgdm9pZCBzdnNfc2V0X3BoYXNlKHN0cnVjdCBtdGtfc3ZzICpzdnMsIHUzMiB0YXJn ZXRfcGhhc2UpDQorew0KKwlzdHJ1Y3Qgc3ZzX2JhbmsgKnN2c2IgPSBzdnMtPmJhbms7DQorCXUz MiBkZXNfY2hhciwgdGVtcF9jaGFyLCBkZXRfY2hhciwgbGltaXRfdmFsczsNCisJdTMyIGluaXQy dmFscywgdHNfY2FsY3MsIHZhbCwgZmlsdGVyLCBpOw0KKw0KKwlzdnNfc3dpdGNoX2Jhbmsoc3Zz KTsNCisNCisJZGVzX2NoYXIgPSAoKHN2c2ItPmJkZXMgPDwgOCkgJiBHRU5NQVNLKDE1LCA4KSkg fA0KKwkJICAgKHN2c2ItPm1kZXMgJiBHRU5NQVNLKDcsIDApKTsNCisJc3ZzX3dyaXRlbChzdnMs IGRlc19jaGFyLCBERVNDSEFSKTsNCisNCisJdGVtcF9jaGFyID0gKChzdnNiLT52Y28gPDwgMTYp ICYgR0VOTUFTSygyMywgMTYpKSB8DQorCQkgICAgKChzdnNiLT5tdGRlcyA8PCA4KSAmIEdFTk1B U0soMTUsIDgpKSB8DQorCQkgICAgKHN2c2ItPmR2dF9maXhlZCAmIEdFTk1BU0soNywgMCkpOw0K KwlzdnNfd3JpdGVsKHN2cywgdGVtcF9jaGFyLCBURU1QQ0hBUik7DQorDQorCWRldF9jaGFyID0g KChzdnNiLT5kY2JkZXQgPDwgOCkgJiBHRU5NQVNLKDE1LCA4KSkgfA0KKwkJICAgKHN2c2ItPmRj bWRldCAmIEdFTk1BU0soNywgMCkpOw0KKwlzdnNfd3JpdGVsKHN2cywgZGV0X2NoYXIsIERFVENI QVIpOw0KKw0KKwlzdnNfd3JpdGVsKHN2cywgc3ZzYi0+ZGNfY29uZmlnLCBEQ0NPTkZJRyk7DQor CXN2c193cml0ZWwoc3ZzLCBzdnNiLT5hZ2VfY29uZmlnLCBBR0VDT05GSUcpOw0KKw0KKwlpZiAo c3ZzYi0+YWdlbSA9PSAweDApIHsNCisJCXN2c193cml0ZWwoc3ZzLCBSVU5DT05GSUdfREVGQVVM VCwgUlVOQ09ORklHKTsNCisJfSBlbHNlIHsNCisJCXZhbCA9IDB4MDsNCisNCisJCWZvciAoaSA9 IDA7IGkgPCAyNDsgaSArPSAyKSB7DQorCQkJZmlsdGVyID0gMHgzIDw8IGk7DQorDQorCQkJaWYg KChzdnNiLT5hZ2VfY29uZmlnICYgZmlsdGVyKSA9PSAweDApDQorCQkJCXZhbCB8PSAoMHgxIDw8 IGkpOw0KKwkJCWVsc2UNCisJCQkJdmFsIHw9IChzdnNiLT5hZ2VfY29uZmlnICYgZmlsdGVyKTsN CisJCX0NCisJCXN2c193cml0ZWwoc3ZzLCB2YWwsIFJVTkNPTkZJRyk7DQorCX0NCisNCisJc3Zz Yi0+b3BzLT5zZXRfZnJlcXNfcGN0KHN2cyk7DQorDQorCWxpbWl0X3ZhbHMgPSAoKHN2c2ItPnZt YXggPDwgMjQpICYgR0VOTUFTSygzMSwgMjQpKSB8DQorCQkgICAgICgoc3ZzYi0+dm1pbiA8PCAx NikgJiBHRU5NQVNLKDIzLCAxNikpIHwNCisJCSAgICAgKChzdnNiLT5kdGhpIDw8IDgpICYgR0VO TUFTSygxNSwgOCkpIHwNCisJCSAgICAgKHN2c2ItPmR0bG8gJiBHRU5NQVNLKDcsIDApKTsNCisJ c3ZzX3dyaXRlbChzdnMsIGxpbWl0X3ZhbHMsIExJTUlUVkFMUyk7DQorCXN2c193cml0ZWwoc3Zz LCAoc3ZzYi0+dmJvb3QgJiBHRU5NQVNLKDcsIDApKSwgVkJPT1QpOw0KKwlzdnNfd3JpdGVsKHN2 cywgKHN2c2ItPmRldF93aW5kb3cgJiBHRU5NQVNLKDE1LCAwKSksIERFVFdJTkRPVyk7DQorCXN2 c193cml0ZWwoc3ZzLCAoc3ZzYi0+ZGV0X21heCAmIEdFTk1BU0soMTUsIDApKSwgQ09ORklHKTsN CisNCisJaWYgKHN2c2ItPmNoa3NoaWZ0ICE9IDApDQorCQlzdnNfd3JpdGVsKHN2cywgKHN2c2It PmNoa3NoaWZ0ICYgR0VOTUFTSyg3LCAwKSksIENIS1NISUZUKTsNCisNCisJaWYgKHN2c2ItPmN0 bDAgIT0gMCkNCisJCXN2c193cml0ZWwoc3ZzLCBzdnNiLT5jdGwwLCBDVEwwKTsNCisNCisJc3Zz X3dyaXRlbChzdnMsIElOVFNUU19DTEVBTiwgSU5UU1RTKTsNCisNCisJc3dpdGNoICh0YXJnZXRf cGhhc2UpIHsNCisJY2FzZSBTVlNCX1BIQVNFX0lOSVQwMToNCisJCXN2c193cml0ZWwoc3ZzLCBJ TlRFTl9JTklUMHgsIElOVEVOKTsNCisJCXN2c193cml0ZWwoc3ZzLCBTVlNFTl9JTklUMDEsIFNW U0VOKTsNCisJCWJyZWFrOw0KKwljYXNlIFNWU0JfUEhBU0VfSU5JVDAyOg0KKwkJc3ZzX3dyaXRl bChzdnMsIElOVEVOX0lOSVQweCwgSU5URU4pOw0KKwkJaW5pdDJ2YWxzID0gKChzdnNiLT5hZ2Vf dm9mZnNldF9pbiA8PCAxNikgJiBHRU5NQVNLKDMxLCAxNikpIHwNCisJCQkgICAgKHN2c2ItPmRj X3ZvZmZzZXRfaW4gJiBHRU5NQVNLKDE1LCAwKSk7DQorCQlzdnNfd3JpdGVsKHN2cywgaW5pdDJ2 YWxzLCBJTklUMlZBTFMpOw0KKwkJc3ZzX3dyaXRlbChzdnMsIFNWU0VOX0lOSVQwMiwgU1ZTRU4p Ow0KKwkJYnJlYWs7DQorCWNhc2UgU1ZTQl9QSEFTRV9NT046DQorCQl0c19jYWxjcyA9ICgoc3Zz Yi0+YnRzIDw8IDEyKSAmIEdFTk1BU0soMjMsIDEyKSkgfA0KKwkJCSAgIChzdnNiLT5tdHMgJiBH RU5NQVNLKDExLCAwKSk7DQorCQlzdnNfd3JpdGVsKHN2cywgdHNfY2FsY3MsIFRTQ0FMQ1MpOw0K KwkJc3ZzX3dyaXRlbChzdnMsIElOVEVOX01PTlZPUEVOLCBJTlRFTik7DQorCQlzdnNfd3JpdGVs KHN2cywgU1ZTRU5fTU9OLCBTVlNFTik7DQorCQlicmVhazsNCisJZGVmYXVsdDoNCisJCVdBUk5f T04oMSk7DQorCQlicmVhazsNCisJfQ0KK30NCisNCitzdGF0aWMgaW5saW5lIHZvaWQgc3ZzX2lu aXQwMV9pc3JfaGFuZGxlcihzdHJ1Y3QgbXRrX3N2cyAqc3ZzKQ0KK3sNCisJc3RydWN0IHN2c19i YW5rICpzdnNiID0gc3ZzLT5iYW5rOw0KKwllbnVtIHJlZ19pbmRleCByZ19pOw0KKw0KKwlwcl9u b3RpY2UoIiVzOiAlczogVkRONzQ6MHglMDh4LCBWRE4zMDoweCUwOHgsIERDVkFMVUVTOjB4JTA4 eFxuIiwNCisJCSAgc3ZzYi0+bmFtZSwgX19mdW5jX18sIHN2c19yZWFkbChzdnMsIFZERVNJR043 NCksDQorCQkgIHN2c19yZWFkbChzdnMsIFZERVNJR04zMCksIHN2c19yZWFkbChzdnMsIERDVkFM VUVTKSk7DQorDQorCWZvciAocmdfaSA9IFRFTVBNT05DVEwwOyByZ19pIDwgcmVnX251bTsgcmdf aSsrKQ0KKwkJc3ZzYi0+cmVnX2RhdGFbU1ZTQl9QSEFTRV9JTklUMDFdW3JnX2ldID0gc3ZzX3Jl YWRsKHN2cywgcmdfaSk7DQorDQorCXN2c2ItPmRjX3ZvZmZzZXRfaW4gPSB+KHN2c19yZWFkbChz dnMsIERDVkFMVUVTKSAmIEdFTk1BU0soMTUsIDApKSArIDE7DQorCWlmIChzdnNiLT5pbml0MDFf dm9sdF9mbGFnID09IFNWU0JfSU5JVDAxX1ZPTFRfSUdOT1JFKQ0KKwkJc3ZzYi0+ZGNfdm9mZnNl dF9pbiA9IDA7DQorCWVsc2UgaWYgKChzdnNiLT5kY192b2Zmc2V0X2luICYgRENfU0lHTkVEX0JJ VCkgJiYNCisJCSAoc3ZzYi0+aW5pdDAxX3ZvbHRfZmxhZyA9PSBTVlNCX0lOSVQwMV9WT0xUX0lO Q19PTkxZKSkNCisJCXN2c2ItPmRjX3ZvZmZzZXRfaW4gPSAwOw0KKw0KKwlzdnNiLT5hZ2Vfdm9m ZnNldF9pbiA9IHN2c19yZWFkbChzdnMsIEFHRVZBTFVFUykgJiBHRU5NQVNLKDE1LCAwKTsNCisN CisJc3ZzX3dyaXRlbChzdnMsIFNWU0VOX09GRiwgU1ZTRU4pOw0KKwlzdnNfd3JpdGVsKHN2cywg SU5UU1RTX0NPTVBMRVRFLCBJTlRTVFMpOw0KKw0KKwkvKiBzdnMgaW5pdDAxIGNsb2NrIGdhdGlu ZyAqLw0KKwlzdnNiLT5jb3Jlc2VsICY9IH5zdnNiLT5zeXN0ZW1jbGtfZW47DQorDQorCXN2c2It PnBoYXNlID0gU1ZTQl9QSEFTRV9JTklUMDE7DQorCWNvbXBsZXRlKCZzdnNiLT5pbml0X2NvbXBs ZXRpb24pOw0KK30NCisNCitzdGF0aWMgaW5saW5lIHZvaWQgc3ZzX2luaXQwMl9pc3JfaGFuZGxl cihzdHJ1Y3QgbXRrX3N2cyAqc3ZzKQ0KK3sNCisJc3RydWN0IHN2c19iYW5rICpzdnNiID0gc3Zz LT5iYW5rOw0KKwllbnVtIHJlZ19pbmRleCByZ19pOw0KKw0KKwlwcl9ub3RpY2UoIiVzOiAlczog Vk9QNzQ6MHglMDh4LCBWT1AzMDoweCUwOHgsIERDVkFMVUVTOjB4JTA4eFxuIiwNCisJCSAgc3Zz Yi0+bmFtZSwgX19mdW5jX18sIHN2c19yZWFkbChzdnMsIFZPUDc0KSwNCisJCSAgc3ZzX3JlYWRs KHN2cywgVk9QMzApLCBzdnNfcmVhZGwoc3ZzLCBEQ1ZBTFVFUykpOw0KKw0KKwlmb3IgKHJnX2kg PSBURU1QTU9OQ1RMMDsgcmdfaSA8IHJlZ19udW07IHJnX2krKykNCisJCXN2c2ItPnJlZ19kYXRh W1NWU0JfUEhBU0VfSU5JVDAyXVtyZ19pXSA9IHN2c19yZWFkbChzdnMsIHJnX2kpOw0KKw0KKwlz dnNiLT5vcHMtPmdldF92b3BzKHN2cyk7DQorCW1lbWNweShzdnNiLT5pbml0MDJfdm9sdHMsIHN2 c2ItPnZvbHRzLCBzaXplb2YodTMyKSAqIHN2c2ItPm9wcF9jb3VudCk7DQorCXN2c2ItPnBoYXNl ID0gU1ZTQl9QSEFTRV9JTklUMDI7DQorDQorCXN2c193cml0ZWwoc3ZzLCBTVlNFTl9PRkYsIFNW U0VOKTsNCisJc3ZzX3dyaXRlbChzdnMsIElOVFNUU19DT01QTEVURSwgSU5UU1RTKTsNCisNCisJ Y29tcGxldGUoJnN2c2ItPmluaXRfY29tcGxldGlvbik7DQorfQ0KKw0KK3N0YXRpYyBpbmxpbmUg dm9pZCBzdnNfbW9uX21vZGVfaXNyX2hhbmRsZXIoc3RydWN0IG10a19zdnMgKnN2cykNCit7DQor CXN0cnVjdCBzdnNfYmFuayAqc3ZzYiA9IHN2cy0+YmFuazsNCisJZW51bSByZWdfaW5kZXggcmdf aTsNCisNCisJZm9yIChyZ19pID0gVEVNUE1PTkNUTDA7IHJnX2kgPCByZWdfbnVtOyByZ19pKysp DQorCQlzdnNiLT5yZWdfZGF0YVtTVlNCX1BIQVNFX01PTl1bcmdfaV0gPSBzdnNfcmVhZGwoc3Zz LCByZ19pKTsNCisNCisJc3ZzYi0+c3ZzX3RlbXAgPSBzdnNfcmVhZGwoc3ZzLCBURU1QKSAmIEdF Tk1BU0soNywgMCk7DQorDQorCXN2c2ItPm9wcy0+Z2V0X3ZvcHMoc3ZzKTsNCisJc3ZzYi0+cGhh c2UgPSBTVlNCX1BIQVNFX01PTjsNCisNCisJc3ZzX3dyaXRlbChzdnMsIElOVFNUU19NT05WT1As IElOVFNUUyk7DQorfQ0KKw0KK3N0YXRpYyBpbmxpbmUgdm9pZCBzdnNfZXJyb3JfaXNyX2hhbmRs ZXIoc3RydWN0IG10a19zdnMgKnN2cykNCit7DQorCWNvbnN0IHN0cnVjdCBzdnNfcGxhdGZvcm0g KnN2c3AgPSBzdnMtPnBsYXRmb3JtOw0KKwlzdHJ1Y3Qgc3ZzX2JhbmsgKnN2c2IgPSBzdnMtPmJh bms7DQorCWVudW0gcmVnX2luZGV4IHJnX2k7DQorDQorCXByX2VycigiJXMoKTogJXMoJXMpIiwg X19mdW5jX18sIHN2c3AtPm5hbWUsIHN2c2ItPm5hbWUpOw0KKwlwcl9lcnIoIkNPUkVTRUwoMHgl eCkgPSAweCUwOHhcbiIsDQorCSAgICAgICBzdnNwLT5yZWdzW0NPUkVTRUxdLCBzdnNfcmVhZGwo c3ZzLCBDT1JFU0VMKSksDQorCXByX2VycigiU1ZTRU4oMHgleCkgPSAweCUwOHgsIElOVFNUUygw eCV4KSA9IDB4JTA4eFxuIiwNCisJICAgICAgIHN2c3AtPnJlZ3NbU1ZTRU5dLCBzdnNfcmVhZGwo c3ZzLCBTVlNFTiksDQorCSAgICAgICBzdnNwLT5yZWdzW0lOVFNUU10sIHN2c19yZWFkbChzdnMs IElOVFNUUykpOw0KKwlwcl9lcnIoIlNNU1RBVEUwKDB4JXgpID0gMHglMDh4LCBTTVNUQVRFMSgw eCV4KSA9IDB4JTA4eFxuIiwNCisJICAgICAgIHN2c3AtPnJlZ3NbU01TVEFURTBdLCBzdnNfcmVh ZGwoc3ZzLCBTTVNUQVRFMCksDQorCSAgICAgICBzdnNwLT5yZWdzW1NNU1RBVEUxXSwgc3ZzX3Jl YWRsKHN2cywgU01TVEFURTEpKTsNCisNCisJZm9yIChyZ19pID0gVEVNUE1PTkNUTDA7IHJnX2kg PCByZWdfbnVtOyByZ19pKyspDQorCQlzdnNiLT5yZWdfZGF0YVtTVlNCX1BIQVNFX01PTl1bcmdf aV0gPSBzdnNfcmVhZGwoc3ZzLCByZ19pKTsNCisNCisJc3ZzYi0+bW9kZV9zdXBwb3J0ID0gU1ZT Ql9NT0RFX0FMTF9ESVNBQkxFOw0KKw0KKwlpZiAoc3ZzYi0+cGhhc2UgPT0gU1ZTQl9QSEFTRV9N T04pDQorCQlzdnNiLT5waGFzZSA9IFNWU0JfUEhBU0VfSU5JVDAyOw0KKw0KKwlzdnNfd3JpdGVs KHN2cywgU1ZTRU5fT0ZGLCBTVlNFTik7DQorCXN2c193cml0ZWwoc3ZzLCBJTlRTVFNfQ0xFQU4s IElOVFNUUyk7DQorfQ0KKw0KK3N0YXRpYyBpbmxpbmUgdm9pZCBzdnNfaXNyX2hhbmRsZXIoc3Ry dWN0IG10a19zdnMgKnN2cykNCit7DQorCXUzMiBpbnRzdHMsIHN2c2VuOw0KKw0KKwlzdnNfc3dp dGNoX2Jhbmsoc3ZzKTsNCisNCisJaW50c3RzID0gc3ZzX3JlYWRsKHN2cywgSU5UU1RTKTsNCisJ c3ZzZW4gPSBzdnNfcmVhZGwoc3ZzLCBTVlNFTik7DQorDQorCWlmIChpbnRzdHMgPT0gSU5UU1RT X0NPTVBMRVRFICYmDQorCSAgICAoKHN2c2VuICYgU1ZTRU5fTUFTSykgPT0gU1ZTRU5fSU5JVDAx KSkNCisJCXN2c19pbml0MDFfaXNyX2hhbmRsZXIoc3ZzKTsNCisJZWxzZSBpZiAoKGludHN0cyA9 PSBJTlRTVFNfQ09NUExFVEUpICYmDQorCQkgKChzdnNlbiAmIFNWU0VOX01BU0spID09IFNWU0VO X0lOSVQwMikpDQorCQlzdnNfaW5pdDAyX2lzcl9oYW5kbGVyKHN2cyk7DQorCWVsc2UgaWYgKCEh KGludHN0cyAmIElOVFNUU19NT05WT1ApKQ0KKwkJc3ZzX21vbl9tb2RlX2lzcl9oYW5kbGVyKHN2 cyk7DQorCWVsc2UNCisJCXN2c19lcnJvcl9pc3JfaGFuZGxlcihzdnMpOw0KK30NCisNCitzdGF0 aWMgaXJxcmV0dXJuX3Qgc3ZzX2lzcihpbnQgaXJxLCB2b2lkICpkYXRhKQ0KK3sNCisJc3RydWN0 IG10a19zdnMgKnN2cyA9IChzdHJ1Y3QgbXRrX3N2cyAqKWRhdGE7DQorCWNvbnN0IHN0cnVjdCBz dnNfcGxhdGZvcm0gKnN2c3AgPSBzdnMtPnBsYXRmb3JtOw0KKwlzdHJ1Y3Qgc3ZzX2JhbmsgKnN2 c2IgPSBOVUxMOw0KKwl1bnNpZ25lZCBsb25nIGZsYWdzOw0KKwl1MzIgaWR4Ow0KKw0KKwlmbGFn cyA9IGNsYWltX210a19zdnNfbG9jaygpOw0KKwlmb3IgKGlkeCA9IDA7IGlkeCA8IHN2c3AtPmJh bmtfbnVtOyBpZHgrKykgew0KKwkJc3ZzYiA9ICZzdnNwLT5iYW5rc1tpZHhdOw0KKwkJc3ZzLT5i YW5rID0gc3ZzYjsNCisNCisJCWlmIChzdnNiLT5zdXNwZW5kZWQpDQorCQkJY29udGludWU7DQor CQllbHNlIGlmIChzdnNiLT5pbnRzdCAmIHN2c19yZWFkbChzdnMsIElOVFNUKSkNCisJCQljb250 aW51ZTsNCisNCisJCXN2c19pc3JfaGFuZGxlcihzdnMpOw0KKwkJYnJlYWs7DQorCX0NCisJcmVs ZWFzZV9tdGtfc3ZzX2xvY2soZmxhZ3MpOw0KKw0KKwlpZiAoc3ZzYi0+cGhhc2UgIT0gU1ZTQl9Q SEFTRV9JTklUMDEpDQorCQlzdnNiX3NldF92b2x0cyhzdnNiLCBmYWxzZSk7DQorDQorCXJldHVy biBJUlFfSEFORExFRDsNCit9DQorDQorc3RhdGljIHZvaWQgc3ZzX21vbl9tb2RlKHN0cnVjdCBt dGtfc3ZzICpzdnMpDQorew0KKwljb25zdCBzdHJ1Y3Qgc3ZzX3BsYXRmb3JtICpzdnNwID0gc3Zz LT5wbGF0Zm9ybTsNCisJc3RydWN0IHN2c19iYW5rICpzdnNiOw0KKwl1bnNpZ25lZCBsb25nIGZs YWdzOw0KKwl1MzIgaWR4Ow0KKw0KKwlmbGFncyA9IGNsYWltX210a19zdnNfbG9jaygpOw0KKwlm b3IgKGlkeCA9IDA7IGlkeCA8IHN2c3AtPmJhbmtfbnVtOyBpZHgrKykgew0KKwkJc3ZzYiA9ICZz dnNwLT5iYW5rc1tpZHhdOw0KKwkJc3ZzLT5iYW5rID0gc3ZzYjsNCisNCisJCWlmICghKHN2c2It Pm1vZGVfc3VwcG9ydCAmIFNWU0JfTU9ERV9NT04pKQ0KKwkJCWNvbnRpbnVlOw0KKw0KKwkJc3Zz X3NldF9waGFzZShzdnMsIFNWU0JfUEhBU0VfTU9OKTsNCisJfQ0KKwlyZWxlYXNlX210a19zdnNf bG9jayhmbGFncyk7DQorfQ0KKw0KK3N0YXRpYyBpbnQgc3ZzX2luaXQwMihzdHJ1Y3QgbXRrX3N2 cyAqc3ZzKQ0KK3sNCisJY29uc3Qgc3RydWN0IHN2c19wbGF0Zm9ybSAqc3ZzcCA9IHN2cy0+cGxh dGZvcm07DQorCXN0cnVjdCBzdnNfYmFuayAqc3ZzYjsNCisJdW5zaWduZWQgbG9uZyBmbGFncywg dGltZV9sZWZ0Ow0KKwl1MzIgaWR4Ow0KKw0KKwlmb3IgKGlkeCA9IDA7IGlkeCA8IHN2c3AtPmJh bmtfbnVtOyBpZHgrKykgew0KKwkJc3ZzYiA9ICZzdnNwLT5iYW5rc1tpZHhdOw0KKwkJc3ZzLT5i YW5rID0gc3ZzYjsNCisNCisJCWlmICghKHN2c2ItPm1vZGVfc3VwcG9ydCAmIFNWU0JfTU9ERV9J TklUMDIpKQ0KKwkJCWNvbnRpbnVlOw0KKw0KKwkJcmVpbml0X2NvbXBsZXRpb24oJnN2c2ItPmlu aXRfY29tcGxldGlvbik7DQorCQlmbGFncyA9IGNsYWltX210a19zdnNfbG9jaygpOw0KKwkJc3Zz X3NldF9waGFzZShzdnMsIFNWU0JfUEhBU0VfSU5JVDAyKTsNCisJCXJlbGVhc2VfbXRrX3N2c19s b2NrKGZsYWdzKTsNCisJCXRpbWVfbGVmdCA9DQorCQkJd2FpdF9mb3JfY29tcGxldGlvbl90aW1l b3V0KCZzdnNiLT5pbml0X2NvbXBsZXRpb24sDQorCQkJCQkJICAgIG1zZWNzX3RvX2ppZmZpZXMo MjAwMCkpOw0KKwkJaWYgKHRpbWVfbGVmdCA9PSAwKSB7DQorCQkJcHJfZXJyKCIlczogaW5pdDAy IGNvbXBsZXRpb24gdGltZW91dFxuIiwgc3ZzYi0+bmFtZSk7DQorCQkJcmV0dXJuIC1FQlVTWTsN CisJCX0NCisJfQ0KKw0KKwlyZXR1cm4gMDsNCit9DQorDQorc3RhdGljIGludCBzdnNfaW5pdDAx KHN0cnVjdCBtdGtfc3ZzICpzdnMpDQorew0KKwljb25zdCBzdHJ1Y3Qgc3ZzX3BsYXRmb3JtICpz dnNwID0gc3ZzLT5wbGF0Zm9ybTsNCisJc3RydWN0IHN2c19iYW5rICpzdnNiOw0KKwlzdHJ1Y3Qg cG1fcW9zX3JlcXVlc3QgKnFvc19yZXF1ZXN0Ow0KKwl1bnNpZ25lZCBsb25nIGZsYWdzLCB0aW1l X2xlZnQ7DQorCWJvb2wgc2VhcmNoX2RvbmU7DQorCWludCByZXQgPSAtRUlOVkFMOw0KKwl1MzIg b3BwX2ZyZXFzLCBvcHBfdmJvb3QsIGJ1Y2tfdm9sdCwgaWR4LCBpOw0KKw0KKwlxb3NfcmVxdWVz dCA9IGt6YWxsb2Moc2l6ZW9mKCpxb3NfcmVxdWVzdCksIEdGUF9LRVJORUwpOw0KKwlpZiAoIXFv c19yZXF1ZXN0KQ0KKwkJcmV0dXJuIC1FTk9NRU07DQorDQorCS8qIExldCBDUFVzIGxlYXZlIGlk bGUtb2ZmIHN0YXRlIGZvciBpbml0aWFsaXppbmcgc3ZzX2luaXQwMS4gKi8NCisJcG1fcW9zX2Fk ZF9yZXF1ZXN0KHFvc19yZXF1ZXN0LCBQTV9RT1NfQ1BVX0RNQV9MQVRFTkNZLCAwKTsNCisNCisJ LyoNCisJICogU29tZXRpbWVzIHR3byBzdnMgYmFua3MgdXNlIHRoZSBzYW1lIGJ1Y2suDQorCSAq IFRoZXJlZm9yZSwgd2Ugc2V0IGVhY2ggc3ZzIGJhbmsgdG8gdmJvb3Qgdm9sdGFnZSBmaXJzdC4N CisJICovDQorCWZvciAoaWR4ID0gMDsgaWR4IDwgc3ZzcC0+YmFua19udW07IGlkeCsrKSB7DQor CQlzdnNiID0gJnN2c3AtPmJhbmtzW2lkeF07DQorCQlzZWFyY2hfZG9uZSA9IGZhbHNlOw0KKw0K KwkJaWYgKCEoc3ZzYi0+bW9kZV9zdXBwb3J0ICYgU1ZTQl9NT0RFX0lOSVQwMSkpDQorCQkJY29u dGludWU7DQorDQorCQlyZXQgPSByZWd1bGF0b3Jfc2V0X21vZGUoc3ZzYi0+YnVjaywgUkVHVUxB VE9SX01PREVfRkFTVCk7DQorCQlpZiAocmV0KQ0KKwkJCXByX25vdGljZSgiJXM6IGZhaWwgdG8g c2V0IGZhc3QgbW9kZTogJWRcbiIsDQorCQkJCSAgc3ZzYi0+bmFtZSwgcmV0KTsNCisNCisJCWlm IChzdnNiLT5tdGNtb3NfcmVxdWVzdCkgew0KKwkJCXJldCA9IHJlZ3VsYXRvcl9lbmFibGUoc3Zz Yi0+YnVjayk7DQorCQkJaWYgKHJldCkgew0KKwkJCQlwcl9lcnIoIiVzOiBmYWlsIHRvIGVuYWJs ZSAlcyBwb3dlcjogJWRcbiIsDQorCQkJCSAgICAgICBzdnNiLT5uYW1lLCBzdnNiLT5idWNrX25h bWUsIHJldCk7DQorCQkJCWdvdG8gaW5pdDAxX2ZpbmlzaDsNCisJCQl9DQorDQorCQkJcmV0ID0g ZGV2X3BtX2RvbWFpbl9hdHRhY2goc3ZzYi0+ZGV2LCBmYWxzZSk7DQorCQkJaWYgKHJldCkgew0K KwkJCQlwcl9lcnIoIiVzOiBhdHRhY2ggcG0gZG9tYWluIGZhaWw6ICVkXG4iLA0KKwkJCQkgICAg ICAgc3ZzYi0+bmFtZSwgcmV0KTsNCisJCQkJZ290byBpbml0MDFfZmluaXNoOw0KKwkJCX0NCisN CisJCQlwbV9ydW50aW1lX2VuYWJsZShzdnNiLT5kZXYpOw0KKwkJCXJldCA9IHBtX3J1bnRpbWVf Z2V0X3N5bmMoc3ZzYi0+ZGV2KTsNCisJCQlpZiAocmV0IDwgMCkgew0KKwkJCQlwcl9lcnIoIiVz OiB0dXJuIG10Y21vcyBvbiBmYWlsOiAlZFxuIiwNCisJCQkJICAgICAgIHN2c2ItPm5hbWUsIHJl dCk7DQorCQkJCWdvdG8gaW5pdDAxX2ZpbmlzaDsNCisJCQl9DQorCQl9DQorDQorCQkvKg0KKwkJ ICogRmluZCB0aGUgZmFzdGVzdCBmcmVxIHRoYXQgY2FuIGJlIHJ1biBhdCB2Ym9vdCBhbmQNCisJ CSAqIGZpeCB0byB0aGF0IGZyZXEgdW50aWwgc3ZzX2luaXQwMSBpcyBkb25lLg0KKwkJICovDQor CQlvcHBfdmJvb3QgPSBzdnNiX3ZvbHRfdG9fb3BwX3ZvbHQoc3ZzYi0+dmJvb3QsDQorCQkJCQkJ ICBzdnNiLT52b2x0X3N0ZXAsDQorCQkJCQkJICBzdnNiLT52b2x0X2Jhc2UpOw0KKw0KKwkJZm9y IChpID0gMDsgaSA8IHN2c2ItPm9wcF9jb3VudDsgaSsrKSB7DQorCQkJb3BwX2ZyZXFzID0gc3Zz Yi0+b3BwX2ZyZXFzW2ldOw0KKwkJCWlmICghc2VhcmNoX2RvbmUgJiYgc3ZzYi0+b3BwX3ZvbHRz W2ldIDw9IG9wcF92Ym9vdCkgew0KKwkJCQlyZXQgPSBkZXZfcG1fb3BwX2FkanVzdF92b2x0YWdl KHN2c2ItPmRldiwNCisJCQkJCQkJCW9wcF9mcmVxcywNCisJCQkJCQkJCW9wcF92Ym9vdCwNCisJ CQkJCQkJCW9wcF92Ym9vdCwNCisJCQkJCQkJCW9wcF92Ym9vdCk7DQorCQkJCWlmIChyZXQpIHsN CisJCQkJCXByX2VycigiJXM6IHNldCB2b2x0YWdlIGZhaWxlZDogJWRcbiIsDQorCQkJCQkgICAg ICAgc3ZzYi0+bmFtZSwgcmV0KTsNCisJCQkJCWdvdG8gaW5pdDAxX2ZpbmlzaDsNCisJCQkJfQ0K Kw0KKwkJCQlzZWFyY2hfZG9uZSA9IHRydWU7DQorCQkJfSBlbHNlIHsNCisJCQkJZGV2X3BtX29w cF9kaXNhYmxlKHN2c2ItPmRldiwNCisJCQkJCQkgICBzdnNiLT5vcHBfZnJlcXNbaV0pOw0KKwkJ CX0NCisJCX0NCisJfQ0KKw0KKwkvKiBzdnMgYmFuayBpbml0MDEgYmVnaW5zICovDQorCWZvciAo aWR4ID0gMDsgaWR4IDwgc3ZzcC0+YmFua19udW07IGlkeCsrKSB7DQorCQlzdnNiID0gJnN2c3At PmJhbmtzW2lkeF07DQorCQlzdnMtPmJhbmsgPSBzdnNiOw0KKw0KKwkJaWYgKCEoc3ZzYi0+bW9k ZV9zdXBwb3J0ICYgU1ZTQl9NT0RFX0lOSVQwMSkpDQorCQkJY29udGludWU7DQorDQorCQlvcHBf dmJvb3QgPSBzdnNiX3ZvbHRfdG9fb3BwX3ZvbHQoc3ZzYi0+dmJvb3QsDQorCQkJCQkJICBzdnNi LT52b2x0X3N0ZXAsDQorCQkJCQkJICBzdnNiLT52b2x0X2Jhc2UpOw0KKw0KKwkJYnVja192b2x0 ID0gcmVndWxhdG9yX2dldF92b2x0YWdlKHN2c2ItPmJ1Y2spOw0KKwkJaWYgKGJ1Y2tfdm9sdCAh PSBvcHBfdmJvb3QpIHsNCisJCQlwcl9lcnIoIiVzOiBidWNrIHZvbHRhZ2U6ICV1LCBleHBlY3Rl ZCB2Ym9vdDogJXVcbiIsDQorCQkJICAgICAgIHN2c2ItPm5hbWUsIGJ1Y2tfdm9sdCwgb3BwX3Zi b290KTsNCisJCQlyZXQgPSAtRVBFUk07DQorCQkJZ290byBpbml0MDFfZmluaXNoOw0KKwkJfQ0K Kw0KKwkJaW5pdF9jb21wbGV0aW9uKCZzdnNiLT5pbml0X2NvbXBsZXRpb24pOw0KKwkJZmxhZ3Mg PSBjbGFpbV9tdGtfc3ZzX2xvY2soKTsNCisJCXN2c19zZXRfcGhhc2Uoc3ZzLCBTVlNCX1BIQVNF X0lOSVQwMSk7DQorCQlyZWxlYXNlX210a19zdnNfbG9jayhmbGFncyk7DQorCQl0aW1lX2xlZnQg PQ0KKwkJCXdhaXRfZm9yX2NvbXBsZXRpb25fdGltZW91dCgmc3ZzYi0+aW5pdF9jb21wbGV0aW9u LA0KKwkJCQkJCSAgICBtc2Vjc190b19qaWZmaWVzKDIwMDApKTsNCisJCWlmICh0aW1lX2xlZnQg PT0gMCkgew0KKwkJCXByX2VycigiJXM6IGluaXQwMSBjb21wbGV0aW9uIHRpbWVvdXRcbiIsIHN2 c2ItPm5hbWUpOw0KKwkJCXJldCA9IC1FQlVTWTsNCisJCQlnb3RvIGluaXQwMV9maW5pc2g7DQor CQl9DQorCX0NCisNCitpbml0MDFfZmluaXNoOg0KKwlmb3IgKGlkeCA9IDA7IGlkeCA8IHN2c3At PmJhbmtfbnVtOyBpZHgrKykgew0KKwkJc3ZzYiA9ICZzdnNwLT5iYW5rc1tpZHhdOw0KKw0KKwkJ aWYgKCEoc3ZzYi0+bW9kZV9zdXBwb3J0ICYgU1ZTQl9NT0RFX0lOSVQwMSkpDQorCQkJY29udGlu dWU7DQorDQorCQlmb3IgKGkgPSAwOyBpIDwgc3ZzYi0+b3BwX2NvdW50OyBpKyspDQorCQkJZGV2 X3BtX29wcF9lbmFibGUoc3ZzYi0+ZGV2LCBzdnNiLT5vcHBfZnJlcXNbaV0pOw0KKw0KKwkJaWYg KHJlZ3VsYXRvcl9zZXRfbW9kZShzdnNiLT5idWNrLCBSRUdVTEFUT1JfTU9ERV9OT1JNQUwpKQ0K KwkJCXByX25vdGljZSgiJXM6IGZhaWwgdG8gc2V0IG5vcm1hbCBtb2RlOiAlZFxuIiwNCisJCQkJ ICBzdnNiLT5uYW1lLCByZXQpOw0KKw0KKwkJaWYgKHN2c2ItPm10Y21vc19yZXF1ZXN0KSB7DQor CQkJaWYgKHBtX3J1bnRpbWVfcHV0X3N5bmMoc3ZzYi0+ZGV2KSkNCisJCQkJcHJfZXJyKCIlczog dHVybiBtdGNtb3Mgb2ZmIGZhaWw6ICVkXG4iLA0KKwkJCQkgICAgICAgc3ZzYi0+bmFtZSwgcmV0 KTsNCisJCQlwbV9ydW50aW1lX2Rpc2FibGUoc3ZzYi0+ZGV2KTsNCisJCQlkZXZfcG1fZG9tYWlu X2RldGFjaChzdnNiLT5kZXYsIDApOw0KKwkJCWlmIChyZWd1bGF0b3JfZGlzYWJsZShzdnNiLT5i dWNrKSkNCisJCQkJcHJfZXJyKCIlczogZmFpbCB0byBkaXNhYmxlICVzIHBvd2VyOiAlZFxuIiwN CisJCQkJICAgICAgIHN2c2ItPm5hbWUsIHN2c2ItPmJ1Y2tfbmFtZSwgcmV0KTsNCisJCX0NCisJ fQ0KKw0KKwlwbV9xb3NfcmVtb3ZlX3JlcXVlc3QocW9zX3JlcXVlc3QpOw0KKwlrZnJlZShxb3Nf cmVxdWVzdCk7DQorDQorCXJldHVybiByZXQ7DQorfQ0KKw0KK3N0YXRpYyBpbnQgc3ZzX3N0YXJ0 KHN0cnVjdCBtdGtfc3ZzICpzdnMpDQorew0KKwlpbnQgcmV0Ow0KKw0KKwlyZXQgPSBzdnNfaW5p dDAxKHN2cyk7DQorCWlmIChyZXQpDQorCQlyZXR1cm4gcmV0Ow0KKw0KKwlyZXQgPSBzdnNfaW5p dDAyKHN2cyk7DQorCWlmIChyZXQpDQorCQlyZXR1cm4gcmV0Ow0KKw0KKwlzdnNfbW9uX21vZGUo c3ZzKTsNCisNCisJcmV0dXJuIHJldDsNCit9DQorDQorc3RhdGljIGJvb2wgc3ZzX210ODE4M19l ZnVzZV9wYXJzaW5nKHN0cnVjdCBtdGtfc3ZzICpzdnMpDQorew0KKwljb25zdCBzdHJ1Y3Qgc3Zz X3BsYXRmb3JtICpzdnNwID0gc3ZzLT5wbGF0Zm9ybTsNCisJc3RydWN0IHRoZXJtYWxfcGFyYW1l dGVyIHRwOw0KKwlzdHJ1Y3Qgc3ZzX2JhbmsgKnN2c2I7DQorCWJvb2wgbW9uX21vZGVfc3VwcG9y dCA9IHRydWU7DQorCWludCBmb3JtYXRbNl0sIHhfcm9vbXRbNl0sIHRiX3Jvb210ID0gMDsNCisJ dTMyIGlkeCwgaSwgZnRfcGdtLCBtdHMsIHRlbXAwLCB0ZW1wMSwgdGVtcDI7DQorDQorCWlmIChz dnNwLT5mYWtlX2VmdXNlKSB7DQorCQlwcl9ub3RpY2UoImZha2UgZWZ1c2VcbiIpOw0KKwkJc3Zz LT5lZnVzZVswXSA9IDB4MDAzMTAwODA7DQorCQlzdnMtPmVmdXNlWzFdID0gMHhhYmZiZjc1NzsN CisJCXN2cy0+ZWZ1c2VbMl0gPSAweDQ3Yzc0N2M3Ow0KKwkJc3ZzLT5lZnVzZVszXSA9IDB4YWJm YmY3NTc7DQorCQlzdnMtPmVmdXNlWzRdID0gMHhlN2ZjYTBlYzsNCisJCXN2cy0+ZWZ1c2VbNV0g PSAweDQ3YmY0Yjg4Ow0KKwkJc3ZzLT5lZnVzZVs2XSA9IDB4YWJmYjhmYTU7DQorCQlzdnMtPmVm dXNlWzddID0gMHhhYmZiMjE3YjsNCisJCXN2cy0+ZWZ1c2VbOF0gPSAweDRiZjM0YmUxOw0KKwkJ c3ZzLT5lZnVzZVs5XSA9IDB4YWJmYjY3MGQ7DQorCQlzdnMtPmVmdXNlWzE2XSA9IDB4YWJmYmM2 NTM7DQorCQlzdnMtPmVmdXNlWzE3XSA9IDB4NDdmMzQ3ZTE7DQorCQlzdnMtPmVmdXNlWzE4XSA9 IDB4YWJmYmQ4NDg7DQorDQorCQlzdnMtPnRoZXJtYWxfZWZ1c2VbMF0gPSAweDAyODczZjY5Ow0K KwkJc3ZzLT50aGVybWFsX2VmdXNlWzFdID0gMHhhMTFkOTE0MjsNCisJCXN2cy0+dGhlcm1hbF9l ZnVzZVsyXSA9IDB4YTI1MjY5MDA7DQorCX0NCisNCisJZm9yIChpID0gMDsgaSA8IHN2c3AtPmVm dXNlX251bTsgaSsrKSB7DQorCQlpZiAoc3ZzLT5lZnVzZVtpXSkNCisJCQlwcl9ub3RpY2UoIk1f SFdfUkVTJWQ6IDB4JTA4eFxuIiwgaSwgc3ZzLT5lZnVzZVtpXSk7DQorCX0NCisNCisJLyogc3Zz IGVmdXNlIHBhcnNpbmcgKi8NCisJZnRfcGdtID0gKHN2cy0+ZWZ1c2VbMF0gPj4gNCkgJiAweGY7 DQorDQorCWZvciAoaWR4ID0gMDsgaWR4IDwgc3ZzcC0+YmFua19udW07IGlkeCsrKSB7DQorCQlz dnNiID0gJnN2c3AtPmJhbmtzW2lkeF07DQorCQlpZiAoZnRfcGdtIDw9IDEpDQorCQkJc3ZzYi0+ aW5pdDAxX3ZvbHRfZmxhZyA9IFNWU0JfSU5JVDAxX1ZPTFRfSUdOT1JFOw0KKw0KKwkJc3dpdGNo IChzdnNiLT5zd19pZCkgew0KKwkJY2FzZSBTVlNfQ1BVX0xJVFRMRToNCisJCQlzdnNiLT5iZGVz ID0gc3ZzLT5lZnVzZVsxNl0gJiBHRU5NQVNLKDcsIDApOw0KKwkJCXN2c2ItPm1kZXMgPSAoc3Zz LT5lZnVzZVsxNl0gPj4gOCkgJiBHRU5NQVNLKDcsIDApOw0KKwkJCXN2c2ItPmRjYmRldCA9IChz dnMtPmVmdXNlWzE2XSA+PiAxNikgJiBHRU5NQVNLKDcsIDApOw0KKwkJCXN2c2ItPmRjbWRldCA9 IChzdnMtPmVmdXNlWzE2XSA+PiAyNCkgJiBHRU5NQVNLKDcsIDApOw0KKwkJCXN2c2ItPm10ZGVz ICA9IChzdnMtPmVmdXNlWzE3XSA+PiAxNikgJiBHRU5NQVNLKDcsIDApOw0KKw0KKwkJCWlmIChm dF9wZ20gPD0gMykNCisJCQkJc3ZzYi0+dm9sdF9vZmZzZXQgKz0gMTA7DQorCQkJZWxzZQ0KKwkJ CQlzdnNiLT52b2x0X29mZnNldCArPSAyOw0KKwkJCWJyZWFrOw0KKwkJY2FzZSBTVlNfQ1BVX0JJ RzoNCisJCQlzdnNiLT5iZGVzID0gc3ZzLT5lZnVzZVsxOF0gJiBHRU5NQVNLKDcsIDApOw0KKwkJ CXN2c2ItPm1kZXMgPSAoc3ZzLT5lZnVzZVsxOF0gPj4gOCkgJiBHRU5NQVNLKDcsIDApOw0KKwkJ CXN2c2ItPmRjYmRldCA9IChzdnMtPmVmdXNlWzE4XSA+PiAxNikgJiBHRU5NQVNLKDcsIDApOw0K KwkJCXN2c2ItPmRjbWRldCA9IChzdnMtPmVmdXNlWzE4XSA+PiAyNCkgJiBHRU5NQVNLKDcsIDAp Ow0KKwkJCXN2c2ItPm10ZGVzICA9IHN2cy0+ZWZ1c2VbMTddICYgR0VOTUFTSyg3LCAwKTsNCisN CisJCQlpZiAoZnRfcGdtIDw9IDMpDQorCQkJCXN2c2ItPnZvbHRfb2Zmc2V0ICs9IDE1Ow0KKwkJ CWVsc2UNCisJCQkJc3ZzYi0+dm9sdF9vZmZzZXQgKz0gMTI7DQorCQkJYnJlYWs7DQorCQljYXNl IFNWU19DQ0k6DQorCQkJc3ZzYi0+YmRlcyA9IHN2cy0+ZWZ1c2VbNF0gJiBHRU5NQVNLKDcsIDAp Ow0KKwkJCXN2c2ItPm1kZXMgPSAoc3ZzLT5lZnVzZVs0XSA+PiA4KSAmIEdFTk1BU0soNywgMCk7 DQorCQkJc3ZzYi0+ZGNiZGV0ID0gKHN2cy0+ZWZ1c2VbNF0gPj4gMTYpICYgR0VOTUFTSyg3LCAw KTsNCisJCQlzdnNiLT5kY21kZXQgPSAoc3ZzLT5lZnVzZVs0XSA+PiAyNCkgJiBHRU5NQVNLKDcs IDApOw0KKwkJCXN2c2ItPm10ZGVzICA9IChzdnMtPmVmdXNlWzVdID4+IDE2KSAmIEdFTk1BU0so NywgMCk7DQorDQorCQkJaWYgKGZ0X3BnbSA8PSAzKQ0KKwkJCQlzdnNiLT52b2x0X29mZnNldCAr PSAxMDsNCisJCQllbHNlDQorCQkJCXN2c2ItPnZvbHRfb2Zmc2V0ICs9IDI7DQorCQkJYnJlYWs7 DQorCQljYXNlIFNWU19HUFU6DQorCQkJc3ZzYi0+YmRlcyA9IHN2cy0+ZWZ1c2VbNl0gJiBHRU5N QVNLKDcsIDApOw0KKwkJCXN2c2ItPm1kZXMgPSAoc3ZzLT5lZnVzZVs2XSA+PiA4KSAmIEdFTk1B U0soNywgMCk7DQorCQkJc3ZzYi0+ZGNiZGV0ID0gKHN2cy0+ZWZ1c2VbNl0gPj4gMTYpICYgR0VO TUFTSyg3LCAwKTsNCisJCQlzdnNiLT5kY21kZXQgPSAoc3ZzLT5lZnVzZVs2XSA+PiAyNCkgJiBH RU5NQVNLKDcsIDApOw0KKwkJCXN2c2ItPm10ZGVzICA9IHN2cy0+ZWZ1c2VbNV0gJiBHRU5NQVNL KDcsIDApOw0KKw0KKwkJCWlmIChmdF9wZ20gPj0gMikgew0KKwkJCQlzdnNiLT5mcmVxX2Jhc2Ug PSA4MDAwMDAwMDA7IC8qIDgwME1IeiAqLw0KKwkJCQlzdnNiLT5kdnRfZml4ZWQgPSAyOw0KKwkJ CX0NCisJCQlicmVhazsNCisJCWRlZmF1bHQ6DQorCQkJYnJlYWs7DQorCQl9DQorCX0NCisNCisJ LyogVGhlcm1hbCBlZnVzZSBwYXJzaW5nICovDQorCWlmICghc3ZzLT50aGVybWFsX2VmdXNlKQ0K KwkJcmV0dXJuIHRydWU7DQorDQorCXRwLmFkY19nZV90ID0gKHN2cy0+dGhlcm1hbF9lZnVzZVsx XSA+PiAyMikgJiBHRU5NQVNLKDksIDApOw0KKwl0cC5hZGNfb2VfdCA9IChzdnMtPnRoZXJtYWxf ZWZ1c2VbMV0gPj4gMTIpICYgR0VOTUFTSyg5LCAwKTsNCisNCisJdHAub192dHNtY3UxID0gKHN2 cy0+dGhlcm1hbF9lZnVzZVswXSA+PiAxNykgJiBHRU5NQVNLKDgsIDApOw0KKwl0cC5vX3Z0c21j dTIgPSAoc3ZzLT50aGVybWFsX2VmdXNlWzBdID4+IDgpICYgR0VOTUFTSyg4LCAwKTsNCisJdHAu b192dHNtY3UzID0gc3ZzLT50aGVybWFsX2VmdXNlWzFdICYgR0VOTUFTSyg4LCAwKTsNCisJdHAu b192dHNtY3U0ID0gKHN2cy0+dGhlcm1hbF9lZnVzZVsyXSA+PiAyMykgJiBHRU5NQVNLKDgsIDAp Ow0KKwl0cC5vX3Z0c21jdTUgPSAoc3ZzLT50aGVybWFsX2VmdXNlWzJdID4+IDUpICYgR0VOTUFT Syg4LCAwKTsNCisJdHAub192dHNhYmIgPSAoc3ZzLT50aGVybWFsX2VmdXNlWzJdID4+IDE0KSAm IEdFTk1BU0soOCwgMCk7DQorDQorCXRwLmRlZ2NfY2FsaSA9IChzdnMtPnRoZXJtYWxfZWZ1c2Vb MF0gPj4gMSkgJiBHRU5NQVNLKDUsIDApOw0KKwl0cC5hZGNfY2FsaV9lbl90ID0gc3ZzLT50aGVy bWFsX2VmdXNlWzBdICYgQklUKDApOw0KKwl0cC5vX3Nsb3BlX3NpZ24gPSAoc3ZzLT50aGVybWFs X2VmdXNlWzBdID4+IDcpICYgQklUKDApOw0KKw0KKwl0cC50c19pZCA9IChzdnMtPnRoZXJtYWxf ZWZ1c2VbMV0gPj4gOSkgJiBCSVQoMCk7DQorCXRwLm9fc2xvcGUgPSAoc3ZzLT50aGVybWFsX2Vm dXNlWzBdID4+IDI2KSAmIEdFTk1BU0soNSwgMCk7DQorDQorCWlmICh0cC5hZGNfY2FsaV9lbl90 ID09IDEpIHsNCisJCWlmICh0cC50c19pZCA9PSAwKQ0KKwkJCXRwLm9fc2xvcGUgPSAwOw0KKw0K KwkJaWYgKCh0cC5hZGNfZ2VfdCA8IDI2NSB8fCB0cC5hZGNfZ2VfdCA+IDc1OCkgfHwNCisJCSAg ICAodHAuYWRjX29lX3QgPCAyNjUgfHwgdHAuYWRjX29lX3QgPiA3NTgpIHx8DQorCQkgICAgKHRw Lm9fdnRzbWN1MSA8IC04IHx8IHRwLm9fdnRzbWN1MSA+IDQ4NCkgfHwNCisJCSAgICAodHAub192 dHNtY3UyIDwgLTggfHwgdHAub192dHNtY3UyID4gNDg0KSB8fA0KKwkJICAgICh0cC5vX3Z0c21j dTMgPCAtOCB8fCB0cC5vX3Z0c21jdTMgPiA0ODQpIHx8DQorCQkgICAgKHRwLm9fdnRzbWN1NCA8 IC04IHx8IHRwLm9fdnRzbWN1NCA+IDQ4NCkgfHwNCisJCSAgICAodHAub192dHNtY3U1IDwgLTgg fHwgdHAub192dHNtY3U1ID4gNDg0KSB8fA0KKwkJICAgICh0cC5vX3Z0c2FiYiA8IC04IHx8IHRw Lm9fdnRzYWJiID4gNDg0KSB8fA0KKwkJICAgICh0cC5kZWdjX2NhbGkgPCAxIHx8IHRwLmRlZ2Nf Y2FsaSA+IDYzKSkgew0KKwkJCXByX2VycigiYmFkIHRoZXJtYWwgZWZ1c2UgZGF0YS4gZGlzYWJs ZSBtb24gbW9kZVxuIik7DQorCQkJbW9uX21vZGVfc3VwcG9ydCA9IGZhbHNlOw0KKwkJfQ0KKwl9 IGVsc2Ugew0KKwkJcHJfZXJyKCJubyB0aGVybWFsIGVmdXNlIGRhdGEuIGRpc2FibGUgbW9uIG1v ZGVcbiIpOw0KKwkJbW9uX21vZGVfc3VwcG9ydCA9IGZhbHNlOw0KKwl9DQorDQorCWlmICghbW9u X21vZGVfc3VwcG9ydCkgew0KKwkJZm9yIChpZHggPSAwOyBpZHggPCBzdnNwLT5iYW5rX251bTsg aWR4KyspIHsNCisJCQlzdnNiID0gJnN2c3AtPmJhbmtzW2lkeF07DQorCQkJc3ZzYi0+bW9kZV9z dXBwb3J0ICY9IH5TVlNCX01PREVfTU9OOw0KKwkJfQ0KKw0KKwkJcmV0dXJuIHRydWU7DQorCX0N CisNCisJdHAuZ2UgPSAoKHRwLmFkY19nZV90IC0gNTEyKSAqIDEwMDAwKSAvIDQwOTY7DQorCXRw Lm9lID0gKHRwLmFkY19vZV90IC0gNTEyKTsNCisJdHAuZ2FpbiA9ICgxMDAwMCArIHRwLmdlKTsN CisNCisJZm9ybWF0WzBdID0gKHRwLm9fdnRzbWN1MSArIDMzNTAgLSB0cC5vZSk7DQorCWZvcm1h dFsxXSA9ICh0cC5vX3Z0c21jdTIgKyAzMzUwIC0gdHAub2UpOw0KKwlmb3JtYXRbMl0gPSAodHAu b192dHNtY3UzICsgMzM1MCAtIHRwLm9lKTsNCisJZm9ybWF0WzNdID0gKHRwLm9fdnRzbWN1NCAr IDMzNTAgLSB0cC5vZSk7DQorCWZvcm1hdFs0XSA9ICh0cC5vX3Z0c21jdTUgKyAzMzUwIC0gdHAu b2UpOw0KKwlmb3JtYXRbNV0gPSAodHAub192dHNhYmIgKyAzMzUwIC0gdHAub2UpOw0KKw0KKwlm b3IgKGkgPSAwOyBpIDwgNjsgaSsrKQ0KKwkJeF9yb29tdFtpXSA9ICgoKGZvcm1hdFtpXSAqIDEw MDAwKSAvIDQwOTYpICogMTAwMDApIC8gdHAuZ2FpbjsNCisNCisJdGVtcDAgPSAoMTAwMDAgKiAx MDAwMDAgLyB0cC5nYWluKSAqIDE1IC8gMTg7DQorDQorCWlmICh0cC5vX3Nsb3BlX3NpZ24gPT0g MCkNCisJCW10cyA9ICh0ZW1wMCAqIDEwKSAvICgxNTM0ICsgdHAub19zbG9wZSAqIDEwKTsNCisJ ZWxzZQ0KKwkJbXRzID0gKHRlbXAwICogMTApIC8gKDE1MzQgLSB0cC5vX3Nsb3BlICogMTApOw0K Kw0KKwlmb3IgKGlkeCA9IDA7IGlkeCA8IHN2c3AtPmJhbmtfbnVtOyBpZHgrKykgew0KKwkJc3Zz YiA9ICZzdnNwLT5iYW5rc1tpZHhdOw0KKwkJc3ZzYi0+bXRzID0gbXRzOw0KKw0KKwkJc3dpdGNo IChzdnNiLT5zd19pZCkgew0KKwkJY2FzZSBTVlNfQ1BVX0xJVFRMRToNCisJCQl0Yl9yb29tdCA9 IHhfcm9vbXRbM107DQorCQkJYnJlYWs7DQorCQljYXNlIFNWU19DUFVfQklHOg0KKwkJCXRiX3Jv b210ID0geF9yb29tdFs0XTsNCisJCQlicmVhazsNCisJCWNhc2UgU1ZTX0NDSToNCisJCQl0Yl9y b29tdCA9IHhfcm9vbXRbM107DQorCQkJYnJlYWs7DQorCQljYXNlIFNWU19HUFU6DQorCQkJdGJf cm9vbXQgPSB4X3Jvb210WzFdOw0KKwkJCWJyZWFrOw0KKwkJZGVmYXVsdDoNCisJCQlicmVhazsN CisJCX0NCisNCisJCXRlbXAwID0gKHRwLmRlZ2NfY2FsaSAqIDEwIC8gMik7DQorCQl0ZW1wMSA9 ICgoMTAwMDAgKiAxMDAwMDAgLyA0MDk2IC8gdHAuZ2FpbikgKg0KKwkJCSB0cC5vZSArIHRiX3Jv b210ICogMTApICogMTUgLyAxODsNCisNCisJCWlmICh0cC5vX3Nsb3BlX3NpZ24gPT0gMCkNCisJ CQl0ZW1wMiA9IHRlbXAxICogMTAwIC8gKDE1MzQgKyB0cC5vX3Nsb3BlICogMTApOw0KKwkJZWxz ZQ0KKwkJCXRlbXAyID0gdGVtcDEgKiAxMDAgLyAoMTUzNCAtIHRwLm9fc2xvcGUgKiAxMCk7DQor DQorCQlzdnNiLT5idHMgPSAodGVtcDAgKyB0ZW1wMiAtIDI1MCkgKiA0IC8gMTA7DQorCX0NCisN CisJcmV0dXJuIHRydWU7DQorfQ0KKw0KK3N0YXRpYyBib29sIHN2c19pc19zdXBwb3J0ZWQoc3Ry dWN0IG10a19zdnMgKnN2cykNCit7DQorCWNvbnN0IHN0cnVjdCBzdnNfcGxhdGZvcm0gKnN2c3Ag PSBzdnMtPnBsYXRmb3JtOw0KKwlzdHJ1Y3Qgc3ZzX2JhbmsgKnN2c2I7DQorCXN0cnVjdCBudm1l bV9jZWxsICpjZWxsOw0KKwlzaXplX3QgbGVuOw0KKwlib29sIHJldDsNCisJdTMyIGlkeDsNCisN CisJaWYgKHN2c3AtPmZha2VfZWZ1c2UpIHsNCisJCWxlbiA9IHN2c3AtPmVmdXNlX251bSAqIDQ7 DQorCQlzdnMtPmVmdXNlID0gZGV2bV9remFsbG9jKHN2cy0+ZGV2LCBsZW4sIEdGUF9LRVJORUwp Ow0KKwkJaWYgKCFzdnMtPmVmdXNlKSB7DQorCQkJcHJfZXJyKCJubyBtZW1vcnkgZm9yIGFsbG9j YXRpbmcgc3ZzX2VmdXNlXG4iKTsNCisJCQlyZXR1cm4gZmFsc2U7DQorCQl9DQorDQorCQlsZW4g PSBzdnNwLT50aGVybWFsX2VmdXNlX251bSAqIDQ7DQorCQlzdnMtPnRoZXJtYWxfZWZ1c2UgPSBk ZXZtX2t6YWxsb2Moc3ZzLT5kZXYsIGxlbiwgR0ZQX0tFUk5FTCk7DQorCQlpZiAoIXN2cy0+dGhl cm1hbF9lZnVzZSkgew0KKwkJCXByX2Vycigibm8gbWVtb3J5IGZvciBhbGxvY2F0aW5nIHN2c190 aGVybWFsX2VmdXNlXG4iKTsNCisJCQlyZXR1cm4gZmFsc2U7DQorCQl9DQorDQorCQlnb3RvIHN2 c3BfZWZ1c2VfcGFyc2luZzsNCisJfQ0KKw0KKwkvKiBHZXQgc3ZzIGVmdXNlIGJ5IG52bWVtICov DQorCWNlbGwgPSBudm1lbV9jZWxsX2dldChzdnMtPmRldiwgInN2cy1jYWxpYnJhdGlvbi1kYXRh Iik7DQorCWlmIChJU19FUlIoY2VsbCkpIHsNCisJCXByX2Vycigibm8gXCJzdnMtY2FsaWJyYXRp b24tZGF0YVwiIGZyb20gZHRzPyBkaXNhYmxlIHN2c1xuIik7DQorCQlyZXR1cm4gZmFsc2U7DQor CX0NCisNCisJc3ZzLT5lZnVzZSA9ICh1MzIgKiludm1lbV9jZWxsX3JlYWQoY2VsbCwgJmxlbik7 DQorCW52bWVtX2NlbGxfcHV0KGNlbGwpOw0KKw0KKwlpZiAoc3ZzLT5lZnVzZVtzdnNwLT5lZnVz ZV9jaGVja10gPT0gMCkgew0KKwkJcHJfZXJyKCJzdnNfZWZ1c2VbJXVdIGlzIGVtcHR5XG4iLCBz dnNwLT5lZnVzZV9jaGVjayk7DQorCQlyZXR1cm4gZmFsc2U7DQorCX0NCisNCisJLyogR2V0IHRo ZXJtYWwgZWZ1c2UgYnkgbnZtZW0gKi8NCisJY2VsbCA9IG52bWVtX2NlbGxfZ2V0KHN2cy0+ZGV2 LCAiY2FsaWJyYXRpb24tZGF0YSIpOw0KKwlpZiAoSVNfRVJSKGNlbGwpKSB7DQorCQlwcl9lcnIo Im5vIFwiY2FsaWJyYXRpb24tZGF0YVwiIGZyb20gZHRzPyBkaXNhYmxlIG1vbiBtb2RlXG4iKTsN CisJCXN2cy0+dGhlcm1hbF9lZnVzZSA9IE5VTEw7DQorCQlmb3IgKGlkeCA9IDA7IGlkeCA8IHN2 c3AtPmJhbmtfbnVtOyBpZHgrKykgew0KKwkJCXN2c2IgPSAmc3ZzcC0+YmFua3NbaWR4XTsNCisJ CQlzdnNiLT5tb2RlX3N1cHBvcnQgJj0gflNWU0JfTU9ERV9NT047DQorCQl9DQorCQlnb3RvIHN2 c3BfZWZ1c2VfcGFyc2luZzsNCisJfQ0KKw0KKwlzdnMtPnRoZXJtYWxfZWZ1c2UgPSAodTMyICop bnZtZW1fY2VsbF9yZWFkKGNlbGwsICZsZW4pOw0KKwludm1lbV9jZWxsX3B1dChjZWxsKTsNCisN CitzdnNwX2VmdXNlX3BhcnNpbmc6DQorCXJldCA9IHN2c3AtPmVmdXNlX3BhcnNpbmcoc3ZzKTsN CisNCisJcmV0dXJuIHJldDsNCit9DQorDQorc3RhdGljIGludCBzdnNfcmVzb3VyY2Vfc2V0dXAo c3RydWN0IG10a19zdnMgKnN2cykNCit7DQorCWNvbnN0IHN0cnVjdCBzdnNfcGxhdGZvcm0gKnN2 c3AgPSBzdnMtPnBsYXRmb3JtOw0KKwlzdHJ1Y3Qgc3ZzX2JhbmsgKnN2c2I7DQorCXN0cnVjdCBw bGF0Zm9ybV9kZXZpY2UgKnBkZXY7DQorCXN0cnVjdCBkZXZpY2Vfbm9kZSAqbnAgPSBOVUxMOw0K KwlzdHJ1Y3QgZGV2X3BtX29wcCAqb3BwOw0KKwl1bnNpZ25lZCBsb25nIGZyZXE7DQorCWludCBj b3VudCwgcmV0Ow0KKwl1MzIgaWR4LCBpOw0KKw0KKwlmb3IgKGlkeCA9IDA7IGlkeCA8IHN2c3At PmJhbmtfbnVtOyBpZHgrKykgew0KKwkJc3ZzYiA9ICZzdnNwLT5iYW5rc1tpZHhdOw0KKw0KKwkJ c3dpdGNoIChzdnNiLT5zd19pZCkgew0KKwkJY2FzZSBTVlNfQ1BVX0xJVFRMRToNCisJCQlzdnNi LT5uYW1lID0gIlNWU19DUFVfTElUVExFIjsNCisJCQlicmVhazsNCisJCWNhc2UgU1ZTX0NQVV9C SUc6DQorCQkJc3ZzYi0+bmFtZSA9ICJTVlNfQ1BVX0JJRyI7DQorCQkJYnJlYWs7DQorCQljYXNl IFNWU19DQ0k6DQorCQkJc3ZzYi0+bmFtZSA9ICJTVlNfQ0NJIjsNCisJCQlicmVhazsNCisJCWNh c2UgU1ZTX0dQVToNCisJCQlzdnNiLT5uYW1lID0gIlNWU19HUFUiOw0KKwkJCWJyZWFrOw0KKwkJ ZGVmYXVsdDoNCisJCQlXQVJOX09OKDEpOw0KKwkJCXJldHVybiAtRUlOVkFMOw0KKwkJfQ0KKw0K KwkJLyogQWRkIHN2cyBiYW5rIGRldmljZSBmb3Igb3BwLXRhYmxlL210Y21vcy9idWNrIGNvbnRy b2wgKi8NCisJCXBkZXYgPSBwbGF0Zm9ybV9kZXZpY2VfYWxsb2Moc3ZzYi0+bmFtZSwgMCk7DQor CQlpZiAoIXBkZXYpIHsNCisJCQlwcl9lcnIoIiVzOiBmYWlsIHRvIGFsbG9jIHBkZXYgZm9yIHN2 c19iYW5rXG4iLA0KKwkJCSAgICAgICBzdnNiLT5uYW1lKTsNCisJCQlyZXR1cm4gLUVOT01FTTsN CisJCX0NCisNCisJCWZvcl9lYWNoX2NoaWxkX29mX25vZGUoc3ZzLT5kZXYtPm9mX25vZGUsIG5w KSB7DQorCQkJaWYgKG9mX2RldmljZV9pc19jb21wYXRpYmxlKG5wLCBzdnNiLT5vZl9jb21wYXRp YmxlKSkgew0KKwkJCQlwZGV2LT5kZXYub2Zfbm9kZSA9IG5wOw0KKwkJCQlicmVhazsNCisJCQl9 DQorCQl9DQorDQorCQlyZXQgPSBwbGF0Zm9ybV9kZXZpY2VfYWRkKHBkZXYpOw0KKwkJaWYgKHJl dCkgew0KKwkJCXByX2VycigiJXM6IGZhaWwgdG8gYWRkIHN2c19iYW5rIGRldmljZTogJWRcbiIs DQorCQkJICAgICAgIHN2c2ItPm5hbWUsIHJldCk7DQorCQkJcmV0dXJuIHJldDsNCisJCX0NCisN CisJCXN2c2ItPmRldiA9ICZwZGV2LT5kZXY7DQorCQlkZXZfc2V0X2RydmRhdGEoc3ZzYi0+ZGV2 LCBzdnMpOw0KKwkJcmV0ID0gZGV2X3BtX29wcF9vZl9hZGRfdGFibGUoc3ZzYi0+ZGV2KTsNCisJ CWlmIChyZXQpIHsNCisJCQlwcl9lcnIoIiVzOiBmYWlsIHRvIGFkZCBvcHAgdGFibGU6ICVkXG4i LA0KKwkJCSAgICAgICBzdnNiLT5uYW1lLCByZXQpOw0KKwkJCXJldHVybiByZXQ7DQorCQl9DQor DQorCQltdXRleF9pbml0KCZzdnNiLT5sb2NrKTsNCisNCisJCXN2c2ItPmJ1Y2sgPSBkZXZtX3Jl Z3VsYXRvcl9nZXRfb3B0aW9uYWwoc3ZzYi0+ZGV2LA0KKwkJCQkJCQkgc3ZzYi0+YnVja19uYW1l KTsNCisJCWlmIChJU19FUlIoc3ZzYi0+YnVjaykpIHsNCisJCQlwcl9lcnIoIiVzOiBjYW5ub3Qg Z2V0IHJlZ3VsYXRvciBcIiVzLXN1cHBseVwiXG4iLA0KKwkJCSAgICAgICBzdnNiLT5uYW1lLCBz dnNiLT5idWNrX25hbWUpOw0KKwkJCXJldHVybiBQVFJfRVJSKHN2c2ItPmJ1Y2spOw0KKwkJfQ0K Kw0KKwkJY291bnQgPSBkZXZfcG1fb3BwX2dldF9vcHBfY291bnQoc3ZzYi0+ZGV2KTsNCisJCWlm IChzdnNiLT5vcHBfY291bnQgIT0gY291bnQpIHsNCisJCQlwcl9lcnIoIiVzOiBvcHBfY291bnQg bm90IFwiJXVcIiBidXQgZ2V0IFwiJWRcIj9cbiIsDQorCQkJICAgICAgIHN2c2ItPm5hbWUsIHN2 c2ItPm9wcF9jb3VudCwgY291bnQpOw0KKwkJCXJldHVybiBjb3VudDsNCisJCX0NCisNCisJCWZv ciAoaSA9IDAsIGZyZXEgPSAodTMyKS0xOyBpIDwgc3ZzYi0+b3BwX2NvdW50OyBpKyssIGZyZXEt LSkgew0KKwkJCW9wcCA9IGRldl9wbV9vcHBfZmluZF9mcmVxX2Zsb29yKHN2c2ItPmRldiwgJmZy ZXEpOw0KKwkJCWlmIChJU19FUlIob3BwKSkgew0KKwkJCQlwcl9lcnIoIiVzOiBlcnJvciBvcHAg ZW50cnkhISwgZXJyID0gJWxkXG4iLA0KKwkJCQkgICAgICAgc3ZzYi0+bmFtZSwgUFRSX0VSUihv cHApKTsNCisJCQkJcmV0dXJuIFBUUl9FUlIob3BwKTsNCisJCQl9DQorDQorCQkJc3ZzYi0+b3Bw X2ZyZXFzW2ldID0gZnJlcTsNCisJCQlzdnNiLT5vcHBfdm9sdHNbaV0gPSBkZXZfcG1fb3BwX2dl dF92b2x0YWdlKG9wcCk7DQorCQkJc3ZzYi0+ZnJlcXNfcGN0W2ldID0gcGVyY2VudChzdnNiLT5v cHBfZnJlcXNbaV0sDQorCQkJCQkJICAgICBzdnNiLT5mcmVxX2Jhc2UpOw0KKwkJCWRldl9wbV9v cHBfcHV0KG9wcCk7DQorCQl9DQorCX0NCisNCisJcmV0dXJuIDA7DQorfQ0KKw0KK3N0YXRpYyBp bnQgc3ZzX3N1c3BlbmQoc3RydWN0IGRldmljZSAqZGV2KQ0KK3sNCisJc3RydWN0IG10a19zdnMg KnN2cyA9IGRldl9nZXRfZHJ2ZGF0YShkZXYpOw0KKwljb25zdCBzdHJ1Y3Qgc3ZzX3BsYXRmb3Jt ICpzdnNwID0gc3ZzLT5wbGF0Zm9ybTsNCisJc3RydWN0IHN2c19iYW5rICpzdnNiOw0KKwl1bnNp Z25lZCBsb25nIGZsYWdzOw0KKwl1MzIgaWR4Ow0KKw0KKwkvKiBXYWl0IGlmIHRoZXJlIGlzIHBy b2Nlc3Npbmcgc3ZzX2lzcigpLiBTdXNwZW5kIGFsbCBiYW5rcy4gKi8NCisJZmxhZ3MgPSBjbGFp bV9tdGtfc3ZzX2xvY2soKTsNCisJZm9yIChpZHggPSAwOyBpZHggPCBzdnNwLT5iYW5rX251bTsg aWR4KyspIHsNCisJCXN2c2IgPSAmc3ZzcC0+YmFua3NbaWR4XTsNCisJCXN2cy0+YmFuayA9IHN2 c2I7DQorCQlzdnNfc3dpdGNoX2Jhbmsoc3ZzKTsNCisJCXN2c193cml0ZWwoc3ZzLCBTVlNFTl9P RkYsIFNWU0VOKTsNCisJCXN2c193cml0ZWwoc3ZzLCBJTlRTVFNfQ0xFQU4sIElOVFNUUyk7DQor CQlzdnNiLT5zdXNwZW5kZWQgPSB0cnVlOw0KKwl9DQorCXJlbGVhc2VfbXRrX3N2c19sb2NrKGZs YWdzKTsNCisNCisJZm9yIChpZHggPSAwOyBpZHggPCBzdnNwLT5iYW5rX251bTsgaWR4KyspIHsN CisJCXN2c2IgPSAmc3ZzcC0+YmFua3NbaWR4XTsNCisJCWlmIChzdnNiLT5waGFzZSA9PSBTVlNC X1BIQVNFX01PTikgew0KKwkJCXN2c2ItPnBoYXNlID0gU1ZTQl9QSEFTRV9JTklUMDI7DQorCQkJ c3ZzYl9zZXRfdm9sdHMoc3ZzYiwgdHJ1ZSk7DQorCQl9DQorCX0NCisNCisJY2xrX2Rpc2FibGVf dW5wcmVwYXJlKHN2cy0+bWFpbl9jbGspOw0KKw0KKwlyZXR1cm4gMDsNCit9DQorDQorc3RhdGlj IGludCBzdnNfcmVzdW1lKHN0cnVjdCBkZXZpY2UgKmRldikNCit7DQorCXN0cnVjdCBtdGtfc3Zz ICpzdnMgPSBkZXZfZ2V0X2RydmRhdGEoZGV2KTsNCisJY29uc3Qgc3RydWN0IHN2c19wbGF0Zm9y bSAqc3ZzcCA9IHN2cy0+cGxhdGZvcm07DQorCXN0cnVjdCBzdnNfYmFuayAqc3ZzYjsNCisJaW50 IHJldDsNCisJdTMyIGlkeDsNCisNCisJcmV0ID0gY2xrX3ByZXBhcmVfZW5hYmxlKHN2cy0+bWFp bl9jbGspOw0KKwlpZiAocmV0KQ0KKwkJcHJfZXJyKCIlcygpOiBjYW5ub3QgZW5hYmxlIG1haW5f Y2xrXG4iLCBfX2Z1bmNfXyk7DQorDQorCWZvciAoaWR4ID0gMDsgaWR4IDwgc3ZzcC0+YmFua19u dW07IGlkeCsrKSB7DQorCQlzdnNiID0gJnN2c3AtPmJhbmtzW2lkeF07DQorCQlzdnNiLT5zdXNw ZW5kZWQgPSBmYWxzZTsNCisJfQ0KKw0KKwlyZXQgPSBzdnNfaW5pdDAyKHN2cyk7DQorCWlmIChy ZXQpDQorCQlyZXR1cm4gcmV0Ow0KKw0KKwlzdnNfbW9uX21vZGUoc3ZzKTsNCisNCisJcmV0dXJu IDA7DQorfQ0KKw0KK3N0YXRpYyBpbnQgc3ZzX2RlYnVnX3Byb2Nfc2hvdyhzdHJ1Y3Qgc2VxX2Zp bGUgKm0sIHZvaWQgKnYpDQorew0KKwlzdHJ1Y3Qgc3ZzX2JhbmsgKnN2c2IgPSAoc3RydWN0IHN2 c19iYW5rICopbS0+cHJpdmF0ZTsNCisNCisJaWYgKHN2c2ItPnBoYXNlID09IFNWU0JfUEhBU0Vf SU5JVDAxKQ0KKwkJc2VxX3B1dHMobSwgImluaXQxXG4iKTsNCisJZWxzZSBpZiAoc3ZzYi0+cGhh c2UgPT0gU1ZTQl9QSEFTRV9JTklUMDIpDQorCQlzZXFfcHV0cyhtLCAiaW5pdDJcbiIpOw0KKwll bHNlIGlmIChzdnNiLT5waGFzZSA9PSBTVlNCX1BIQVNFX01PTikNCisJCXNlcV9wdXRzKG0sICJt b24gbW9kZVxuIik7DQorCWVsc2UgaWYgKHN2c2ItPnBoYXNlID09IFNWU0JfUEhBU0VfRVJST1Ip DQorCQlzZXFfcHV0cyhtLCAiZGlzYWJsZWRcbiIpOw0KKwllbHNlDQorCQlzZXFfcHV0cyhtLCAi dW5rbm93blxuIik7DQorDQorCXJldHVybiAwOw0KK30NCisNCitzdGF0aWMgc3NpemVfdCBzdnNf ZGVidWdfcHJvY193cml0ZShzdHJ1Y3QgZmlsZSAqZmlsZSwNCisJCQkJICAgIGNvbnN0IGNoYXIg X191c2VyICpidWZmZXIsDQorCQkJCSAgICBzaXplX3QgY291bnQsIGxvZmZfdCAqcG9zKQ0KK3sN CisJc3RydWN0IHN2c19iYW5rICpzdnNiID0gKHN0cnVjdCBzdnNfYmFuayAqKVBERV9EQVRBKGZp bGVfaW5vZGUoZmlsZSkpOw0KKwlzdHJ1Y3QgbXRrX3N2cyAqc3ZzID0gZGV2X2dldF9kcnZkYXRh KHN2c2ItPmRldik7DQorCWNoYXIgKmJ1ZiA9IChjaGFyICopX19nZXRfZnJlZV9wYWdlKEdGUF9V U0VSKTsNCisJdW5zaWduZWQgbG9uZyBmbGFnczsNCisJaW50IGVuYWJsZWQsIHJldDsNCisNCisJ aWYgKHN2c2ItPnBoYXNlID09IFNWU0JfUEhBU0VfRVJST1IpDQorCQlyZXR1cm4gY291bnQ7DQor DQorCWlmICghYnVmKQ0KKwkJcmV0dXJuIC1FTk9NRU07DQorDQorCWlmIChjb3VudCA+PSBQQUdF X1NJWkUpIHsNCisJCWZyZWVfcGFnZSgodW5zaWduZWQgbG9uZylidWYpOw0KKwkJcmV0dXJuIC1F SU5WQUw7DQorCX0NCisNCisJaWYgKGNvcHlfZnJvbV91c2VyKGJ1ZiwgYnVmZmVyLCBjb3VudCkp IHsNCisJCWZyZWVfcGFnZSgodW5zaWduZWQgbG9uZylidWYpOw0KKwkJcmV0dXJuIC1FRkFVTFQ7 DQorCX0NCisNCisJYnVmW2NvdW50XSA9ICdcMCc7DQorDQorCXJldCA9IGtzdHJ0b2ludChidWYs IDEwLCAmZW5hYmxlZCk7DQorCWlmIChyZXQpDQorCQlyZXR1cm4gcmV0Ow0KKw0KKwlpZiAoIWVu YWJsZWQpIHsNCisJCWZsYWdzID0gY2xhaW1fbXRrX3N2c19sb2NrKCk7DQorCQlzdnMtPmJhbmsg PSBzdnNiOw0KKwkJc3ZzYi0+bW9kZV9zdXBwb3J0ID0gU1ZTQl9NT0RFX0FMTF9ESVNBQkxFOw0K KwkJc3ZzX3N3aXRjaF9iYW5rKHN2cyk7DQorCQlzdnNfd3JpdGVsKHN2cywgU1ZTRU5fT0ZGLCBT VlNFTik7DQorCQlzdnNfd3JpdGVsKHN2cywgSU5UU1RTX0NMRUFOLCBJTlRTVFMpOw0KKwkJcmVs ZWFzZV9tdGtfc3ZzX2xvY2soZmxhZ3MpOw0KKwl9DQorDQorCXN2c2ItPnBoYXNlID0gU1ZTQl9Q SEFTRV9FUlJPUjsNCisJc3ZzYl9zZXRfdm9sdHMoc3ZzYiwgdHJ1ZSk7DQorDQorCXJldHVybiBj b3VudDsNCit9DQorDQorcHJvY19mb3BzX3J3KHN2c19kZWJ1Zyk7DQorDQorc3RhdGljIGludCBz dnNfZHVtcF9wcm9jX3Nob3coc3RydWN0IHNlcV9maWxlICptLCB2b2lkICp2KQ0KK3sNCisJc3Ry dWN0IG10a19zdnMgKnN2cyA9IChzdHJ1Y3QgbXRrX3N2cyAqKW0tPnByaXZhdGU7DQorCWNvbnN0 IHN0cnVjdCBzdnNfcGxhdGZvcm0gKnN2c3AgPSBzdnMtPnBsYXRmb3JtOw0KKwlzdHJ1Y3Qgc3Zz X2JhbmsgKnN2c2I7DQorCXVuc2lnbmVkIGxvbmcgc3ZzX3JlZ19hZGRyOw0KKwl1MzIgaWR4LCBp LCBqOw0KKw0KKwlmb3IgKGkgPSAwOyBpIDwgc3ZzcC0+ZWZ1c2VfbnVtOyBpKyspIHsNCisJCWlm IChzdnMtPmVmdXNlW2ldKQ0KKwkJCXNlcV9wcmludGYobSwgIk1fSFdfUkVTJWQgPSAweCUwOHhc biIsDQorCQkJCSAgIGksIHN2cy0+ZWZ1c2VbaV0pOw0KKwl9DQorDQorCWZvciAoaSA9IDA7IGkg PCBzdnNwLT50aGVybWFsX2VmdXNlX251bTsgaSsrKSB7DQorCQlpZiAoc3ZzLT50aGVybWFsX2Vm dXNlICYmIHN2cy0+dGhlcm1hbF9lZnVzZVtpXSkNCisJCQlzZXFfcHJpbnRmKG0sICJUSEVSTUFM X0VGVVNFJWQgPSAweCUwOHhcbiIsDQorCQkJCSAgIGksIHN2cy0+dGhlcm1hbF9lZnVzZVtpXSk7 DQorCX0NCisNCisJZm9yIChpZHggPSAwOyBpZHggPCBzdnNwLT5iYW5rX251bTsgaWR4KyspIHsN CisJCXN2c2IgPSAmc3ZzcC0+YmFua3NbaWR4XTsNCisNCisJCWZvciAoaSA9IFNWU0JfUEhBU0Vf SU5JVDAxOyBpIDw9IFNWU0JfUEhBU0VfTU9OOyBpKyspIHsNCisJCQlzZXFfcHJpbnRmKG0sICJC YW5rX251bWJlciA9ICV1XG4iLCBzdnNiLT5iYW5rX2lkKTsNCisNCisJCQlpZiAoaSA8IFNWU0Jf UEhBU0VfTU9OKQ0KKwkJCQlzZXFfcHJpbnRmKG0sICJtb2RlID0gaW5pdCVkXG4iLCBpICsgMSk7 DQorCQkJZWxzZQ0KKwkJCQlzZXFfcHV0cyhtLCAibW9kZSA9IG1vblxuIik7DQorDQorCQkJZm9y IChqID0gVEVNUE1PTkNUTDA7IGogPCByZWdfbnVtOyBqKyspIHsNCisJCQkJc3ZzX3JlZ19hZGRy ID0gKHVuc2lnbmVkIGxvbmcpKHN2cy0+YmFzZSArDQorCQkJCQkJCSAgICAgICBzdnNwLT5yZWdz W2pdKTsNCisJCQkJc2VxX3ByaW50ZihtLCAiMHglMDhseCA9IDB4JTA4eFxuIiwNCisJCQkJCSAg IHN2c19yZWdfYWRkciwgc3ZzYi0+cmVnX2RhdGFbaV1bal0pOw0KKwkJCX0NCisJCX0NCisJfQ0K Kw0KKwlyZXR1cm4gMDsNCit9DQorDQorcHJvY19mb3BzX3JvKHN2c19kdW1wKTsNCisNCitzdGF0 aWMgaW50IHN2c19zdGF0dXNfcHJvY19zaG93KHN0cnVjdCBzZXFfZmlsZSAqbSwgdm9pZCAqdikN Cit7DQorCXN0cnVjdCBzdnNfYmFuayAqc3ZzYiA9IChzdHJ1Y3Qgc3ZzX2JhbmsgKiltLT5wcml2 YXRlOw0KKwlzdHJ1Y3QgZGV2X3BtX29wcCAqb3BwOw0KKwl1bnNpZ25lZCBsb25nIGZyZXE7DQor CWludCB6b25lX3RlbXAsIHJldDsNCisJdTMyIGk7DQorDQorCXJldCA9IHN2c2JfZ2V0X3pvbmVf dGVtcGVyYXR1cmUoc3ZzYiwgJnpvbmVfdGVtcCk7DQorCWlmIChyZXQpDQorCQlzZXFfcHJpbnRm KG0sICIlczogY2Fubm90IGdldCB6b25lIFwiJXNcIiB0ZW1wZXJhdHVyZVxuIiwNCisJCQkgICBz dnNiLT5uYW1lLCBzdnNiLT56b25lX25hbWUpOw0KKwllbHNlDQorCQlzZXFfcHJpbnRmKG0sICIl czogdGVtcGVyYXR1cmUgPSAlZFxuIiwgc3ZzYi0+bmFtZSwgem9uZV90ZW1wKTsNCisNCisJZm9y IChpID0gMCwgZnJlcSA9ICh1MzIpLTE7IGkgPCBzdnNiLT5vcHBfY291bnQ7IGkrKywgZnJlcS0t KSB7DQorCQlvcHAgPSBkZXZfcG1fb3BwX2ZpbmRfZnJlcV9mbG9vcihzdnNiLT5kZXYsICZmcmVx KTsNCisJCWlmIChJU19FUlIob3BwKSkgew0KKwkJCXNlcV9wcmludGYobSwgIiVzOiBlcnJvciBv cHAgZW50cnkhISwgZXJyID0gJWxkXG4iLA0KKwkJCQkgICBzdnNiLT5uYW1lLCBQVFJfRVJSKG9w cCkpOw0KKwkJCXJldHVybiBQVFJfRVJSKG9wcCk7DQorCQl9DQorDQorCQlzZXFfcHJpbnRmKG0s ICJvcHBfZnJlcXNbJTAydV06ICVsdSwgdm9sdHNbJTAydV06ICVsdSwgIiwNCisJCQkgICBpLCBm cmVxLCBpLCBkZXZfcG1fb3BwX2dldF92b2x0YWdlKG9wcCkpOw0KKwkJc2VxX3ByaW50ZihtLCAi c3ZzYl92b2x0c1slMDJ1XTogMHgleCwgZnJlcXNfcGN0WyUwMnVdOiAldVxuIiwNCisJCQkgICBp LCBzdnNiLT52b2x0c1tpXSwgaSwgc3ZzYi0+ZnJlcXNfcGN0W2ldKTsNCisJCWRldl9wbV9vcHBf cHV0KG9wcCk7DQorCX0NCisNCisJcmV0dXJuIDA7DQorfQ0KKw0KK3Byb2NfZm9wc19ybyhzdnNf c3RhdHVzKTsNCisNCitzdGF0aWMgaW50IHN2c192b2x0X29mZnNldF9wcm9jX3Nob3coc3RydWN0 IHNlcV9maWxlICptLCB2b2lkICp2KQ0KK3sNCisJc3RydWN0IHN2c19iYW5rICpzdnNiID0gKHN0 cnVjdCBzdnNfYmFuayAqKW0tPnByaXZhdGU7DQorDQorCXNlcV9wcmludGYobSwgIiVkXG4iLCBz dnNiLT52b2x0X29mZnNldCk7DQorDQorCXJldHVybiAwOw0KK30NCisNCitzdGF0aWMgc3NpemVf dCBzdnNfdm9sdF9vZmZzZXRfcHJvY193cml0ZShzdHJ1Y3QgZmlsZSAqZmlsZSwNCisJCQkJCSAg Y29uc3QgY2hhciBfX3VzZXIgKmJ1ZmZlciwNCisJCQkJCSAgc2l6ZV90IGNvdW50LCBsb2ZmX3Qg KnBvcykNCit7DQorCXN0cnVjdCBzdnNfYmFuayAqc3ZzYiA9IChzdHJ1Y3Qgc3ZzX2JhbmsgKilQ REVfREFUQShmaWxlX2lub2RlKGZpbGUpKTsNCisJY2hhciAqYnVmID0gKGNoYXIgKilfX2dldF9m cmVlX3BhZ2UoR0ZQX1VTRVIpOw0KKwlpbnQgcmV0LCB2b2x0X29mZnNldDsNCisNCisJaWYgKCFi dWYpDQorCQlyZXR1cm4gLUVOT01FTTsNCisNCisJaWYgKGNvdW50ID49IFBBR0VfU0laRSkgew0K KwkJZnJlZV9wYWdlKCh1bnNpZ25lZCBsb25nKWJ1Zik7DQorCQlyZXR1cm4gLUVJTlZBTDsNCisJ fQ0KKw0KKwlpZiAoY29weV9mcm9tX3VzZXIoYnVmLCBidWZmZXIsIGNvdW50KSkgew0KKwkJZnJl ZV9wYWdlKCh1bnNpZ25lZCBsb25nKWJ1Zik7DQorCQlyZXR1cm4gLUVGQVVMVDsNCisJfQ0KKw0K KwlidWZbY291bnRdID0gJ1wwJzsNCisNCisJaWYgKCFrc3RydG9pbnQoYnVmLCAxMCwgJnZvbHRf b2Zmc2V0KSkgew0KKwkJc3ZzYi0+dm9sdF9vZmZzZXQgPSB2b2x0X29mZnNldDsNCisJCXJldCA9 IHN2c2Jfc2V0X3ZvbHRzKHN2c2IsIHRydWUpOw0KKwkJaWYgKHJldCkNCisJCQlyZXR1cm4gcmV0 Ow0KKwl9DQorDQorCXJldHVybiBjb3VudDsNCit9DQorDQorcHJvY19mb3BzX3J3KHN2c192b2x0 X29mZnNldCk7DQorDQorc3RhdGljIGludCBzdnNfY3JlYXRlX3N2c19wcm9jZnMoc3RydWN0IG10 a19zdnMgKnN2cykNCit7DQorCWNvbnN0IHN0cnVjdCBzdnNfcGxhdGZvcm0gKnN2c3AgPSBzdnMt PnBsYXRmb3JtOw0KKwlzdHJ1Y3Qgc3ZzX2JhbmsgKnN2c2I7DQorCXN0cnVjdCBwcm9jX2Rpcl9l bnRyeSAqc3ZzX2RpciwgKmJhbmtfZGlyOw0KKwl1MzIgaWR4LCBpOw0KKw0KKwlzdHJ1Y3QgcGVu dHJ5IHsNCisJCWNvbnN0IGNoYXIgKm5hbWU7DQorCQljb25zdCBzdHJ1Y3QgZmlsZV9vcGVyYXRp b25zICpmb3BzOw0KKwl9Ow0KKw0KKwlzdHJ1Y3QgcGVudHJ5IHN2c19lbnRyaWVzW10gPSB7DQor CQlwcm9jX2VudHJ5KHN2c19kdW1wKSwNCisJfTsNCisNCisJc3RydWN0IHBlbnRyeSBiYW5rX2Vu dHJpZXNbXSA9IHsNCisJCXByb2NfZW50cnkoc3ZzX2RlYnVnKSwNCisJCXByb2NfZW50cnkoc3Zz X3N0YXR1cyksDQorCQlwcm9jX2VudHJ5KHN2c192b2x0X29mZnNldCksDQorCX07DQorDQorCXN2 c19kaXIgPSBwcm9jX21rZGlyKCJzdnMiLCBOVUxMKTsNCisJaWYgKCFzdnNfZGlyKSB7DQorCQlw cl9lcnIoIm1rZGlyIC9wcm9jL3N2cyBmYWlsZWRcbiIpOw0KKwkJcmV0dXJuIC1FUEVSTTsNCisJ fQ0KKw0KKwlmb3IgKGkgPSAwOyBpIDwgQVJSQVlfU0laRShzdnNfZW50cmllcyk7IGkrKykgew0K KwkJaWYgKCFwcm9jX2NyZWF0ZV9kYXRhKHN2c19lbnRyaWVzW2ldLm5hbWUsIDA2NjQsDQorCQkJ CSAgICAgIHN2c19kaXIsIHN2c19lbnRyaWVzW2ldLmZvcHMsIHN2cykpIHsNCisJCQlwcl9lcnIo ImNyZWF0ZSAvcHJvYy9zdnMvJXMgZmFpbGVkXG4iLA0KKwkJCSAgICAgICBzdnNfZW50cmllc1tp XS5uYW1lKTsNCisJCQlyZXR1cm4gLUVQRVJNOw0KKwkJfQ0KKwl9DQorDQorCWZvciAoaWR4ID0g MDsgaWR4IDwgc3ZzcC0+YmFua19udW07IGlkeCsrKSB7DQorCQlzdnNiID0gJnN2c3AtPmJhbmtz W2lkeF07DQorDQorCQlpZiAoc3ZzYi0+bW9kZV9zdXBwb3J0ID09IFNWU0JfTU9ERV9BTExfRElT QUJMRSkNCisJCQljb250aW51ZTsNCisNCisJCWJhbmtfZGlyID0gcHJvY19ta2RpcihzdnNiLT5u YW1lLCBzdnNfZGlyKTsNCisJCWlmICghYmFua19kaXIpIHsNCisJCQlwcl9lcnIoIm1rZGlyIC9w cm9jL3N2cy8lcyBmYWlsZWRcbiIsIHN2c2ItPm5hbWUpOw0KKwkJCXJldHVybiAtRVBFUk07DQor CQl9DQorDQorCQlmb3IgKGkgPSAwOyBpIDwgQVJSQVlfU0laRShiYW5rX2VudHJpZXMpOyBpKysp IHsNCisJCQlpZiAoIXByb2NfY3JlYXRlX2RhdGEoYmFua19lbnRyaWVzW2ldLm5hbWUsIDA2NjQs DQorCQkJCQkgICAgICBiYW5rX2RpciwgYmFua19lbnRyaWVzW2ldLmZvcHMsDQorCQkJCQkgICAg ICBzdnNiKSkgew0KKwkJCQlwcl9lcnIoImNyZWF0ZSAvcHJvYy9zdnMvJXMvJXMgZmFpbGVkXG4i LA0KKwkJCQkgICAgICAgc3ZzYi0+bmFtZSwgYmFua19lbnRyaWVzW2ldLm5hbWUpOw0KKwkJCQly ZXR1cm4gLUVQRVJNOw0KKwkJCX0NCisJCX0NCisJfQ0KKw0KKwlyZXR1cm4gMDsNCit9DQorDQor c3RhdGljIHN0cnVjdCBzdnNfYmFua19vcHMgc3ZzX210ODE4M19iYW5rc19vcHMgPSB7DQorCS5z ZXRfZnJlcXNfcGN0CT0gc3ZzX3NldF9mcmVxc19wY3RfdjIsDQorCS5nZXRfdm9wcwk9IHN2c19n ZXRfdm9wc192MiwNCit9Ow0KKw0KK3N0YXRpYyBzdHJ1Y3Qgc3ZzX2Jhbmsgc3ZzX210ODE4M19i YW5rc1s0XSA9IHsNCisJew0KKwkJLm9mX2NvbXBhdGlibGUJCT0gIm1lZGlhdGVrLG10ODE4My1z dnMtY3B1LWxpdHRsZSIsDQorCQkuc3dfaWQJCQk9IFNWU19DUFVfTElUVExFLA0KKwkJLmJhbmtf aWQJCT0gMCwNCisJCS5vcHMJCQk9ICZzdnNfbXQ4MTgzX2JhbmtzX29wcywNCisJCS56b25lX25h bWUJCT0gInR6dHM0IiwNCisJCS5idWNrX25hbWUJCT0gInZjcHUtbGl0dGxlIiwNCisJCS5tdGNt b3NfcmVxdWVzdAkJPSBmYWxzZSwNCisJCS5pbml0MDFfdm9sdF9mbGFnCT0gU1ZTQl9JTklUMDFf Vk9MVF9JTkNfT05MWSwNCisJCS5tb2RlX3N1cHBvcnQJCT0gU1ZTQl9NT0RFX0lOSVQwMSB8IFNW U0JfTU9ERV9JTklUMDIsDQorCQkub3BwX2NvdW50CQk9IDE2LA0KKwkJLmZyZXFfYmFzZQkJPSAx OTg5MDAwMDAwLA0KKwkJLnZib290CQkJPSAweDMwLA0KKwkJLnZvbHRfc3RlcAkJPSA2MjUwLA0K KwkJLnZvbHRfYmFzZQkJPSA1MDAwMDAsDQorCQkudm9sdF9vZmZzZXQJCT0gMCwNCisJCS52bWF4 CQkJPSAweDY0LA0KKwkJLnZtaW4JCQk9IDB4MTgsDQorCQkuZHRoaQkJCT0gMHgxLA0KKwkJLmR0 bG8JCQk9IDB4ZmUsDQorCQkuZGV0X3dpbmRvdwkJPSAweGEyOCwNCisJCS5kZXRfbWF4CQk9IDB4 ZmZmZiwNCisJCS5hZ2VfY29uZmlnCQk9IDB4NTU1NTU1LA0KKwkJLmFnZW0JCQk9IDB4MCwNCisJ CS5kY19jb25maWcJCT0gMHg1NTU1NTUsDQorCQkuZHZ0X2ZpeGVkCQk9IDB4NywNCisJCS52Y28J CQk9IDB4MTAsDQorCQkuY2hrc2hpZnQJCT0gMHg3NywNCisJCS51cHBlcl90ZW1wX2JvdW5kCT0g MHg2NCwNCisJCS5sb3dlcl90ZW1wX2JvdW5kCT0gMHhiMiwNCisJCS5sb3dfdGVtcF90aHJlYXNo b2xkCT0gMjUwMDAsDQorCQkubG93X3RlbXBfb2Zmc2V0CT0gMCwNCisJCS5jb3Jlc2VsCQk9IDB4 OGZmZjAwMDAsDQorCQkuc3lzdGVtY2xrX2VuCQk9IEJJVCgzMSksDQorCQkuaW50c3QJCQk9IEJJ VCgwKSwNCisJCS5jdGwwCQkJPSAweDAwMDEwMDAxLA0KKwl9LA0KKwl7DQorCQkub2ZfY29tcGF0 aWJsZQkJPSAibWVkaWF0ZWssbXQ4MTgzLXN2cy1jcHUtYmlnIiwNCisJCS5zd19pZAkJCT0gU1ZT X0NQVV9CSUcsDQorCQkuYmFua19pZAkJPSAxLA0KKwkJLm9wcwkJCT0gJnN2c19tdDgxODNfYmFu a3Nfb3BzLA0KKwkJLnpvbmVfbmFtZQkJPSAidHp0czUiLA0KKwkJLmJ1Y2tfbmFtZQkJPSAidmNw dS1iaWciLA0KKwkJLm10Y21vc19yZXF1ZXN0CQk9IGZhbHNlLA0KKwkJLmluaXQwMV92b2x0X2Zs YWcJPSBTVlNCX0lOSVQwMV9WT0xUX0lOQ19PTkxZLA0KKwkJLm1vZGVfc3VwcG9ydAkJPSBTVlNC X01PREVfSU5JVDAxIHwgU1ZTQl9NT0RFX0lOSVQwMiwNCisJCS5vcHBfY291bnQJCT0gMTYsDQor CQkuZnJlcV9iYXNlCQk9IDE5ODkwMDAwMDAsDQorCQkudmJvb3QJCQk9IDB4MzAsDQorCQkudm9s dF9zdGVwCQk9IDYyNTAsDQorCQkudm9sdF9iYXNlCQk9IDUwMDAwMCwNCisJCS52b2x0X29mZnNl dAkJPSAwLA0KKwkJLnZtYXgJCQk9IDB4NTgsDQorCQkudm1pbgkJCT0gMHgxMCwNCisJCS5kdGhp CQkJPSAweDEsDQorCQkuZHRsbwkJCT0gMHhmZSwNCisJCS5kZXRfd2luZG93CQk9IDB4YTI4LA0K KwkJLmRldF9tYXgJCT0gMHhmZmZmLA0KKwkJLmFnZV9jb25maWcJCT0gMHg1NTU1NTUsDQorCQku YWdlbQkJCT0gMHgwLA0KKwkJLmRjX2NvbmZpZwkJPSAweDU1NTU1NSwNCisJCS5kdnRfZml4ZWQJ CT0gMHg3LA0KKwkJLnZjbwkJCT0gMHgxMCwNCisJCS5jaGtzaGlmdAkJPSAweDc3LA0KKwkJLnVw cGVyX3RlbXBfYm91bmQJPSAweDY0LA0KKwkJLmxvd2VyX3RlbXBfYm91bmQJPSAweGIyLA0KKwkJ Lmxvd190ZW1wX3RocmVhc2hvbGQJPSAyNTAwMCwNCisJCS5sb3dfdGVtcF9vZmZzZXQJPSAwLA0K KwkJLmNvcmVzZWwJCT0gMHg4ZmZmMDAwMSwNCisJCS5zeXN0ZW1jbGtfZW4JCT0gQklUKDMxKSwN CisJCS5pbnRzdAkJCT0gQklUKDEpLA0KKwkJLmN0bDAJCQk9IDB4MDAwMDAwMDEsDQorCX0sDQor CXsNCisJCS5vZl9jb21wYXRpYmxlCQk9ICJtZWRpYXRlayxtdDgxODMtc3ZzLWNjaSIsDQorCQku c3dfaWQJCQk9IFNWU19DQ0ksDQorCQkuYmFua19pZAkJPSAyLA0KKwkJLm9wcwkJCT0gJnN2c19t dDgxODNfYmFua3Nfb3BzLA0KKwkJLnpvbmVfbmFtZQkJPSAidHp0czQiLA0KKwkJLmJ1Y2tfbmFt ZQkJPSAidmNjaSIsDQorCQkubXRjbW9zX3JlcXVlc3QJCT0gZmFsc2UsDQorCQkuaW5pdDAxX3Zv bHRfZmxhZwk9IFNWU0JfSU5JVDAxX1ZPTFRfSU5DX09OTFksDQorCQkubW9kZV9zdXBwb3J0CQk9 IFNWU0JfTU9ERV9JTklUMDEgfCBTVlNCX01PREVfSU5JVDAyLA0KKwkJLm9wcF9jb3VudAkJPSAx NiwNCisJCS5mcmVxX2Jhc2UJCT0gMTE5NjAwMDAwMCwNCisJCS52Ym9vdAkJCT0gMHgzMCwNCisJ CS52b2x0X3N0ZXAJCT0gNjI1MCwNCisJCS52b2x0X2Jhc2UJCT0gNTAwMDAwLA0KKwkJLnZvbHRf b2Zmc2V0CQk9IDAsDQorCQkudm1heAkJCT0gMHg2NCwNCisJCS52bWluCQkJPSAweDE4LA0KKwkJ LmR0aGkJCQk9IDB4MSwNCisJCS5kdGxvCQkJPSAweGZlLA0KKwkJLmRldF93aW5kb3cJCT0gMHhh MjgsDQorCQkuZGV0X21heAkJPSAweGZmZmYsDQorCQkuYWdlX2NvbmZpZwkJPSAweDU1NTU1NSwN CisJCS5hZ2VtCQkJPSAweDAsDQorCQkuZGNfY29uZmlnCQk9IDB4NTU1NTU1LA0KKwkJLmR2dF9m aXhlZAkJPSAweDcsDQorCQkudmNvCQkJPSAweDEwLA0KKwkJLmNoa3NoaWZ0CQk9IDB4NzcsDQor CQkudXBwZXJfdGVtcF9ib3VuZAk9IDB4NjQsDQorCQkubG93ZXJfdGVtcF9ib3VuZAk9IDB4YjIs DQorCQkubG93X3RlbXBfdGhyZWFzaG9sZAk9IDI1MDAwLA0KKwkJLmxvd190ZW1wX29mZnNldAk9 IDAsDQorCQkuY29yZXNlbAkJPSAweDhmZmYwMDAyLA0KKwkJLnN5c3RlbWNsa19lbgkJPSBCSVQo MzEpLA0KKwkJLmludHN0CQkJPSBCSVQoMiksDQorCQkuY3RsMAkJCT0gMHgwMDEwMDAwMywNCisJ fSwNCisJew0KKwkJLm9mX2NvbXBhdGlibGUJCT0gIm1lZGlhdGVrLG10ODE4My1zdnMtZ3B1IiwN CisJCS5zd19pZAkJCT0gU1ZTX0dQVSwNCisJCS5iYW5rX2lkCQk9IDMsDQorCQkub3BzCQkJPSAm c3ZzX210ODE4M19iYW5rc19vcHMsDQorCQkuem9uZV9uYW1lCQk9ICJ0enRzMiIsDQorCQkuYnVj a19uYW1lCQk9ICJ2Z3B1IiwNCisJCS5tdGNtb3NfcmVxdWVzdAkJPSB0cnVlLA0KKwkJLmluaXQw MV92b2x0X2ZsYWcJPSBTVlNCX0lOSVQwMV9WT0xUX0lOQ19PTkxZLA0KKwkJLm1vZGVfc3VwcG9y dAkJPSBTVlNCX01PREVfSU5JVDAxIHwgU1ZTQl9NT0RFX0lOSVQwMiB8DQorCQkJCQkgIFNWU0Jf TU9ERV9NT04sDQorCQkub3BwX2NvdW50CQk9IDE2LA0KKwkJLmZyZXFfYmFzZQkJPSA5MDAwMDAw MDAsDQorCQkudmJvb3QJCQk9IDB4MzAsDQorCQkudm9sdF9zdGVwCQk9IDYyNTAsDQorCQkudm9s dF9iYXNlCQk9IDUwMDAwMCwNCisJCS52b2x0X29mZnNldAkJPSAwLA0KKwkJLnZtYXgJCQk9IDB4 NDAsDQorCQkudm1pbgkJCT0gMHgxNCwNCisJCS5kdGhpCQkJPSAweDEsDQorCQkuZHRsbwkJCT0g MHhmZSwNCisJCS5kZXRfd2luZG93CQk9IDB4YTI4LA0KKwkJLmRldF9tYXgJCT0gMHhmZmZmLA0K KwkJLmFnZV9jb25maWcJCT0gMHg1NTU1NTUsDQorCQkuYWdlbQkJCT0gMHgwLA0KKwkJLmRjX2Nv bmZpZwkJPSAweDU1NTU1NSwNCisJCS5kdnRfZml4ZWQJCT0gMHgzLA0KKwkJLnZjbwkJCT0gMHgx MCwNCisJCS5jaGtzaGlmdAkJPSAweDc3LA0KKwkJLnVwcGVyX3RlbXBfYm91bmQJPSAweDY0LA0K KwkJLmxvd2VyX3RlbXBfYm91bmQJPSAweGIyLA0KKwkJLmxvd190ZW1wX3RocmVhc2hvbGQJPSAy NTAwMCwNCisJCS5sb3dfdGVtcF9vZmZzZXQJPSAzLA0KKwkJLmNvcmVzZWwJCT0gMHg4ZmZmMDAw MywNCisJCS5zeXN0ZW1jbGtfZW4JCT0gQklUKDMxKSwNCisJCS5pbnRzdAkJCT0gQklUKDMpLA0K KwkJLmN0bDAJCQk9IDB4MDAwNTAwMDEsDQorCX0sDQorfTsNCisNCitzdGF0aWMgY29uc3Qgc3Ry dWN0IHN2c19wbGF0Zm9ybSBzdnNfbXQ4MTgzX3BsYXRmb3JtID0gew0KKwkubmFtZQkJPSAibXQ4 MTgzLXN2cyIsDQorCS5iYW5rcwkJPSBzdnNfbXQ4MTgzX2JhbmtzLA0KKwkuZWZ1c2VfcGFyc2lu Zwk9IHN2c19tdDgxODNfZWZ1c2VfcGFyc2luZywNCisJLnJlZ3MJCT0gc3ZzX3JlZ3NfdjIsDQor CS5mYWtlX2VmdXNlCT0gZmFsc2UsDQorCS5iYW5rX251bQk9IDQsDQorCS5lZnVzZV9udW0JPSAy NSwNCisJLmVmdXNlX2NoZWNrCT0gMiwNCisJLnRoZXJtYWxfZWZ1c2VfbnVtID0gMywNCit9Ow0K Kw0KK3N0YXRpYyBjb25zdCBzdHJ1Y3Qgb2ZfZGV2aWNlX2lkIG10a19zdnNfb2ZfbWF0Y2hbXSA9 IHsNCisJew0KKwkJLmNvbXBhdGlibGUgPSAibWVkaWF0ZWssbXQ4MTgzLXN2cyIsDQorCQkuZGF0 YSA9ICZzdnNfbXQ4MTgzX3BsYXRmb3JtLA0KKwl9LCB7DQorCQkvKiBTZW50aW5lbCAqLw0KKwl9 LA0KK307DQorDQorc3RhdGljIGludCBzdnNfcHJvYmUoc3RydWN0IHBsYXRmb3JtX2RldmljZSAq cGRldikNCit7DQorCWNvbnN0IHN0cnVjdCBvZl9kZXZpY2VfaWQgKm9mX2Rldl9pZDsNCisJc3Ry dWN0IG10a19zdnMgKnN2czsNCisJaW50IHJldDsNCisJdTMyIHN2c19pcnE7DQorDQorCXN2cyA9 IGRldm1fa3phbGxvYygmcGRldi0+ZGV2LCBzaXplb2YoKnN2cyksIEdGUF9LRVJORUwpOw0KKwlp ZiAoIXN2cykNCisJCXJldHVybiAtRU5PTUVNOw0KKw0KKwlzdnMtPmRldiA9ICZwZGV2LT5kZXY7 DQorCWlmICghc3ZzLT5kZXYtPm9mX25vZGUpIHsNCisJCXByX2VycigiY2Fubm90IGZpbmQgZGV2 aWNlIG5vZGVcbiIpOw0KKwkJcmV0dXJuIC1FTk9ERVY7DQorCX0NCisNCisJc3ZzLT5iYXNlID0g b2ZfaW9tYXAoc3ZzLT5kZXYtPm9mX25vZGUsIDApOw0KKwlpZiAoSVNfRVJSKHN2cy0+YmFzZSkp IHsNCisJCXByX2VycigiY2Fubm90IGZpbmQgc3ZzIHJlZ2lzdGVyIGJhc2VcbiIpOw0KKwkJcmV0 dXJuIFBUUl9FUlIoc3ZzLT5iYXNlKTsNCisJfQ0KKw0KKwlzdnNfaXJxID0gaXJxX29mX3BhcnNl X2FuZF9tYXAoc3ZzLT5kZXYtPm9mX25vZGUsIDApOw0KKwlyZXQgPSBkZXZtX3JlcXVlc3RfdGhy ZWFkZWRfaXJxKHN2cy0+ZGV2LCBzdnNfaXJxLCBOVUxMLCBzdnNfaXNyLA0KKwkJCQkJSVJRRl9U UklHR0VSX0xPVyB8IElSUUZfT05FU0hPVCwNCisJCQkJCSJtdGstc3ZzIiwgc3ZzKTsNCisJaWYg KHJldCkgew0KKwkJcHJfZXJyKCJyZWdpc3RlciBpcnEoJWQpIGZhaWxlZDogJWRcbiIsIHN2c19p cnEsIHJldCk7DQorCQlyZXR1cm4gcmV0Ow0KKwl9DQorDQorCW9mX2Rldl9pZCA9IG9mX21hdGNo X25vZGUobXRrX3N2c19vZl9tYXRjaCwgc3ZzLT5kZXYtPm9mX25vZGUpOw0KKwlpZiAoIW9mX2Rl dl9pZCB8fCAhb2ZfZGV2X2lkLT5kYXRhKQ0KKwkJcmV0dXJuIC1FSU5WQUw7DQorDQorCXN2cy0+ cGxhdGZvcm0gPSBvZl9kZXZfaWQtPmRhdGE7DQorCWRldl9zZXRfZHJ2ZGF0YShzdnMtPmRldiwg c3ZzKTsNCisNCisJc3ZzLT5tYWluX2NsayA9IGRldm1fY2xrX2dldChzdnMtPmRldiwgIm1haW4i KTsNCisJaWYgKElTX0VSUihzdnMtPm1haW5fY2xrKSkgew0KKwkJcHJfZXJyKCJmYWlsZWQgdG8g Z2V0IGNsb2NrOiAlbGRcbiIsIFBUUl9FUlIoc3ZzLT5tYWluX2NsaykpOw0KKwkJcmV0dXJuIFBU Ul9FUlIoc3ZzLT5tYWluX2Nsayk7DQorCX0NCisNCisJcmV0ID0gY2xrX3ByZXBhcmVfZW5hYmxl KHN2cy0+bWFpbl9jbGspOw0KKwlpZiAocmV0KSB7DQorCQlwcl9lcnIoImNhbm5vdCBlbmFibGUg bWFpbiBjbGs6ICVkXG4iLCByZXQpOw0KKwkJcmV0dXJuIHJldDsNCisJfQ0KKw0KKwlpZiAoIXN2 c19pc19zdXBwb3J0ZWQoc3ZzKSkgew0KKwkJcHJfbm90aWNlKCJzdnMgaXMgbm90IHN1cHBvcnRl ZFxuIik7DQorCQlnb3RvIHN2c19wcm9iZV9mYWlsOw0KKwl9DQorDQorCXJldCA9IHN2c19yZXNv dXJjZV9zZXR1cChzdnMpOw0KKwlpZiAocmV0KQ0KKwkJZ290byBzdnNfcHJvYmVfZmFpbDsNCisN CisJcmV0ID0gc3ZzX3N0YXJ0KHN2cyk7DQorCWlmIChyZXQpDQorCQlnb3RvIHN2c19wcm9iZV9m YWlsOw0KKw0KKwlyZXQgPSBzdnNfY3JlYXRlX3N2c19wcm9jZnMoc3ZzKTsNCisJaWYgKHJldCkN CisJCWdvdG8gc3ZzX3Byb2JlX2ZhaWw7DQorDQorCXJldHVybiAwOw0KKw0KK3N2c19wcm9iZV9m YWlsOg0KKwljbGtfZGlzYWJsZV91bnByZXBhcmUoc3ZzLT5tYWluX2Nsayk7DQorDQorCXJldHVy biByZXQ7DQorfQ0KKw0KK3N0YXRpYyBjb25zdCBzdHJ1Y3QgZGV2X3BtX29wcyBzdnNfcG1fb3Bz ID0gew0KKwkuc3VzcGVuZAk9IHN2c19zdXNwZW5kLA0KKwkucmVzdW1lCQk9IHN2c19yZXN1bWUs DQorfTsNCisNCitzdGF0aWMgc3RydWN0IHBsYXRmb3JtX2RyaXZlciBzdnNfZHJpdmVyID0gew0K KwkucHJvYmUJPSBzdnNfcHJvYmUsDQorCS5kcml2ZXIJPSB7DQorCQkubmFtZQkJPSAibXRrLXN2 cyIsDQorCQkucG0JCT0gJnN2c19wbV9vcHMsDQorCQkub2ZfbWF0Y2hfdGFibGUJPSBvZl9tYXRj aF9wdHIobXRrX3N2c19vZl9tYXRjaCksDQorCX0sDQorfTsNCisNCitzdGF0aWMgaW50IF9faW5p dCBzdnNfaW5pdCh2b2lkKQ0KK3sNCisJaW50IHJldDsNCisNCisJcmV0ID0gcGxhdGZvcm1fZHJp dmVyX3JlZ2lzdGVyKCZzdnNfZHJpdmVyKTsNCisJaWYgKHJldCkgew0KKwkJcHJfZXJyKCJzdnMg cGxhdGZvcm0gZHJpdmVyIHJlZ2lzdGVyIGZhaWxlZDogJWRcbiIsIHJldCk7DQorCQlyZXR1cm4g cmV0Ow0KKwl9DQorDQorCXJldHVybiAwOw0KK30NCisNCitsYXRlX2luaXRjYWxsX3N5bmMoc3Zz X2luaXQpOw0KKw0KK01PRFVMRV9ERVNDUklQVElPTigiTWVkaWFUZWsgU1ZTIERyaXZlciB2MS4w Iik7DQorTU9EVUxFX0xJQ0VOU0UoIkdQTCIpOw0KZGlmZiAtLWdpdCBhL2luY2x1ZGUvbGludXgv cG93ZXIvbXRrX3N2cy5oIGIvaW5jbHVkZS9saW51eC9wb3dlci9tdGtfc3ZzLmgNCm5ldyBmaWxl IG1vZGUgMTAwNjQ0DQppbmRleCAwMDAwMDAwMDAwMDAuLjVjMDM5ODJlMzU3Ng0KLS0tIC9kZXYv bnVsbA0KKysrIGIvaW5jbHVkZS9saW51eC9wb3dlci9tdGtfc3ZzLmgNCkBAIC0wLDAgKzEsMjMg QEANCisvKiBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogR1BMLTIuMA0KKyAqDQorICogQ29weXJp Z2h0IChDKSAyMDIwIE1lZGlhVGVrIEluYy4NCisgKi8NCisNCisjaWZuZGVmIF9fTVRLX1NWU19I X18NCisjZGVmaW5lIF9fTVRLX1NWU19IX18NCisNCisjaWYgSVNfRU5BQkxFRChDT05GSUdfTVRL X1NWUykNCit1bnNpZ25lZCBsb25nIGNsYWltX210a19zdnNfbG9jayh2b2lkKTsNCit2b2lkIHJl bGVhc2VfbXRrX3N2c19sb2NrKHVuc2lnbmVkIGxvbmcgZmxhZ3MpOw0KKyNlbHNlDQorc3RhdGlj IGlubGluZSB1bnNpZ25lZCBsb25nIGNsYWltX210a19zdnNfbG9jayh2b2lkKQ0KK3sNCisJcmV0 dXJuIDA7DQorfQ0KKw0KK3N0YXRpYyBpbmxpbmUgdm9pZCByZWxlYXNlX210a19zdnNfbG9jayh1 bnNpZ25lZCBsb25nIGZsYWdzKQ0KK3sNCit9DQorI2VuZGlmIC8qIENPTkZJR19NVEtfU1ZTICov DQorDQorI2VuZGlmIC8qIF9fTVRLX1NWU19IX18gKi8NCi0tIA0KMi4xOC4wDQo= From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=DKIMWL_WL_HIGH,DKIM_SIGNED, DKIM_VALID,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,UNPARSEABLE_RELAY,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2E338C33C9B for ; Tue, 7 Jan 2020 07:03:54 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id E9577206DB for ; Tue, 7 Jan 2020 07:03:53 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="UXwDhnRh"; dkim=fail reason="signature verification failed" (1024-bit key) header.d=mediatek.com header.i=@mediatek.com header.b="Kni1eguz" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org E9577206DB Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=mediatek.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+infradead-linux-arm-kernel=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=OS/h+JQjWwlfdDX2pD8wN5jh2m33k8Ho5CGb/KUbalc=; b=UXwDhnRhWf83un sSA5bqiszC8kOuWG6BXqbx0ILTVTzOTAT3LtB/+shgW95Fun9BVqhEgWwI2qmI5UG2HcPPqd2EVFl 7biHi+yxqZDTT0SLG6+Z/yuVXi8BYmXA9sNsKmcYwmwgUxXhwG1/sL4YZVg2Kjpo8kWSmli5v4ypY UUBRICpC8LjqaDp9v1kENTIMSOWI3tXInC3Ddb/MbmC68oMUJ3Gh3VvBFo12QLGRWeLmFwZ83/ZnB 4zBM3fpE+XXxibi3IZaVBTcfLlDYpwj8sTGu9121SJNUgNRtIAS6dYI4cjELDUf9qteSBV6u7f2Zu 6M/T8rRgZ+CtJFO8I2zQ==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1ioiu4-00030H-5z; Tue, 07 Jan 2020 07:03:48 +0000 Received: from mailgw01.mediatek.com ([216.200.240.184]) by bombadil.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1ioitx-0002yZ-8M; Tue, 07 Jan 2020 07:03:45 +0000 X-UUID: 50c0a69672d54a38acee72d7e71341c2-20200106 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mediatek.com; s=dk; h=Content-Transfer-Encoding:Content-Type:MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:CC:To:From; bh=VjjSHPgZ0wy2flMgNuu3m046BjiufOod+qrlXQt1vkw=; b=Kni1eguztIKmj7GAGBPZKWoE696/yHhyP3CAeUhW4kqvRtiDe68ANEJwnO2i/7u6zmDVTfTZiO7HNzHT8fBY5wGOENT+V/2s5ex2ti+2ilu/QOVkyXVausWhq8y7L2NID6SLX33Idll4oVUkbnQqtywnXrOUyLO2Y7Z/MxAG5fs=; X-UUID: 50c0a69672d54a38acee72d7e71341c2-20200106 Received: from mtkcas66.mediatek.inc [(172.29.193.44)] by mailgw01.mediatek.com (envelope-from ) (musrelay.mediatek.com ESMTP with TLS) with ESMTP id 1359242466; Mon, 06 Jan 2020 23:03:33 -0800 Received: from mtkmbs05n1.mediatek.inc (172.21.101.15) by MTKMBS62N1.mediatek.inc (172.29.193.41) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Mon, 6 Jan 2020 23:02:49 -0800 Received: from mtkcas07.mediatek.inc (172.21.101.84) by mtkmbs05n1.mediatek.inc (172.21.101.15) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Tue, 7 Jan 2020 15:01:45 +0800 Received: from mtksdaap41.mediatek.inc (172.21.77.4) by mtkcas07.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.0.1395.4 via Frontend Transport; Tue, 7 Jan 2020 15:01:09 +0800 From: Roger Lu To: Kevin Hilman , Rob Herring , Nicolas Boichat , Stephen Boyd Subject: [PATCH v6 3/3] PM / AVS: SVS: Introduce SVS engine Date: Tue, 7 Jan 2020 15:01:54 +0800 Message-ID: <20200107070154.1574-4-roger.lu@mediatek.com> X-Mailer: git-send-email 2.18.0 In-Reply-To: <20200107070154.1574-1-roger.lu@mediatek.com> References: <20200107070154.1574-1-roger.lu@mediatek.com> MIME-Version: 1.0 X-MTK: N X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200106_230341_324911_0FA9C4AB X-CRM114-Status: GOOD ( 14.57 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Mark Rutland , Nishanth Menon , Angus Lin , devicetree@vger.kernel.org, linux-pm@vger.kernel.org, Roger Lu , linux-kernel@vger.kernel.org, Xiaoqing Liu , YT Lee , Fan Chen , linux-mediatek@lists.infradead.org, HenryC Chen , Charles Yang , Matthias Brugger , linux-arm-kernel@lists.infradead.org Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+infradead-linux-arm-kernel=archiver.kernel.org@lists.infradead.org The SVS (Smart Voltage Scaling) engine is a piece of hardware which is used to calculate optimized voltage values of several power domains, e.g. CPU/GPU/CCI, according to chip process corner, temperatures, and other factors. Then DVFS driver could apply those optimized voltage values to reduce power consumption. Signed-off-by: Roger Lu --- drivers/power/avs/Kconfig | 10 + drivers/power/avs/Makefile | 1 + drivers/power/avs/mtk_svs.c | 2075 +++++++++++++++++++++++++++++++++ include/linux/power/mtk_svs.h | 23 + 4 files changed, 2109 insertions(+) create mode 100644 drivers/power/avs/mtk_svs.c create mode 100644 include/linux/power/mtk_svs.h diff --git a/drivers/power/avs/Kconfig b/drivers/power/avs/Kconfig index 089b6244b716..ba9d296bbdc1 100644 --- a/drivers/power/avs/Kconfig +++ b/drivers/power/avs/Kconfig @@ -19,3 +19,13 @@ config ROCKCHIP_IODOMAIN Say y here to enable support io domains on Rockchip SoCs. It is necessary for the io domain setting of the SoC to match the voltage supplied by the regulators. + +config MTK_SVS + bool "MediaTek Smart Voltage Scaling(SVS)" + depends on POWER_AVS && MTK_EFUSE && NVMEM + help + The SVS engine is a piece of hardware which is used to calculate + optimized voltage values of several power domains, e.g. + CPU clusters/GPU/CCI, according to chip process corner, temperatures, + and other factors. Then DVFS driver could apply those optimized voltage + values to reduce power consumption. diff --git a/drivers/power/avs/Makefile b/drivers/power/avs/Makefile index a1b8cd453f19..57246b977a93 100644 --- a/drivers/power/avs/Makefile +++ b/drivers/power/avs/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_POWER_AVS_OMAP) += smartreflex.o obj-$(CONFIG_ROCKCHIP_IODOMAIN) += rockchip-io-domain.o +obj-$(CONFIG_MTK_SVS) += mtk_svs.o diff --git a/drivers/power/avs/mtk_svs.c b/drivers/power/avs/mtk_svs.c new file mode 100644 index 000000000000..c46211a15fcd --- /dev/null +++ b/drivers/power/avs/mtk_svs.c @@ -0,0 +1,2075 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 MediaTek Inc. + */ + +#define pr_fmt(fmt) "[mtk_svs] " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* svs sw id */ +#define SVS_CPU_LITTLE BIT(0) +#define SVS_CPU_BIG BIT(1) +#define SVS_CCI BIT(2) +#define SVS_GPU BIT(3) + +/* svs bank mode support */ +#define SVSB_MODE_ALL_DISABLE (0) +#define SVSB_MODE_INIT01 BIT(1) +#define SVSB_MODE_INIT02 BIT(2) +#define SVSB_MODE_MON BIT(3) + +/* svs bank init01 condition */ +#define SVSB_INIT01_VOLT_IGNORE BIT(1) +#define SVSB_INIT01_VOLT_INC_ONLY BIT(2) + +/* svs bank common setting */ +#define RUNCONFIG_DEFAULT (0x80000000) +#define DC_SIGNED_BIT (0x8000) +#define INTEN_INIT0x (0x00005f01) +#define INTEN_MONVOPEN (0x00ff0000) +#define SVSEN_OFF (0x0) +#define SVSEN_MASK (0x7) +#define SVSEN_INIT01 (0x1) +#define SVSEN_INIT02 (0x5) +#define SVSEN_MON (0x2) +#define INTSTS_MONVOP (0x00ff0000) +#define INTSTS_COMPLETE (0x1) +#define INTSTS_CLEAN (0x00ffffff) + +#define proc_fops_rw(name) \ + static int name ## _proc_open(struct inode *inode, \ + struct file *file) \ + { \ + return single_open(file, name ## _proc_show, \ + PDE_DATA(inode)); \ + } \ + static const struct file_operations name ## _proc_fops = { \ + .owner = THIS_MODULE, \ + .open = name ## _proc_open, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ + .write = name ## _proc_write, \ + } + +#define proc_fops_ro(name) \ + static int name ## _proc_open(struct inode *inode, \ + struct file *file) \ + { \ + return single_open(file, name ## _proc_show, \ + PDE_DATA(inode)); \ + } \ + static const struct file_operations name ## _proc_fops = { \ + .owner = THIS_MODULE, \ + .open = name ## _proc_open, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ + } + +#define proc_entry(name) {__stringify(name), &name ## _proc_fops} + +static DEFINE_SPINLOCK(mtk_svs_lock); +struct mtk_svs; + +enum svsb_phase { + SVSB_PHASE_INIT01 = 0, + SVSB_PHASE_INIT02, + SVSB_PHASE_MON, + SVSB_PHASE_ERROR, +}; + +enum reg_index { + TEMPMONCTL0 = 0, + TEMPMONCTL1, + TEMPMONCTL2, + TEMPMONINT, + TEMPMONINTSTS, + TEMPMONIDET0, + TEMPMONIDET1, + TEMPMONIDET2, + TEMPH2NTHRE, + TEMPHTHRE, + TEMPCTHRE, + TEMPOFFSETH, + TEMPOFFSETL, + TEMPMSRCTL0, + TEMPMSRCTL1, + TEMPAHBPOLL, + TEMPAHBTO, + TEMPADCPNP0, + TEMPADCPNP1, + TEMPADCPNP2, + TEMPADCMUX, + TEMPADCEXT, + TEMPADCEXT1, + TEMPADCEN, + TEMPPNPMUXADDR, + TEMPADCMUXADDR, + TEMPADCEXTADDR, + TEMPADCEXT1ADDR, + TEMPADCENADDR, + TEMPADCVALIDADDR, + TEMPADCVOLTADDR, + TEMPRDCTRL, + TEMPADCVALIDMASK, + TEMPADCVOLTAGESHIFT, + TEMPADCWRITECTRL, + TEMPMSR0, + TEMPMSR1, + TEMPMSR2, + TEMPADCHADDR, + TEMPIMMD0, + TEMPIMMD1, + TEMPIMMD2, + TEMPMONIDET3, + TEMPADCPNP3, + TEMPMSR3, + TEMPIMMD3, + TEMPPROTCTL, + TEMPPROTTA, + TEMPPROTTB, + TEMPPROTTC, + TEMPSPARE0, + TEMPSPARE1, + TEMPSPARE2, + TEMPSPARE3, + TEMPMSR0_1, + TEMPMSR1_1, + TEMPMSR2_1, + TEMPMSR3_1, + DESCHAR, + TEMPCHAR, + DETCHAR, + AGECHAR, + DCCONFIG, + AGECONFIG, + FREQPCT30, + FREQPCT74, + LIMITVALS, + VBOOT, + DETWINDOW, + CONFIG, + TSCALCS, + RUNCONFIG, + SVSEN, + INIT2VALS, + DCVALUES, + AGEVALUES, + VOP30, + VOP74, + TEMP, + INTSTS, + INTSTSRAW, + INTEN, + CHKINT, + CHKSHIFT, + STATUS, + VDESIGN30, + VDESIGN74, + DVT30, + DVT74, + AGECOUNT, + SMSTATE0, + SMSTATE1, + CTL0, + DESDETSEC, + TEMPAGESEC, + CTRLSPARE0, + CTRLSPARE1, + CTRLSPARE2, + CTRLSPARE3, + CORESEL, + THERMINTST, + INTST, + THSTAGE0ST, + THSTAGE1ST, + THSTAGE2ST, + THAHBST0, + THAHBST1, + SPARE0, + SPARE1, + SPARE2, + SPARE3, + THSLPEVEB, + reg_num, +}; + +static const u32 svs_regs_v2[] = { + [TEMPMONCTL0] = 0x000, + [TEMPMONCTL1] = 0x004, + [TEMPMONCTL2] = 0x008, + [TEMPMONINT] = 0x00c, + [TEMPMONINTSTS] = 0x010, + [TEMPMONIDET0] = 0x014, + [TEMPMONIDET1] = 0x018, + [TEMPMONIDET2] = 0x01c, + [TEMPH2NTHRE] = 0x024, + [TEMPHTHRE] = 0x028, + [TEMPCTHRE] = 0x02c, + [TEMPOFFSETH] = 0x030, + [TEMPOFFSETL] = 0x034, + [TEMPMSRCTL0] = 0x038, + [TEMPMSRCTL1] = 0x03c, + [TEMPAHBPOLL] = 0x040, + [TEMPAHBTO] = 0x044, + [TEMPADCPNP0] = 0x048, + [TEMPADCPNP1] = 0x04c, + [TEMPADCPNP2] = 0x050, + [TEMPADCMUX] = 0x054, + [TEMPADCEXT] = 0x058, + [TEMPADCEXT1] = 0x05c, + [TEMPADCEN] = 0x060, + [TEMPPNPMUXADDR] = 0x064, + [TEMPADCMUXADDR] = 0x068, + [TEMPADCEXTADDR] = 0x06c, + [TEMPADCEXT1ADDR] = 0x070, + [TEMPADCENADDR] = 0x074, + [TEMPADCVALIDADDR] = 0x078, + [TEMPADCVOLTADDR] = 0x07c, + [TEMPRDCTRL] = 0x080, + [TEMPADCVALIDMASK] = 0x084, + [TEMPADCVOLTAGESHIFT] = 0x088, + [TEMPADCWRITECTRL] = 0x08c, + [TEMPMSR0] = 0x090, + [TEMPMSR1] = 0x094, + [TEMPMSR2] = 0x098, + [TEMPADCHADDR] = 0x09c, + [TEMPIMMD0] = 0x0a0, + [TEMPIMMD1] = 0x0a4, + [TEMPIMMD2] = 0x0a8, + [TEMPMONIDET3] = 0x0b0, + [TEMPADCPNP3] = 0x0b4, + [TEMPMSR3] = 0x0b8, + [TEMPIMMD3] = 0x0bc, + [TEMPPROTCTL] = 0x0c0, + [TEMPPROTTA] = 0x0c4, + [TEMPPROTTB] = 0x0c8, + [TEMPPROTTC] = 0x0cc, + [TEMPSPARE0] = 0x0f0, + [TEMPSPARE1] = 0x0f4, + [TEMPSPARE2] = 0x0f8, + [TEMPSPARE3] = 0x0fc, + [TEMPMSR0_1] = 0x190, + [TEMPMSR1_1] = 0x194, + [TEMPMSR2_1] = 0x198, + [TEMPMSR3_1] = 0x1b8, + [DESCHAR] = 0xc00, + [TEMPCHAR] = 0xc04, + [DETCHAR] = 0xc08, + [AGECHAR] = 0xc0c, + [DCCONFIG] = 0xc10, + [AGECONFIG] = 0xc14, + [FREQPCT30] = 0xc18, + [FREQPCT74] = 0xc1c, + [LIMITVALS] = 0xc20, + [VBOOT] = 0xc24, + [DETWINDOW] = 0xc28, + [CONFIG] = 0xc2c, + [TSCALCS] = 0xc30, + [RUNCONFIG] = 0xc34, + [SVSEN] = 0xc38, + [INIT2VALS] = 0xc3c, + [DCVALUES] = 0xc40, + [AGEVALUES] = 0xc44, + [VOP30] = 0xc48, + [VOP74] = 0xc4c, + [TEMP] = 0xc50, + [INTSTS] = 0xc54, + [INTSTSRAW] = 0xc58, + [INTEN] = 0xc5c, + [CHKINT] = 0xc60, + [CHKSHIFT] = 0xc64, + [STATUS] = 0xc68, + [VDESIGN30] = 0xc6c, + [VDESIGN74] = 0xc70, + [DVT30] = 0xc74, + [DVT74] = 0xc78, + [AGECOUNT] = 0xc7c, + [SMSTATE0] = 0xc80, + [SMSTATE1] = 0xc84, + [CTL0] = 0xc88, + [DESDETSEC] = 0xce0, + [TEMPAGESEC] = 0xce4, + [CTRLSPARE0] = 0xcf0, + [CTRLSPARE1] = 0xcf4, + [CTRLSPARE2] = 0xcf8, + [CTRLSPARE3] = 0xcfc, + [CORESEL] = 0xf00, + [THERMINTST] = 0xf04, + [INTST] = 0xf08, + [THSTAGE0ST] = 0xf0c, + [THSTAGE1ST] = 0xf10, + [THSTAGE2ST] = 0xf14, + [THAHBST0] = 0xf18, + [THAHBST1] = 0xf1c, + [SPARE0] = 0xf20, + [SPARE1] = 0xf24, + [SPARE2] = 0xf28, + [SPARE3] = 0xf2c, + [THSLPEVEB] = 0xf30, +}; + +struct thermal_parameter { + int adc_ge_t; + int adc_oe_t; + int ge; + int oe; + int gain; + int o_vtsabb; + int o_vtsmcu1; + int o_vtsmcu2; + int o_vtsmcu3; + int o_vtsmcu4; + int o_vtsmcu5; + int degc_cali; + int adc_cali_en_t; + int o_slope; + int o_slope_sign; + int ts_id; +}; + +struct svs_bank_ops { + void (*set_freqs_pct)(struct mtk_svs *svs); + void (*get_vops)(struct mtk_svs *svs); +}; + +struct svs_bank { + struct svs_bank_ops *ops; + struct completion init_completion; + struct device *dev; + struct regulator *buck; + struct mutex lock; /* Lock to protect update voltage process */ + bool suspended; + bool mtcmos_request; + s32 volt_offset; + u32 mode_support; + u32 opp_freqs[16]; + u32 freqs_pct[16]; + u32 opp_volts[16]; + u32 init02_volts[16]; + u32 volts[16]; + u32 reg_data[3][reg_num]; + u32 freq_base; + u32 vboot; + u32 volt_step; + u32 volt_base; + u32 init01_volt_flag; + u32 phase; + u32 vmax; + u32 vmin; + u32 bts; + u32 mts; + u32 bdes; + u32 mdes; + u32 mtdes; + u32 dcbdet; + u32 dcmdet; + u32 dthi; + u32 dtlo; + u32 det_window; + u32 det_max; + u32 age_config; + u32 age_voffset_in; + u32 agem; + u32 dc_config; + u32 dc_voffset_in; + u32 dvt_fixed; + u32 vco; + u32 chkshift; + u32 svs_temp; + u32 upper_temp_bound; + u32 lower_temp_bound; + u32 low_temp_threashold; + u32 low_temp_offset; + u32 coresel; + u32 opp_count; + u32 intst; + u32 systemclk_en; + u32 sw_id; + u32 bank_id; + u32 ctl0; + u8 *of_compatible; + u8 *name; + u8 *zone_name; + u8 *buck_name; +}; + +struct svs_platform { + struct svs_bank *banks; + bool (*efuse_parsing)(struct mtk_svs *svs); + bool fake_efuse; + const u32 *regs; + u32 bank_num; + u32 efuse_num; + u32 efuse_check; + u32 thermal_efuse_num; + u8 *name; +}; + +struct mtk_svs { + const struct svs_platform *platform; + struct svs_bank *bank; + struct device *dev; + void __iomem *base; + struct clk *main_clk; + u32 *efuse; + u32 *thermal_efuse; +}; + +unsigned long claim_mtk_svs_lock(void) + __acquires(&mtk_svs_lock) +{ + unsigned long flags; + + spin_lock_irqsave(&mtk_svs_lock, flags); + + return flags; +} +EXPORT_SYMBOL_GPL(claim_mtk_svs_lock); + +void release_mtk_svs_lock(unsigned long flags) + __releases(&mtk_svs_lock) +{ + spin_unlock_irqrestore(&mtk_svs_lock, flags); +} +EXPORT_SYMBOL_GPL(release_mtk_svs_lock); + +static u32 percent(u32 numerator, u32 denominator) +{ + u32 percent; + + /* If not divide 1000, "numerator * 100" would be data overflow. */ + numerator /= 1000; + denominator /= 1000; + percent = ((numerator * 100) + denominator - 1) / denominator; + percent &= GENMASK(7, 0); + + return percent; +} + +static u32 svs_readl(struct mtk_svs *svs, enum reg_index rg_i) +{ + return readl(svs->base + svs->platform->regs[rg_i]); +} + +static void svs_writel(struct mtk_svs *svs, u32 val, enum reg_index rg_i) +{ + writel(val, svs->base + svs->platform->regs[rg_i]); +} + +static void svs_switch_bank(struct mtk_svs *svs) +{ + struct svs_bank *svsb = svs->bank; + + svs_writel(svs, svsb->coresel, CORESEL); +} + +static u32 svsb_volt_to_opp_volt(u32 svsb_volt, u32 svsb_volt_step, + u32 svsb_volt_base) +{ + u32 u_volt; + + u_volt = (svsb_volt * svsb_volt_step) + svsb_volt_base; + + return u_volt; +} + +static int svsb_get_zone_temperature(struct svs_bank *svsb, int *zone_temp) +{ + struct thermal_zone_device *tzd; + + tzd = thermal_zone_get_zone_by_name(svsb->zone_name); + if (IS_ERR(tzd)) + return PTR_ERR(tzd); + + return thermal_zone_get_temp(tzd, zone_temp); +} + +static int svsb_set_volts(struct svs_bank *svsb, bool force_update) +{ + u32 i, svsb_volt, opp_volt, low_temp_offset = 0; + int zone_temp, ret; + + mutex_lock(&svsb->lock); + + /* + * If bank is suspended, it means init02 voltage is applied. + * Don't need to update opp voltage anymore. + */ + if (svsb->suspended && !force_update) { + pr_notice("%s: bank is suspended\n", svsb->name); + mutex_unlock(&svsb->lock); + return -EPERM; + } + + /* Get thermal effect */ + if (svsb->phase == SVSB_PHASE_MON) { + if (svsb->svs_temp > svsb->upper_temp_bound && + svsb->svs_temp < svsb->lower_temp_bound) { + pr_err("%s: svs_temp is abnormal (0x%x)?\n", + svsb->name, svsb->svs_temp); + mutex_unlock(&svsb->lock); + return -EINVAL; + } + + ret = svsb_get_zone_temperature(svsb, &zone_temp); + if (ret) { + pr_err("%s: cannot get zone \"%s\" temperature(%d)\n", + svsb->name, svsb->zone_name, ret); + pr_err("%s: add low_temp_offset = %u\n", + svsb->name, svsb->low_temp_offset); + zone_temp = svsb->low_temp_threashold; + } + + if (zone_temp <= svsb->low_temp_threashold) + low_temp_offset = svsb->low_temp_offset; + } + + /* vmin <= svsb_volt (opp_volt) <= signed-off voltage */ + for (i = 0; i < svsb->opp_count; i++) { + if (svsb->phase == SVSB_PHASE_MON) { + svsb_volt = max((svsb->volts[i] + svsb->volt_offset + + low_temp_offset), svsb->vmin); + opp_volt = svsb_volt_to_opp_volt(svsb_volt, + svsb->volt_step, + svsb->volt_base); + } else if (svsb->phase == SVSB_PHASE_INIT02) { + svsb_volt = max((svsb->init02_volts[i] + + svsb->volt_offset), svsb->vmin); + opp_volt = svsb_volt_to_opp_volt(svsb_volt, + svsb->volt_step, + svsb->volt_base); + } else if (svsb->phase == SVSB_PHASE_ERROR) { + opp_volt = svsb->opp_volts[i]; + } else { + pr_err("%s: unknown phase: %u?\n", + svsb->name, svsb->phase); + mutex_unlock(&svsb->lock); + return -EINVAL; + } + + opp_volt = min(opp_volt, svsb->opp_volts[i]); + ret = dev_pm_opp_adjust_voltage(svsb->dev, svsb->opp_freqs[i], + opp_volt, opp_volt, + svsb->opp_volts[i]); + if (ret) { + pr_err("%s: set voltage failed: %d\n", svsb->name, ret); + mutex_unlock(&svsb->lock); + return ret; + } + } + + mutex_unlock(&svsb->lock); + + return 0; +} + +static u32 interpolate(u32 f0, u32 f1, u32 v0, u32 v1, u32 fx) +{ + u32 vy; + + if (v0 == v1 || f0 == f1) + return v0; + + /* *100 to have decimal fraction factor, +99 for rounding up. */ + vy = (v0 * 100) - ((((v0 - v1) * 100) / (f0 - f1)) * (f0 - fx)); + vy = (vy + 99) / 100; + + return vy; +} + +static void svs_get_vops_v2(struct mtk_svs *svs) +{ + struct svs_bank *svsb = svs->bank; + u32 temp, i; + + temp = svs_readl(svs, VOP30); + svsb->volts[6] = (temp >> 24) & GENMASK(7, 0); + svsb->volts[4] = (temp >> 16) & GENMASK(7, 0); + svsb->volts[2] = (temp >> 8) & GENMASK(7, 0); + svsb->volts[0] = (temp & GENMASK(7, 0)); + + temp = svs_readl(svs, VOP74); + svsb->volts[14] = (temp >> 24) & GENMASK(7, 0); + svsb->volts[12] = (temp >> 16) & GENMASK(7, 0); + svsb->volts[10] = (temp >> 8) & GENMASK(7, 0); + svsb->volts[8] = (temp & GENMASK(7, 0)); + + for (i = 0; i <= 7; i++) { + if (i < 7) { + svsb->volts[(i * 2) + 1] = + interpolate(svsb->freqs_pct[i * 2], + svsb->freqs_pct[(i + 1) * 2], + svsb->volts[i * 2], + svsb->volts[(i + 1) * 2], + svsb->freqs_pct[(i * 2) + 1]); + } else if (i == 7) { + svsb->volts[(i * 2) + 1] = + interpolate(svsb->freqs_pct[(i - 1) * 2], + svsb->freqs_pct[i * 2], + svsb->volts[(i - 1) * 2], + svsb->volts[i * 2], + svsb->freqs_pct[(i * 2) + 1]); + } + } +} + +static void svs_set_freqs_pct_v2(struct mtk_svs *svs) +{ + struct svs_bank *svsb = svs->bank; + + svs_writel(svs, + ((svsb->freqs_pct[6] << 24) & GENMASK(31, 24)) | + ((svsb->freqs_pct[4] << 16) & GENMASK(23, 16)) | + ((svsb->freqs_pct[2] << 8) & GENMASK(15, 8)) | + (svsb->freqs_pct[0] & GENMASK(7, 0)), + FREQPCT30); + svs_writel(svs, + ((svsb->freqs_pct[14] << 24) & GENMASK(31, 24)) | + ((svsb->freqs_pct[12] << 16) & GENMASK(23, 16)) | + ((svsb->freqs_pct[10] << 8) & GENMASK(15, 8)) | + ((svsb->freqs_pct[8]) & GENMASK(7, 0)), + FREQPCT74); +} + +static void svs_set_phase(struct mtk_svs *svs, u32 target_phase) +{ + struct svs_bank *svsb = svs->bank; + u32 des_char, temp_char, det_char, limit_vals; + u32 init2vals, ts_calcs, val, filter, i; + + svs_switch_bank(svs); + + des_char = ((svsb->bdes << 8) & GENMASK(15, 8)) | + (svsb->mdes & GENMASK(7, 0)); + svs_writel(svs, des_char, DESCHAR); + + temp_char = ((svsb->vco << 16) & GENMASK(23, 16)) | + ((svsb->mtdes << 8) & GENMASK(15, 8)) | + (svsb->dvt_fixed & GENMASK(7, 0)); + svs_writel(svs, temp_char, TEMPCHAR); + + det_char = ((svsb->dcbdet << 8) & GENMASK(15, 8)) | + (svsb->dcmdet & GENMASK(7, 0)); + svs_writel(svs, det_char, DETCHAR); + + svs_writel(svs, svsb->dc_config, DCCONFIG); + svs_writel(svs, svsb->age_config, AGECONFIG); + + if (svsb->agem == 0x0) { + svs_writel(svs, RUNCONFIG_DEFAULT, RUNCONFIG); + } else { + val = 0x0; + + for (i = 0; i < 24; i += 2) { + filter = 0x3 << i; + + if ((svsb->age_config & filter) == 0x0) + val |= (0x1 << i); + else + val |= (svsb->age_config & filter); + } + svs_writel(svs, val, RUNCONFIG); + } + + svsb->ops->set_freqs_pct(svs); + + limit_vals = ((svsb->vmax << 24) & GENMASK(31, 24)) | + ((svsb->vmin << 16) & GENMASK(23, 16)) | + ((svsb->dthi << 8) & GENMASK(15, 8)) | + (svsb->dtlo & GENMASK(7, 0)); + svs_writel(svs, limit_vals, LIMITVALS); + svs_writel(svs, (svsb->vboot & GENMASK(7, 0)), VBOOT); + svs_writel(svs, (svsb->det_window & GENMASK(15, 0)), DETWINDOW); + svs_writel(svs, (svsb->det_max & GENMASK(15, 0)), CONFIG); + + if (svsb->chkshift != 0) + svs_writel(svs, (svsb->chkshift & GENMASK(7, 0)), CHKSHIFT); + + if (svsb->ctl0 != 0) + svs_writel(svs, svsb->ctl0, CTL0); + + svs_writel(svs, INTSTS_CLEAN, INTSTS); + + switch (target_phase) { + case SVSB_PHASE_INIT01: + svs_writel(svs, INTEN_INIT0x, INTEN); + svs_writel(svs, SVSEN_INIT01, SVSEN); + break; + case SVSB_PHASE_INIT02: + svs_writel(svs, INTEN_INIT0x, INTEN); + init2vals = ((svsb->age_voffset_in << 16) & GENMASK(31, 16)) | + (svsb->dc_voffset_in & GENMASK(15, 0)); + svs_writel(svs, init2vals, INIT2VALS); + svs_writel(svs, SVSEN_INIT02, SVSEN); + break; + case SVSB_PHASE_MON: + ts_calcs = ((svsb->bts << 12) & GENMASK(23, 12)) | + (svsb->mts & GENMASK(11, 0)); + svs_writel(svs, ts_calcs, TSCALCS); + svs_writel(svs, INTEN_MONVOPEN, INTEN); + svs_writel(svs, SVSEN_MON, SVSEN); + break; + default: + WARN_ON(1); + break; + } +} + +static inline void svs_init01_isr_handler(struct mtk_svs *svs) +{ + struct svs_bank *svsb = svs->bank; + enum reg_index rg_i; + + pr_notice("%s: %s: VDN74:0x%08x, VDN30:0x%08x, DCVALUES:0x%08x\n", + svsb->name, __func__, svs_readl(svs, VDESIGN74), + svs_readl(svs, VDESIGN30), svs_readl(svs, DCVALUES)); + + for (rg_i = TEMPMONCTL0; rg_i < reg_num; rg_i++) + svsb->reg_data[SVSB_PHASE_INIT01][rg_i] = svs_readl(svs, rg_i); + + svsb->dc_voffset_in = ~(svs_readl(svs, DCVALUES) & GENMASK(15, 0)) + 1; + if (svsb->init01_volt_flag == SVSB_INIT01_VOLT_IGNORE) + svsb->dc_voffset_in = 0; + else if ((svsb->dc_voffset_in & DC_SIGNED_BIT) && + (svsb->init01_volt_flag == SVSB_INIT01_VOLT_INC_ONLY)) + svsb->dc_voffset_in = 0; + + svsb->age_voffset_in = svs_readl(svs, AGEVALUES) & GENMASK(15, 0); + + svs_writel(svs, SVSEN_OFF, SVSEN); + svs_writel(svs, INTSTS_COMPLETE, INTSTS); + + /* svs init01 clock gating */ + svsb->coresel &= ~svsb->systemclk_en; + + svsb->phase = SVSB_PHASE_INIT01; + complete(&svsb->init_completion); +} + +static inline void svs_init02_isr_handler(struct mtk_svs *svs) +{ + struct svs_bank *svsb = svs->bank; + enum reg_index rg_i; + + pr_notice("%s: %s: VOP74:0x%08x, VOP30:0x%08x, DCVALUES:0x%08x\n", + svsb->name, __func__, svs_readl(svs, VOP74), + svs_readl(svs, VOP30), svs_readl(svs, DCVALUES)); + + for (rg_i = TEMPMONCTL0; rg_i < reg_num; rg_i++) + svsb->reg_data[SVSB_PHASE_INIT02][rg_i] = svs_readl(svs, rg_i); + + svsb->ops->get_vops(svs); + memcpy(svsb->init02_volts, svsb->volts, sizeof(u32) * svsb->opp_count); + svsb->phase = SVSB_PHASE_INIT02; + + svs_writel(svs, SVSEN_OFF, SVSEN); + svs_writel(svs, INTSTS_COMPLETE, INTSTS); + + complete(&svsb->init_completion); +} + +static inline void svs_mon_mode_isr_handler(struct mtk_svs *svs) +{ + struct svs_bank *svsb = svs->bank; + enum reg_index rg_i; + + for (rg_i = TEMPMONCTL0; rg_i < reg_num; rg_i++) + svsb->reg_data[SVSB_PHASE_MON][rg_i] = svs_readl(svs, rg_i); + + svsb->svs_temp = svs_readl(svs, TEMP) & GENMASK(7, 0); + + svsb->ops->get_vops(svs); + svsb->phase = SVSB_PHASE_MON; + + svs_writel(svs, INTSTS_MONVOP, INTSTS); +} + +static inline void svs_error_isr_handler(struct mtk_svs *svs) +{ + const struct svs_platform *svsp = svs->platform; + struct svs_bank *svsb = svs->bank; + enum reg_index rg_i; + + pr_err("%s(): %s(%s)", __func__, svsp->name, svsb->name); + pr_err("CORESEL(0x%x) = 0x%08x\n", + svsp->regs[CORESEL], svs_readl(svs, CORESEL)), + pr_err("SVSEN(0x%x) = 0x%08x, INTSTS(0x%x) = 0x%08x\n", + svsp->regs[SVSEN], svs_readl(svs, SVSEN), + svsp->regs[INTSTS], svs_readl(svs, INTSTS)); + pr_err("SMSTATE0(0x%x) = 0x%08x, SMSTATE1(0x%x) = 0x%08x\n", + svsp->regs[SMSTATE0], svs_readl(svs, SMSTATE0), + svsp->regs[SMSTATE1], svs_readl(svs, SMSTATE1)); + + for (rg_i = TEMPMONCTL0; rg_i < reg_num; rg_i++) + svsb->reg_data[SVSB_PHASE_MON][rg_i] = svs_readl(svs, rg_i); + + svsb->mode_support = SVSB_MODE_ALL_DISABLE; + + if (svsb->phase == SVSB_PHASE_MON) + svsb->phase = SVSB_PHASE_INIT02; + + svs_writel(svs, SVSEN_OFF, SVSEN); + svs_writel(svs, INTSTS_CLEAN, INTSTS); +} + +static inline void svs_isr_handler(struct mtk_svs *svs) +{ + u32 intsts, svsen; + + svs_switch_bank(svs); + + intsts = svs_readl(svs, INTSTS); + svsen = svs_readl(svs, SVSEN); + + if (intsts == INTSTS_COMPLETE && + ((svsen & SVSEN_MASK) == SVSEN_INIT01)) + svs_init01_isr_handler(svs); + else if ((intsts == INTSTS_COMPLETE) && + ((svsen & SVSEN_MASK) == SVSEN_INIT02)) + svs_init02_isr_handler(svs); + else if (!!(intsts & INTSTS_MONVOP)) + svs_mon_mode_isr_handler(svs); + else + svs_error_isr_handler(svs); +} + +static irqreturn_t svs_isr(int irq, void *data) +{ + struct mtk_svs *svs = (struct mtk_svs *)data; + const struct svs_platform *svsp = svs->platform; + struct svs_bank *svsb = NULL; + unsigned long flags; + u32 idx; + + flags = claim_mtk_svs_lock(); + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + svs->bank = svsb; + + if (svsb->suspended) + continue; + else if (svsb->intst & svs_readl(svs, INTST)) + continue; + + svs_isr_handler(svs); + break; + } + release_mtk_svs_lock(flags); + + if (svsb->phase != SVSB_PHASE_INIT01) + svsb_set_volts(svsb, false); + + return IRQ_HANDLED; +} + +static void svs_mon_mode(struct mtk_svs *svs) +{ + const struct svs_platform *svsp = svs->platform; + struct svs_bank *svsb; + unsigned long flags; + u32 idx; + + flags = claim_mtk_svs_lock(); + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + svs->bank = svsb; + + if (!(svsb->mode_support & SVSB_MODE_MON)) + continue; + + svs_set_phase(svs, SVSB_PHASE_MON); + } + release_mtk_svs_lock(flags); +} + +static int svs_init02(struct mtk_svs *svs) +{ + const struct svs_platform *svsp = svs->platform; + struct svs_bank *svsb; + unsigned long flags, time_left; + u32 idx; + + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + svs->bank = svsb; + + if (!(svsb->mode_support & SVSB_MODE_INIT02)) + continue; + + reinit_completion(&svsb->init_completion); + flags = claim_mtk_svs_lock(); + svs_set_phase(svs, SVSB_PHASE_INIT02); + release_mtk_svs_lock(flags); + time_left = + wait_for_completion_timeout(&svsb->init_completion, + msecs_to_jiffies(2000)); + if (time_left == 0) { + pr_err("%s: init02 completion timeout\n", svsb->name); + return -EBUSY; + } + } + + return 0; +} + +static int svs_init01(struct mtk_svs *svs) +{ + const struct svs_platform *svsp = svs->platform; + struct svs_bank *svsb; + struct pm_qos_request *qos_request; + unsigned long flags, time_left; + bool search_done; + int ret = -EINVAL; + u32 opp_freqs, opp_vboot, buck_volt, idx, i; + + qos_request = kzalloc(sizeof(*qos_request), GFP_KERNEL); + if (!qos_request) + return -ENOMEM; + + /* Let CPUs leave idle-off state for initializing svs_init01. */ + pm_qos_add_request(qos_request, PM_QOS_CPU_DMA_LATENCY, 0); + + /* + * Sometimes two svs banks use the same buck. + * Therefore, we set each svs bank to vboot voltage first. + */ + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + search_done = false; + + if (!(svsb->mode_support & SVSB_MODE_INIT01)) + continue; + + ret = regulator_set_mode(svsb->buck, REGULATOR_MODE_FAST); + if (ret) + pr_notice("%s: fail to set fast mode: %d\n", + svsb->name, ret); + + if (svsb->mtcmos_request) { + ret = regulator_enable(svsb->buck); + if (ret) { + pr_err("%s: fail to enable %s power: %d\n", + svsb->name, svsb->buck_name, ret); + goto init01_finish; + } + + ret = dev_pm_domain_attach(svsb->dev, false); + if (ret) { + pr_err("%s: attach pm domain fail: %d\n", + svsb->name, ret); + goto init01_finish; + } + + pm_runtime_enable(svsb->dev); + ret = pm_runtime_get_sync(svsb->dev); + if (ret < 0) { + pr_err("%s: turn mtcmos on fail: %d\n", + svsb->name, ret); + goto init01_finish; + } + } + + /* + * Find the fastest freq that can be run at vboot and + * fix to that freq until svs_init01 is done. + */ + opp_vboot = svsb_volt_to_opp_volt(svsb->vboot, + svsb->volt_step, + svsb->volt_base); + + for (i = 0; i < svsb->opp_count; i++) { + opp_freqs = svsb->opp_freqs[i]; + if (!search_done && svsb->opp_volts[i] <= opp_vboot) { + ret = dev_pm_opp_adjust_voltage(svsb->dev, + opp_freqs, + opp_vboot, + opp_vboot, + opp_vboot); + if (ret) { + pr_err("%s: set voltage failed: %d\n", + svsb->name, ret); + goto init01_finish; + } + + search_done = true; + } else { + dev_pm_opp_disable(svsb->dev, + svsb->opp_freqs[i]); + } + } + } + + /* svs bank init01 begins */ + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + svs->bank = svsb; + + if (!(svsb->mode_support & SVSB_MODE_INIT01)) + continue; + + opp_vboot = svsb_volt_to_opp_volt(svsb->vboot, + svsb->volt_step, + svsb->volt_base); + + buck_volt = regulator_get_voltage(svsb->buck); + if (buck_volt != opp_vboot) { + pr_err("%s: buck voltage: %u, expected vboot: %u\n", + svsb->name, buck_volt, opp_vboot); + ret = -EPERM; + goto init01_finish; + } + + init_completion(&svsb->init_completion); + flags = claim_mtk_svs_lock(); + svs_set_phase(svs, SVSB_PHASE_INIT01); + release_mtk_svs_lock(flags); + time_left = + wait_for_completion_timeout(&svsb->init_completion, + msecs_to_jiffies(2000)); + if (time_left == 0) { + pr_err("%s: init01 completion timeout\n", svsb->name); + ret = -EBUSY; + goto init01_finish; + } + } + +init01_finish: + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + + if (!(svsb->mode_support & SVSB_MODE_INIT01)) + continue; + + for (i = 0; i < svsb->opp_count; i++) + dev_pm_opp_enable(svsb->dev, svsb->opp_freqs[i]); + + if (regulator_set_mode(svsb->buck, REGULATOR_MODE_NORMAL)) + pr_notice("%s: fail to set normal mode: %d\n", + svsb->name, ret); + + if (svsb->mtcmos_request) { + if (pm_runtime_put_sync(svsb->dev)) + pr_err("%s: turn mtcmos off fail: %d\n", + svsb->name, ret); + pm_runtime_disable(svsb->dev); + dev_pm_domain_detach(svsb->dev, 0); + if (regulator_disable(svsb->buck)) + pr_err("%s: fail to disable %s power: %d\n", + svsb->name, svsb->buck_name, ret); + } + } + + pm_qos_remove_request(qos_request); + kfree(qos_request); + + return ret; +} + +static int svs_start(struct mtk_svs *svs) +{ + int ret; + + ret = svs_init01(svs); + if (ret) + return ret; + + ret = svs_init02(svs); + if (ret) + return ret; + + svs_mon_mode(svs); + + return ret; +} + +static bool svs_mt8183_efuse_parsing(struct mtk_svs *svs) +{ + const struct svs_platform *svsp = svs->platform; + struct thermal_parameter tp; + struct svs_bank *svsb; + bool mon_mode_support = true; + int format[6], x_roomt[6], tb_roomt = 0; + u32 idx, i, ft_pgm, mts, temp0, temp1, temp2; + + if (svsp->fake_efuse) { + pr_notice("fake efuse\n"); + svs->efuse[0] = 0x00310080; + svs->efuse[1] = 0xabfbf757; + svs->efuse[2] = 0x47c747c7; + svs->efuse[3] = 0xabfbf757; + svs->efuse[4] = 0xe7fca0ec; + svs->efuse[5] = 0x47bf4b88; + svs->efuse[6] = 0xabfb8fa5; + svs->efuse[7] = 0xabfb217b; + svs->efuse[8] = 0x4bf34be1; + svs->efuse[9] = 0xabfb670d; + svs->efuse[16] = 0xabfbc653; + svs->efuse[17] = 0x47f347e1; + svs->efuse[18] = 0xabfbd848; + + svs->thermal_efuse[0] = 0x02873f69; + svs->thermal_efuse[1] = 0xa11d9142; + svs->thermal_efuse[2] = 0xa2526900; + } + + for (i = 0; i < svsp->efuse_num; i++) { + if (svs->efuse[i]) + pr_notice("M_HW_RES%d: 0x%08x\n", i, svs->efuse[i]); + } + + /* svs efuse parsing */ + ft_pgm = (svs->efuse[0] >> 4) & 0xf; + + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + if (ft_pgm <= 1) + svsb->init01_volt_flag = SVSB_INIT01_VOLT_IGNORE; + + switch (svsb->sw_id) { + case SVS_CPU_LITTLE: + svsb->bdes = svs->efuse[16] & GENMASK(7, 0); + svsb->mdes = (svs->efuse[16] >> 8) & GENMASK(7, 0); + svsb->dcbdet = (svs->efuse[16] >> 16) & GENMASK(7, 0); + svsb->dcmdet = (svs->efuse[16] >> 24) & GENMASK(7, 0); + svsb->mtdes = (svs->efuse[17] >> 16) & GENMASK(7, 0); + + if (ft_pgm <= 3) + svsb->volt_offset += 10; + else + svsb->volt_offset += 2; + break; + case SVS_CPU_BIG: + svsb->bdes = svs->efuse[18] & GENMASK(7, 0); + svsb->mdes = (svs->efuse[18] >> 8) & GENMASK(7, 0); + svsb->dcbdet = (svs->efuse[18] >> 16) & GENMASK(7, 0); + svsb->dcmdet = (svs->efuse[18] >> 24) & GENMASK(7, 0); + svsb->mtdes = svs->efuse[17] & GENMASK(7, 0); + + if (ft_pgm <= 3) + svsb->volt_offset += 15; + else + svsb->volt_offset += 12; + break; + case SVS_CCI: + svsb->bdes = svs->efuse[4] & GENMASK(7, 0); + svsb->mdes = (svs->efuse[4] >> 8) & GENMASK(7, 0); + svsb->dcbdet = (svs->efuse[4] >> 16) & GENMASK(7, 0); + svsb->dcmdet = (svs->efuse[4] >> 24) & GENMASK(7, 0); + svsb->mtdes = (svs->efuse[5] >> 16) & GENMASK(7, 0); + + if (ft_pgm <= 3) + svsb->volt_offset += 10; + else + svsb->volt_offset += 2; + break; + case SVS_GPU: + svsb->bdes = svs->efuse[6] & GENMASK(7, 0); + svsb->mdes = (svs->efuse[6] >> 8) & GENMASK(7, 0); + svsb->dcbdet = (svs->efuse[6] >> 16) & GENMASK(7, 0); + svsb->dcmdet = (svs->efuse[6] >> 24) & GENMASK(7, 0); + svsb->mtdes = svs->efuse[5] & GENMASK(7, 0); + + if (ft_pgm >= 2) { + svsb->freq_base = 800000000; /* 800MHz */ + svsb->dvt_fixed = 2; + } + break; + default: + break; + } + } + + /* Thermal efuse parsing */ + if (!svs->thermal_efuse) + return true; + + tp.adc_ge_t = (svs->thermal_efuse[1] >> 22) & GENMASK(9, 0); + tp.adc_oe_t = (svs->thermal_efuse[1] >> 12) & GENMASK(9, 0); + + tp.o_vtsmcu1 = (svs->thermal_efuse[0] >> 17) & GENMASK(8, 0); + tp.o_vtsmcu2 = (svs->thermal_efuse[0] >> 8) & GENMASK(8, 0); + tp.o_vtsmcu3 = svs->thermal_efuse[1] & GENMASK(8, 0); + tp.o_vtsmcu4 = (svs->thermal_efuse[2] >> 23) & GENMASK(8, 0); + tp.o_vtsmcu5 = (svs->thermal_efuse[2] >> 5) & GENMASK(8, 0); + tp.o_vtsabb = (svs->thermal_efuse[2] >> 14) & GENMASK(8, 0); + + tp.degc_cali = (svs->thermal_efuse[0] >> 1) & GENMASK(5, 0); + tp.adc_cali_en_t = svs->thermal_efuse[0] & BIT(0); + tp.o_slope_sign = (svs->thermal_efuse[0] >> 7) & BIT(0); + + tp.ts_id = (svs->thermal_efuse[1] >> 9) & BIT(0); + tp.o_slope = (svs->thermal_efuse[0] >> 26) & GENMASK(5, 0); + + if (tp.adc_cali_en_t == 1) { + if (tp.ts_id == 0) + tp.o_slope = 0; + + if ((tp.adc_ge_t < 265 || tp.adc_ge_t > 758) || + (tp.adc_oe_t < 265 || tp.adc_oe_t > 758) || + (tp.o_vtsmcu1 < -8 || tp.o_vtsmcu1 > 484) || + (tp.o_vtsmcu2 < -8 || tp.o_vtsmcu2 > 484) || + (tp.o_vtsmcu3 < -8 || tp.o_vtsmcu3 > 484) || + (tp.o_vtsmcu4 < -8 || tp.o_vtsmcu4 > 484) || + (tp.o_vtsmcu5 < -8 || tp.o_vtsmcu5 > 484) || + (tp.o_vtsabb < -8 || tp.o_vtsabb > 484) || + (tp.degc_cali < 1 || tp.degc_cali > 63)) { + pr_err("bad thermal efuse data. disable mon mode\n"); + mon_mode_support = false; + } + } else { + pr_err("no thermal efuse data. disable mon mode\n"); + mon_mode_support = false; + } + + if (!mon_mode_support) { + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + svsb->mode_support &= ~SVSB_MODE_MON; + } + + return true; + } + + tp.ge = ((tp.adc_ge_t - 512) * 10000) / 4096; + tp.oe = (tp.adc_oe_t - 512); + tp.gain = (10000 + tp.ge); + + format[0] = (tp.o_vtsmcu1 + 3350 - tp.oe); + format[1] = (tp.o_vtsmcu2 + 3350 - tp.oe); + format[2] = (tp.o_vtsmcu3 + 3350 - tp.oe); + format[3] = (tp.o_vtsmcu4 + 3350 - tp.oe); + format[4] = (tp.o_vtsmcu5 + 3350 - tp.oe); + format[5] = (tp.o_vtsabb + 3350 - tp.oe); + + for (i = 0; i < 6; i++) + x_roomt[i] = (((format[i] * 10000) / 4096) * 10000) / tp.gain; + + temp0 = (10000 * 100000 / tp.gain) * 15 / 18; + + if (tp.o_slope_sign == 0) + mts = (temp0 * 10) / (1534 + tp.o_slope * 10); + else + mts = (temp0 * 10) / (1534 - tp.o_slope * 10); + + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + svsb->mts = mts; + + switch (svsb->sw_id) { + case SVS_CPU_LITTLE: + tb_roomt = x_roomt[3]; + break; + case SVS_CPU_BIG: + tb_roomt = x_roomt[4]; + break; + case SVS_CCI: + tb_roomt = x_roomt[3]; + break; + case SVS_GPU: + tb_roomt = x_roomt[1]; + break; + default: + break; + } + + temp0 = (tp.degc_cali * 10 / 2); + temp1 = ((10000 * 100000 / 4096 / tp.gain) * + tp.oe + tb_roomt * 10) * 15 / 18; + + if (tp.o_slope_sign == 0) + temp2 = temp1 * 100 / (1534 + tp.o_slope * 10); + else + temp2 = temp1 * 100 / (1534 - tp.o_slope * 10); + + svsb->bts = (temp0 + temp2 - 250) * 4 / 10; + } + + return true; +} + +static bool svs_is_supported(struct mtk_svs *svs) +{ + const struct svs_platform *svsp = svs->platform; + struct svs_bank *svsb; + struct nvmem_cell *cell; + size_t len; + bool ret; + u32 idx; + + if (svsp->fake_efuse) { + len = svsp->efuse_num * 4; + svs->efuse = devm_kzalloc(svs->dev, len, GFP_KERNEL); + if (!svs->efuse) { + pr_err("no memory for allocating svs_efuse\n"); + return false; + } + + len = svsp->thermal_efuse_num * 4; + svs->thermal_efuse = devm_kzalloc(svs->dev, len, GFP_KERNEL); + if (!svs->thermal_efuse) { + pr_err("no memory for allocating svs_thermal_efuse\n"); + return false; + } + + goto svsp_efuse_parsing; + } + + /* Get svs efuse by nvmem */ + cell = nvmem_cell_get(svs->dev, "svs-calibration-data"); + if (IS_ERR(cell)) { + pr_err("no \"svs-calibration-data\" from dts? disable svs\n"); + return false; + } + + svs->efuse = (u32 *)nvmem_cell_read(cell, &len); + nvmem_cell_put(cell); + + if (svs->efuse[svsp->efuse_check] == 0) { + pr_err("svs_efuse[%u] is empty\n", svsp->efuse_check); + return false; + } + + /* Get thermal efuse by nvmem */ + cell = nvmem_cell_get(svs->dev, "calibration-data"); + if (IS_ERR(cell)) { + pr_err("no \"calibration-data\" from dts? disable mon mode\n"); + svs->thermal_efuse = NULL; + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + svsb->mode_support &= ~SVSB_MODE_MON; + } + goto svsp_efuse_parsing; + } + + svs->thermal_efuse = (u32 *)nvmem_cell_read(cell, &len); + nvmem_cell_put(cell); + +svsp_efuse_parsing: + ret = svsp->efuse_parsing(svs); + + return ret; +} + +static int svs_resource_setup(struct mtk_svs *svs) +{ + const struct svs_platform *svsp = svs->platform; + struct svs_bank *svsb; + struct platform_device *pdev; + struct device_node *np = NULL; + struct dev_pm_opp *opp; + unsigned long freq; + int count, ret; + u32 idx, i; + + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + + switch (svsb->sw_id) { + case SVS_CPU_LITTLE: + svsb->name = "SVS_CPU_LITTLE"; + break; + case SVS_CPU_BIG: + svsb->name = "SVS_CPU_BIG"; + break; + case SVS_CCI: + svsb->name = "SVS_CCI"; + break; + case SVS_GPU: + svsb->name = "SVS_GPU"; + break; + default: + WARN_ON(1); + return -EINVAL; + } + + /* Add svs bank device for opp-table/mtcmos/buck control */ + pdev = platform_device_alloc(svsb->name, 0); + if (!pdev) { + pr_err("%s: fail to alloc pdev for svs_bank\n", + svsb->name); + return -ENOMEM; + } + + for_each_child_of_node(svs->dev->of_node, np) { + if (of_device_is_compatible(np, svsb->of_compatible)) { + pdev->dev.of_node = np; + break; + } + } + + ret = platform_device_add(pdev); + if (ret) { + pr_err("%s: fail to add svs_bank device: %d\n", + svsb->name, ret); + return ret; + } + + svsb->dev = &pdev->dev; + dev_set_drvdata(svsb->dev, svs); + ret = dev_pm_opp_of_add_table(svsb->dev); + if (ret) { + pr_err("%s: fail to add opp table: %d\n", + svsb->name, ret); + return ret; + } + + mutex_init(&svsb->lock); + + svsb->buck = devm_regulator_get_optional(svsb->dev, + svsb->buck_name); + if (IS_ERR(svsb->buck)) { + pr_err("%s: cannot get regulator \"%s-supply\"\n", + svsb->name, svsb->buck_name); + return PTR_ERR(svsb->buck); + } + + count = dev_pm_opp_get_opp_count(svsb->dev); + if (svsb->opp_count != count) { + pr_err("%s: opp_count not \"%u\" but get \"%d\"?\n", + svsb->name, svsb->opp_count, count); + return count; + } + + for (i = 0, freq = (u32)-1; i < svsb->opp_count; i++, freq--) { + opp = dev_pm_opp_find_freq_floor(svsb->dev, &freq); + if (IS_ERR(opp)) { + pr_err("%s: error opp entry!!, err = %ld\n", + svsb->name, PTR_ERR(opp)); + return PTR_ERR(opp); + } + + svsb->opp_freqs[i] = freq; + svsb->opp_volts[i] = dev_pm_opp_get_voltage(opp); + svsb->freqs_pct[i] = percent(svsb->opp_freqs[i], + svsb->freq_base); + dev_pm_opp_put(opp); + } + } + + return 0; +} + +static int svs_suspend(struct device *dev) +{ + struct mtk_svs *svs = dev_get_drvdata(dev); + const struct svs_platform *svsp = svs->platform; + struct svs_bank *svsb; + unsigned long flags; + u32 idx; + + /* Wait if there is processing svs_isr(). Suspend all banks. */ + flags = claim_mtk_svs_lock(); + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + svs->bank = svsb; + svs_switch_bank(svs); + svs_writel(svs, SVSEN_OFF, SVSEN); + svs_writel(svs, INTSTS_CLEAN, INTSTS); + svsb->suspended = true; + } + release_mtk_svs_lock(flags); + + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + if (svsb->phase == SVSB_PHASE_MON) { + svsb->phase = SVSB_PHASE_INIT02; + svsb_set_volts(svsb, true); + } + } + + clk_disable_unprepare(svs->main_clk); + + return 0; +} + +static int svs_resume(struct device *dev) +{ + struct mtk_svs *svs = dev_get_drvdata(dev); + const struct svs_platform *svsp = svs->platform; + struct svs_bank *svsb; + int ret; + u32 idx; + + ret = clk_prepare_enable(svs->main_clk); + if (ret) + pr_err("%s(): cannot enable main_clk\n", __func__); + + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + svsb->suspended = false; + } + + ret = svs_init02(svs); + if (ret) + return ret; + + svs_mon_mode(svs); + + return 0; +} + +static int svs_debug_proc_show(struct seq_file *m, void *v) +{ + struct svs_bank *svsb = (struct svs_bank *)m->private; + + if (svsb->phase == SVSB_PHASE_INIT01) + seq_puts(m, "init1\n"); + else if (svsb->phase == SVSB_PHASE_INIT02) + seq_puts(m, "init2\n"); + else if (svsb->phase == SVSB_PHASE_MON) + seq_puts(m, "mon mode\n"); + else if (svsb->phase == SVSB_PHASE_ERROR) + seq_puts(m, "disabled\n"); + else + seq_puts(m, "unknown\n"); + + return 0; +} + +static ssize_t svs_debug_proc_write(struct file *file, + const char __user *buffer, + size_t count, loff_t *pos) +{ + struct svs_bank *svsb = (struct svs_bank *)PDE_DATA(file_inode(file)); + struct mtk_svs *svs = dev_get_drvdata(svsb->dev); + char *buf = (char *)__get_free_page(GFP_USER); + unsigned long flags; + int enabled, ret; + + if (svsb->phase == SVSB_PHASE_ERROR) + return count; + + if (!buf) + return -ENOMEM; + + if (count >= PAGE_SIZE) { + free_page((unsigned long)buf); + return -EINVAL; + } + + if (copy_from_user(buf, buffer, count)) { + free_page((unsigned long)buf); + return -EFAULT; + } + + buf[count] = '\0'; + + ret = kstrtoint(buf, 10, &enabled); + if (ret) + return ret; + + if (!enabled) { + flags = claim_mtk_svs_lock(); + svs->bank = svsb; + svsb->mode_support = SVSB_MODE_ALL_DISABLE; + svs_switch_bank(svs); + svs_writel(svs, SVSEN_OFF, SVSEN); + svs_writel(svs, INTSTS_CLEAN, INTSTS); + release_mtk_svs_lock(flags); + } + + svsb->phase = SVSB_PHASE_ERROR; + svsb_set_volts(svsb, true); + + return count; +} + +proc_fops_rw(svs_debug); + +static int svs_dump_proc_show(struct seq_file *m, void *v) +{ + struct mtk_svs *svs = (struct mtk_svs *)m->private; + const struct svs_platform *svsp = svs->platform; + struct svs_bank *svsb; + unsigned long svs_reg_addr; + u32 idx, i, j; + + for (i = 0; i < svsp->efuse_num; i++) { + if (svs->efuse[i]) + seq_printf(m, "M_HW_RES%d = 0x%08x\n", + i, svs->efuse[i]); + } + + for (i = 0; i < svsp->thermal_efuse_num; i++) { + if (svs->thermal_efuse && svs->thermal_efuse[i]) + seq_printf(m, "THERMAL_EFUSE%d = 0x%08x\n", + i, svs->thermal_efuse[i]); + } + + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + + for (i = SVSB_PHASE_INIT01; i <= SVSB_PHASE_MON; i++) { + seq_printf(m, "Bank_number = %u\n", svsb->bank_id); + + if (i < SVSB_PHASE_MON) + seq_printf(m, "mode = init%d\n", i + 1); + else + seq_puts(m, "mode = mon\n"); + + for (j = TEMPMONCTL0; j < reg_num; j++) { + svs_reg_addr = (unsigned long)(svs->base + + svsp->regs[j]); + seq_printf(m, "0x%08lx = 0x%08x\n", + svs_reg_addr, svsb->reg_data[i][j]); + } + } + } + + return 0; +} + +proc_fops_ro(svs_dump); + +static int svs_status_proc_show(struct seq_file *m, void *v) +{ + struct svs_bank *svsb = (struct svs_bank *)m->private; + struct dev_pm_opp *opp; + unsigned long freq; + int zone_temp, ret; + u32 i; + + ret = svsb_get_zone_temperature(svsb, &zone_temp); + if (ret) + seq_printf(m, "%s: cannot get zone \"%s\" temperature\n", + svsb->name, svsb->zone_name); + else + seq_printf(m, "%s: temperature = %d\n", svsb->name, zone_temp); + + for (i = 0, freq = (u32)-1; i < svsb->opp_count; i++, freq--) { + opp = dev_pm_opp_find_freq_floor(svsb->dev, &freq); + if (IS_ERR(opp)) { + seq_printf(m, "%s: error opp entry!!, err = %ld\n", + svsb->name, PTR_ERR(opp)); + return PTR_ERR(opp); + } + + seq_printf(m, "opp_freqs[%02u]: %lu, volts[%02u]: %lu, ", + i, freq, i, dev_pm_opp_get_voltage(opp)); + seq_printf(m, "svsb_volts[%02u]: 0x%x, freqs_pct[%02u]: %u\n", + i, svsb->volts[i], i, svsb->freqs_pct[i]); + dev_pm_opp_put(opp); + } + + return 0; +} + +proc_fops_ro(svs_status); + +static int svs_volt_offset_proc_show(struct seq_file *m, void *v) +{ + struct svs_bank *svsb = (struct svs_bank *)m->private; + + seq_printf(m, "%d\n", svsb->volt_offset); + + return 0; +} + +static ssize_t svs_volt_offset_proc_write(struct file *file, + const char __user *buffer, + size_t count, loff_t *pos) +{ + struct svs_bank *svsb = (struct svs_bank *)PDE_DATA(file_inode(file)); + char *buf = (char *)__get_free_page(GFP_USER); + int ret, volt_offset; + + if (!buf) + return -ENOMEM; + + if (count >= PAGE_SIZE) { + free_page((unsigned long)buf); + return -EINVAL; + } + + if (copy_from_user(buf, buffer, count)) { + free_page((unsigned long)buf); + return -EFAULT; + } + + buf[count] = '\0'; + + if (!kstrtoint(buf, 10, &volt_offset)) { + svsb->volt_offset = volt_offset; + ret = svsb_set_volts(svsb, true); + if (ret) + return ret; + } + + return count; +} + +proc_fops_rw(svs_volt_offset); + +static int svs_create_svs_procfs(struct mtk_svs *svs) +{ + const struct svs_platform *svsp = svs->platform; + struct svs_bank *svsb; + struct proc_dir_entry *svs_dir, *bank_dir; + u32 idx, i; + + struct pentry { + const char *name; + const struct file_operations *fops; + }; + + struct pentry svs_entries[] = { + proc_entry(svs_dump), + }; + + struct pentry bank_entries[] = { + proc_entry(svs_debug), + proc_entry(svs_status), + proc_entry(svs_volt_offset), + }; + + svs_dir = proc_mkdir("svs", NULL); + if (!svs_dir) { + pr_err("mkdir /proc/svs failed\n"); + return -EPERM; + } + + for (i = 0; i < ARRAY_SIZE(svs_entries); i++) { + if (!proc_create_data(svs_entries[i].name, 0664, + svs_dir, svs_entries[i].fops, svs)) { + pr_err("create /proc/svs/%s failed\n", + svs_entries[i].name); + return -EPERM; + } + } + + for (idx = 0; idx < svsp->bank_num; idx++) { + svsb = &svsp->banks[idx]; + + if (svsb->mode_support == SVSB_MODE_ALL_DISABLE) + continue; + + bank_dir = proc_mkdir(svsb->name, svs_dir); + if (!bank_dir) { + pr_err("mkdir /proc/svs/%s failed\n", svsb->name); + return -EPERM; + } + + for (i = 0; i < ARRAY_SIZE(bank_entries); i++) { + if (!proc_create_data(bank_entries[i].name, 0664, + bank_dir, bank_entries[i].fops, + svsb)) { + pr_err("create /proc/svs/%s/%s failed\n", + svsb->name, bank_entries[i].name); + return -EPERM; + } + } + } + + return 0; +} + +static struct svs_bank_ops svs_mt8183_banks_ops = { + .set_freqs_pct = svs_set_freqs_pct_v2, + .get_vops = svs_get_vops_v2, +}; + +static struct svs_bank svs_mt8183_banks[4] = { + { + .of_compatible = "mediatek,mt8183-svs-cpu-little", + .sw_id = SVS_CPU_LITTLE, + .bank_id = 0, + .ops = &svs_mt8183_banks_ops, + .zone_name = "tzts4", + .buck_name = "vcpu-little", + .mtcmos_request = false, + .init01_volt_flag = SVSB_INIT01_VOLT_INC_ONLY, + .mode_support = SVSB_MODE_INIT01 | SVSB_MODE_INIT02, + .opp_count = 16, + .freq_base = 1989000000, + .vboot = 0x30, + .volt_step = 6250, + .volt_base = 500000, + .volt_offset = 0, + .vmax = 0x64, + .vmin = 0x18, + .dthi = 0x1, + .dtlo = 0xfe, + .det_window = 0xa28, + .det_max = 0xffff, + .age_config = 0x555555, + .agem = 0x0, + .dc_config = 0x555555, + .dvt_fixed = 0x7, + .vco = 0x10, + .chkshift = 0x77, + .upper_temp_bound = 0x64, + .lower_temp_bound = 0xb2, + .low_temp_threashold = 25000, + .low_temp_offset = 0, + .coresel = 0x8fff0000, + .systemclk_en = BIT(31), + .intst = BIT(0), + .ctl0 = 0x00010001, + }, + { + .of_compatible = "mediatek,mt8183-svs-cpu-big", + .sw_id = SVS_CPU_BIG, + .bank_id = 1, + .ops = &svs_mt8183_banks_ops, + .zone_name = "tzts5", + .buck_name = "vcpu-big", + .mtcmos_request = false, + .init01_volt_flag = SVSB_INIT01_VOLT_INC_ONLY, + .mode_support = SVSB_MODE_INIT01 | SVSB_MODE_INIT02, + .opp_count = 16, + .freq_base = 1989000000, + .vboot = 0x30, + .volt_step = 6250, + .volt_base = 500000, + .volt_offset = 0, + .vmax = 0x58, + .vmin = 0x10, + .dthi = 0x1, + .dtlo = 0xfe, + .det_window = 0xa28, + .det_max = 0xffff, + .age_config = 0x555555, + .agem = 0x0, + .dc_config = 0x555555, + .dvt_fixed = 0x7, + .vco = 0x10, + .chkshift = 0x77, + .upper_temp_bound = 0x64, + .lower_temp_bound = 0xb2, + .low_temp_threashold = 25000, + .low_temp_offset = 0, + .coresel = 0x8fff0001, + .systemclk_en = BIT(31), + .intst = BIT(1), + .ctl0 = 0x00000001, + }, + { + .of_compatible = "mediatek,mt8183-svs-cci", + .sw_id = SVS_CCI, + .bank_id = 2, + .ops = &svs_mt8183_banks_ops, + .zone_name = "tzts4", + .buck_name = "vcci", + .mtcmos_request = false, + .init01_volt_flag = SVSB_INIT01_VOLT_INC_ONLY, + .mode_support = SVSB_MODE_INIT01 | SVSB_MODE_INIT02, + .opp_count = 16, + .freq_base = 1196000000, + .vboot = 0x30, + .volt_step = 6250, + .volt_base = 500000, + .volt_offset = 0, + .vmax = 0x64, + .vmin = 0x18, + .dthi = 0x1, + .dtlo = 0xfe, + .det_window = 0xa28, + .det_max = 0xffff, + .age_config = 0x555555, + .agem = 0x0, + .dc_config = 0x555555, + .dvt_fixed = 0x7, + .vco = 0x10, + .chkshift = 0x77, + .upper_temp_bound = 0x64, + .lower_temp_bound = 0xb2, + .low_temp_threashold = 25000, + .low_temp_offset = 0, + .coresel = 0x8fff0002, + .systemclk_en = BIT(31), + .intst = BIT(2), + .ctl0 = 0x00100003, + }, + { + .of_compatible = "mediatek,mt8183-svs-gpu", + .sw_id = SVS_GPU, + .bank_id = 3, + .ops = &svs_mt8183_banks_ops, + .zone_name = "tzts2", + .buck_name = "vgpu", + .mtcmos_request = true, + .init01_volt_flag = SVSB_INIT01_VOLT_INC_ONLY, + .mode_support = SVSB_MODE_INIT01 | SVSB_MODE_INIT02 | + SVSB_MODE_MON, + .opp_count = 16, + .freq_base = 900000000, + .vboot = 0x30, + .volt_step = 6250, + .volt_base = 500000, + .volt_offset = 0, + .vmax = 0x40, + .vmin = 0x14, + .dthi = 0x1, + .dtlo = 0xfe, + .det_window = 0xa28, + .det_max = 0xffff, + .age_config = 0x555555, + .agem = 0x0, + .dc_config = 0x555555, + .dvt_fixed = 0x3, + .vco = 0x10, + .chkshift = 0x77, + .upper_temp_bound = 0x64, + .lower_temp_bound = 0xb2, + .low_temp_threashold = 25000, + .low_temp_offset = 3, + .coresel = 0x8fff0003, + .systemclk_en = BIT(31), + .intst = BIT(3), + .ctl0 = 0x00050001, + }, +}; + +static const struct svs_platform svs_mt8183_platform = { + .name = "mt8183-svs", + .banks = svs_mt8183_banks, + .efuse_parsing = svs_mt8183_efuse_parsing, + .regs = svs_regs_v2, + .fake_efuse = false, + .bank_num = 4, + .efuse_num = 25, + .efuse_check = 2, + .thermal_efuse_num = 3, +}; + +static const struct of_device_id mtk_svs_of_match[] = { + { + .compatible = "mediatek,mt8183-svs", + .data = &svs_mt8183_platform, + }, { + /* Sentinel */ + }, +}; + +static int svs_probe(struct platform_device *pdev) +{ + const struct of_device_id *of_dev_id; + struct mtk_svs *svs; + int ret; + u32 svs_irq; + + svs = devm_kzalloc(&pdev->dev, sizeof(*svs), GFP_KERNEL); + if (!svs) + return -ENOMEM; + + svs->dev = &pdev->dev; + if (!svs->dev->of_node) { + pr_err("cannot find device node\n"); + return -ENODEV; + } + + svs->base = of_iomap(svs->dev->of_node, 0); + if (IS_ERR(svs->base)) { + pr_err("cannot find svs register base\n"); + return PTR_ERR(svs->base); + } + + svs_irq = irq_of_parse_and_map(svs->dev->of_node, 0); + ret = devm_request_threaded_irq(svs->dev, svs_irq, NULL, svs_isr, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "mtk-svs", svs); + if (ret) { + pr_err("register irq(%d) failed: %d\n", svs_irq, ret); + return ret; + } + + of_dev_id = of_match_node(mtk_svs_of_match, svs->dev->of_node); + if (!of_dev_id || !of_dev_id->data) + return -EINVAL; + + svs->platform = of_dev_id->data; + dev_set_drvdata(svs->dev, svs); + + svs->main_clk = devm_clk_get(svs->dev, "main"); + if (IS_ERR(svs->main_clk)) { + pr_err("failed to get clock: %ld\n", PTR_ERR(svs->main_clk)); + return PTR_ERR(svs->main_clk); + } + + ret = clk_prepare_enable(svs->main_clk); + if (ret) { + pr_err("cannot enable main clk: %d\n", ret); + return ret; + } + + if (!svs_is_supported(svs)) { + pr_notice("svs is not supported\n"); + goto svs_probe_fail; + } + + ret = svs_resource_setup(svs); + if (ret) + goto svs_probe_fail; + + ret = svs_start(svs); + if (ret) + goto svs_probe_fail; + + ret = svs_create_svs_procfs(svs); + if (ret) + goto svs_probe_fail; + + return 0; + +svs_probe_fail: + clk_disable_unprepare(svs->main_clk); + + return ret; +} + +static const struct dev_pm_ops svs_pm_ops = { + .suspend = svs_suspend, + .resume = svs_resume, +}; + +static struct platform_driver svs_driver = { + .probe = svs_probe, + .driver = { + .name = "mtk-svs", + .pm = &svs_pm_ops, + .of_match_table = of_match_ptr(mtk_svs_of_match), + }, +}; + +static int __init svs_init(void) +{ + int ret; + + ret = platform_driver_register(&svs_driver); + if (ret) { + pr_err("svs platform driver register failed: %d\n", ret); + return ret; + } + + return 0; +} + +late_initcall_sync(svs_init); + +MODULE_DESCRIPTION("MediaTek SVS Driver v1.0"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/power/mtk_svs.h b/include/linux/power/mtk_svs.h new file mode 100644 index 000000000000..5c03982e3576 --- /dev/null +++ b/include/linux/power/mtk_svs.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (C) 2020 MediaTek Inc. + */ + +#ifndef __MTK_SVS_H__ +#define __MTK_SVS_H__ + +#if IS_ENABLED(CONFIG_MTK_SVS) +unsigned long claim_mtk_svs_lock(void); +void release_mtk_svs_lock(unsigned long flags); +#else +static inline unsigned long claim_mtk_svs_lock(void) +{ + return 0; +} + +static inline void release_mtk_svs_lock(unsigned long flags) +{ +} +#endif /* CONFIG_MTK_SVS */ + +#endif /* __MTK_SVS_H__ */ -- 2.18.0 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel