From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pd0-x234.google.com ([2607:f8b0:400e:c02::234]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1Yq3dJ-00049v-QG for linux-mtd@lists.infradead.org; Wed, 06 May 2015 18:01:30 +0000 Received: by pdbnk13 with SMTP id nk13so16613541pdb.0 for ; Wed, 06 May 2015 11:01:02 -0700 (PDT) From: Brian Norris To: Subject: [PATCH v3 03/10] mtd: nand: add NAND driver for Broadcom STB NAND controller Date: Wed, 6 May 2015 10:59:47 -0700 Message-Id: <1430935194-7579-4-git-send-email-computersforpeace@gmail.com> In-Reply-To: <1430935194-7579-1-git-send-email-computersforpeace@gmail.com> References: <1430935194-7579-1-git-send-email-computersforpeace@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: devicetree@vger.kernel.org, Florian Fainelli , Scott Branden , Kevin Cernekee , Corneliu Doban , Ray Jui , =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= , linux-kernel@vger.kernel.org, Dan Ehrenberg , Jonathan Richardson , Anatol Pomazao , Gregory Fong , bcm-kernel-feedback-list@broadcom.com, Brian Norris , Dmitry Torokhov List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , This core originated in Set-Top Box chips (BCM7xxx) but is used in a variety of other Broadcom chips, including some BCM63xxx, BCM33xx, and iProc/Cygnus. It's been used only on ARM and MIPS SoCs, so restrict it to those architectures. There are multiple revisions of this core throughout the years, and almost every version broke register compatibility in some small way, but with some effort, this driver is able to support v4.0, v5.0, v6.x, v7.0, and v7.1. It's been tested on v5.0, v6.0, v7.0, and v7.1 recently, so there hopefully are no more lurking inconsistencies. Signed-off-by: Brian Norris --- drivers/mtd/nand/Kconfig | 8 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/brcmstb_nand.c | 2198 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 2207 insertions(+) create mode 100644 drivers/mtd/nand/brcmstb_nand.c diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 5897d8d8fa5a..3587017b209a 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -394,6 +394,14 @@ config MTD_NAND_GPMI_NAND block, such as SD card. So pay attention to it when you enable the GPMI. +config MTD_NAND_BRCMSTB + tristate "Broadcom STB NAND controller" + depends on ARM || MIPS + help + Enables the Broadcom NAND controller driver. The controller was + originally designed for Set-Top Box but is used on various BCM7xxx, + BCM3xxx, BCM63xxx, iProc/Cygnus and more. + config MTD_NAND_BCM47XXNFLASH tristate "Support for NAND flash on BCM4706 BCMA bus" depends on BCMA_NFLASH diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 582bbd05aff7..3b1adddc83dd 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -52,5 +52,6 @@ obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/ obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o +obj-$(CONFIG_MTD_NAND_BRCMSTB) += brcmstb_nand.o nand-objs := nand_base.o nand_bbt.o nand_timings.o diff --git a/drivers/mtd/nand/brcmstb_nand.c b/drivers/mtd/nand/brcmstb_nand.c new file mode 100644 index 000000000000..ec65a48d2487 --- /dev/null +++ b/drivers/mtd/nand/brcmstb_nand.c @@ -0,0 +1,2198 @@ +/* + * Copyright © 2010-2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This flag controls if WP stays on between erase/write commands to mitigate + * flash corruption due to power glitches. Values: + * 0: NAND_WP is not used or not available + * 1: NAND_WP is set by default, cleared for erase/write operations + * 2: NAND_WP is always cleared + */ +static int wp_on = 1; +module_param(wp_on, int, 0444); + +/*********************************************************************** + * Definitions + ***********************************************************************/ + +#define DRV_NAME "brcmstb_nand" + +#define CMD_NULL 0x00 +#define CMD_PAGE_READ 0x01 +#define CMD_SPARE_AREA_READ 0x02 +#define CMD_STATUS_READ 0x03 +#define CMD_PROGRAM_PAGE 0x04 +#define CMD_PROGRAM_SPARE_AREA 0x05 +#define CMD_COPY_BACK 0x06 +#define CMD_DEVICE_ID_READ 0x07 +#define CMD_BLOCK_ERASE 0x08 +#define CMD_FLASH_RESET 0x09 +#define CMD_BLOCKS_LOCK 0x0a +#define CMD_BLOCKS_LOCK_DOWN 0x0b +#define CMD_BLOCKS_UNLOCK 0x0c +#define CMD_READ_BLOCKS_LOCK_STATUS 0x0d +#define CMD_PARAMETER_READ 0x0e +#define CMD_PARAMETER_CHANGE_COL 0x0f +#define CMD_LOW_LEVEL_OP 0x10 + +struct brcm_nand_dma_desc { + u32 next_desc; + u32 next_desc_ext; + u32 cmd_irq; + u32 dram_addr; + u32 dram_addr_ext; + u32 tfr_len; + u32 total_len; + u32 flash_addr; + u32 flash_addr_ext; + u32 cs; + u32 pad2[5]; + u32 status_valid; +} __packed; + +/* Bitfields for brcm_nand_dma_desc::status_valid */ +#define FLASH_DMA_ECC_ERROR (1 << 8) +#define FLASH_DMA_CORR_ERROR (1 << 9) + +/* 512B flash cache in the NAND controller HW */ +#define FC_SHIFT 9U +#define FC_BYTES 512U +#define FC_WORDS (FC_BYTES >> 2) + +#define BRCMNAND_MIN_PAGESIZE 512 +#define BRCMNAND_MIN_BLOCKSIZE (8 * 1024) +#define BRCMNAND_MIN_DEVSIZE (4ULL * 1024 * 1024) + +/* Controller feature flags */ +enum { + BRCMNAND_HAS_1K_SECTORS = BIT(0), + BRCMNAND_HAS_PREFETCH = BIT(1), + BRCMNAND_HAS_CACHE_MODE = BIT(2), + BRCMNAND_HAS_WP = BIT(3), +}; + +struct brcmnand_controller { + struct device *dev; + struct nand_hw_control controller; + void __iomem *nand_base; + void __iomem *nand_fc; /* flash cache */ + void __iomem *flash_dma_base; + unsigned int irq; + unsigned int dma_irq; + int nand_version; + + int cmd_pending; + bool dma_pending; + struct completion done; + struct completion dma_done; + + /* List of NAND hosts (one for each chip-select) */ + struct list_head host_list; + + struct brcm_nand_dma_desc *dma_desc; + dma_addr_t dma_pa; + + /* in-memory cache of the FLASH_CACHE, used only for some commands */ + u32 flash_cache[FC_WORDS]; + + /* Controller revision details */ + const u16 *reg_offsets; + unsigned int reg_spacing; /* between CS1, CS2, ... regs */ + const u8 *cs_offsets; /* within each chip-select */ + const u8 *cs0_offsets; /* within CS0, if different */ + unsigned int max_block_size; + const unsigned int *block_sizes; + unsigned int max_page_size; + const unsigned int *page_sizes; + unsigned int max_oob; + u32 features; + + /* for low-power standby/resume only */ + u32 nand_cs_nand_select; + u32 nand_cs_nand_xor; + u32 corr_stat_threshold; + u32 flash_dma_mode; +}; + +struct brcmnand_cfg { + u64 device_size; + unsigned int block_size; + unsigned int page_size; + unsigned int spare_area_size; + unsigned int device_width; + unsigned int col_adr_bytes; + unsigned int blk_adr_bytes; + unsigned int ful_adr_bytes; + unsigned int sector_size_1k; + unsigned int ecc_level; + /* use for low-power standby/resume only */ + u32 acc_control; + u32 config; + u32 config_ext; + u32 timing_1; + u32 timing_2; +}; + +struct brcmnand_host { + struct list_head node; + struct device_node *of_node; + + struct nand_chip chip; + struct mtd_info mtd; + struct platform_device *pdev; + int cs; + + unsigned int last_cmd; + unsigned int last_byte; + u64 last_addr; + struct brcmnand_cfg hwcfg; + struct brcmnand_controller *ctrl; +}; + +enum brcmnand_reg { + BRCMNAND_CMD_START = 0, + BRCMNAND_CMD_EXT_ADDRESS, + BRCMNAND_CMD_ADDRESS, + BRCMNAND_INTFC_STATUS, + BRCMNAND_CS_SELECT, + BRCMNAND_CS_XOR, + BRCMNAND_LL_OP, + BRCMNAND_CS0_BASE, + BRCMNAND_CS1_BASE, /* CS1 regs, if non-contiguous */ + BRCMNAND_CORR_THRESHOLD, + BRCMNAND_CORR_THRESHOLD_EXT, + BRCMNAND_UNCORR_COUNT, + BRCMNAND_CORR_COUNT, + BRCMNAND_CORR_EXT_ADDR, + BRCMNAND_CORR_ADDR, + BRCMNAND_UNCORR_EXT_ADDR, + BRCMNAND_UNCORR_ADDR, + BRCMNAND_SEMAPHORE, + BRCMNAND_ID, + BRCMNAND_ID_EXT, + BRCMNAND_LL_RDATA, + BRCMNAND_OOB_READ_BASE, + BRCMNAND_OOB_READ_10_BASE, /* offset 0x10, if non-contiguous */ + BRCMNAND_OOB_WRITE_BASE, + BRCMNAND_OOB_WRITE_10_BASE, /* offset 0x10, if non-contiguous */ + BRCMNAND_FC_BASE, +}; + +/* BRCMNAND v4.0 */ +static const u16 brcmnand_regs_v40[] = { + [BRCMNAND_CMD_START] = 0x04, + [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, + [BRCMNAND_CMD_ADDRESS] = 0x0c, + [BRCMNAND_INTFC_STATUS] = 0x6c, + [BRCMNAND_CS_SELECT] = 0x14, + [BRCMNAND_CS_XOR] = 0x18, + [BRCMNAND_LL_OP] = 0x178, + [BRCMNAND_CS0_BASE] = 0x40, + [BRCMNAND_CS1_BASE] = 0xd0, + [BRCMNAND_CORR_THRESHOLD] = 0x84, + [BRCMNAND_CORR_THRESHOLD_EXT] = 0, + [BRCMNAND_UNCORR_COUNT] = 0, + [BRCMNAND_CORR_COUNT] = 0, + [BRCMNAND_CORR_EXT_ADDR] = 0x70, + [BRCMNAND_CORR_ADDR] = 0x74, + [BRCMNAND_UNCORR_EXT_ADDR] = 0x78, + [BRCMNAND_UNCORR_ADDR] = 0x7c, + [BRCMNAND_SEMAPHORE] = 0x58, + [BRCMNAND_ID] = 0x60, + [BRCMNAND_ID_EXT] = 0x64, + [BRCMNAND_LL_RDATA] = 0x17c, + [BRCMNAND_OOB_READ_BASE] = 0x20, + [BRCMNAND_OOB_READ_10_BASE] = 0x130, + [BRCMNAND_OOB_WRITE_BASE] = 0x30, + [BRCMNAND_OOB_WRITE_10_BASE] = 0, + [BRCMNAND_FC_BASE] = 0x200, +}; + +/* BRCMNAND v5.0 */ +static const u16 brcmnand_regs_v50[] = { + [BRCMNAND_CMD_START] = 0x04, + [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, + [BRCMNAND_CMD_ADDRESS] = 0x0c, + [BRCMNAND_INTFC_STATUS] = 0x6c, + [BRCMNAND_CS_SELECT] = 0x14, + [BRCMNAND_CS_XOR] = 0x18, + [BRCMNAND_LL_OP] = 0x178, + [BRCMNAND_CS0_BASE] = 0x40, + [BRCMNAND_CS1_BASE] = 0xd0, + [BRCMNAND_CORR_THRESHOLD] = 0x84, + [BRCMNAND_CORR_THRESHOLD_EXT] = 0, + [BRCMNAND_UNCORR_COUNT] = 0, + [BRCMNAND_CORR_COUNT] = 0, + [BRCMNAND_CORR_EXT_ADDR] = 0x70, + [BRCMNAND_CORR_ADDR] = 0x74, + [BRCMNAND_UNCORR_EXT_ADDR] = 0x78, + [BRCMNAND_UNCORR_ADDR] = 0x7c, + [BRCMNAND_SEMAPHORE] = 0x58, + [BRCMNAND_ID] = 0x60, + [BRCMNAND_ID_EXT] = 0x64, + [BRCMNAND_LL_RDATA] = 0x17c, + [BRCMNAND_OOB_READ_BASE] = 0x20, + [BRCMNAND_OOB_READ_10_BASE] = 0x130, + [BRCMNAND_OOB_WRITE_BASE] = 0x30, + [BRCMNAND_OOB_WRITE_10_BASE] = 0x140, + [BRCMNAND_FC_BASE] = 0x200, +}; + +/* BRCMNAND v6.0 - v7.1 */ +static const u16 brcmnand_regs_v60[] = { + [BRCMNAND_CMD_START] = 0x04, + [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, + [BRCMNAND_CMD_ADDRESS] = 0x0c, + [BRCMNAND_INTFC_STATUS] = 0x14, + [BRCMNAND_CS_SELECT] = 0x18, + [BRCMNAND_CS_XOR] = 0x1c, + [BRCMNAND_LL_OP] = 0x20, + [BRCMNAND_CS0_BASE] = 0x50, + [BRCMNAND_CS1_BASE] = 0, + [BRCMNAND_CORR_THRESHOLD] = 0xc0, + [BRCMNAND_CORR_THRESHOLD_EXT] = 0xc4, + [BRCMNAND_UNCORR_COUNT] = 0xfc, + [BRCMNAND_CORR_COUNT] = 0x100, + [BRCMNAND_CORR_EXT_ADDR] = 0x10c, + [BRCMNAND_CORR_ADDR] = 0x110, + [BRCMNAND_UNCORR_EXT_ADDR] = 0x114, + [BRCMNAND_UNCORR_ADDR] = 0x118, + [BRCMNAND_SEMAPHORE] = 0x150, + [BRCMNAND_ID] = 0x194, + [BRCMNAND_ID_EXT] = 0x198, + [BRCMNAND_LL_RDATA] = 0x19c, + [BRCMNAND_OOB_READ_BASE] = 0x200, + [BRCMNAND_OOB_READ_10_BASE] = 0, + [BRCMNAND_OOB_WRITE_BASE] = 0x280, + [BRCMNAND_OOB_WRITE_10_BASE] = 0, + [BRCMNAND_FC_BASE] = 0x400, +}; + +enum brcmnand_cs_reg { + BRCMNAND_CS_CFG_EXT = 0, + BRCMNAND_CS_CFG, + BRCMNAND_CS_ACC_CONTROL, + BRCMNAND_CS_TIMING1, + BRCMNAND_CS_TIMING2, +}; + +/* Per chip-select offsets for v7.1 */ +static const u8 brcmnand_cs_offsets_v71[] = { + [BRCMNAND_CS_ACC_CONTROL] = 0x00, + [BRCMNAND_CS_CFG_EXT] = 0x04, + [BRCMNAND_CS_CFG] = 0x08, + [BRCMNAND_CS_TIMING1] = 0x0c, + [BRCMNAND_CS_TIMING2] = 0x10, +}; + +/* Per chip-select offsets for pre v7.1, except CS0 on <= v5.0 */ +static const u8 brcmnand_cs_offsets[] = { + [BRCMNAND_CS_ACC_CONTROL] = 0x00, + [BRCMNAND_CS_CFG_EXT] = 0x04, + [BRCMNAND_CS_CFG] = 0x04, + [BRCMNAND_CS_TIMING1] = 0x08, + [BRCMNAND_CS_TIMING2] = 0x0c, +}; + +/* Per chip-select offset for <= v5.0 on CS0 only */ +static const u8 brcmnand_cs_offsets_cs0[] = { + [BRCMNAND_CS_ACC_CONTROL] = 0x00, + [BRCMNAND_CS_CFG_EXT] = 0x08, + [BRCMNAND_CS_CFG] = 0x08, + [BRCMNAND_CS_TIMING1] = 0x10, + [BRCMNAND_CS_TIMING2] = 0x14, +}; + +/* BRCMNAND_INTFC_STATUS */ +enum { + INTFC_FLASH_STATUS = GENMASK(7, 0), + + INTFC_ERASED = BIT(27), + INTFC_OOB_VALID = BIT(28), + INTFC_CACHE_VALID = BIT(29), + INTFC_FLASH_READY = BIT(30), + INTFC_CTLR_READY = BIT(31), +}; + +static inline u32 nand_readreg(struct brcmnand_controller *ctrl, u32 offs) +{ + return __raw_readl(ctrl->nand_base + offs); +} + +static inline void nand_writereg(struct brcmnand_controller *ctrl, u32 offs, + u32 val) +{ + __raw_writel(val, ctrl->nand_base + offs); +} + +static int brcmnand_revision_init(struct brcmnand_controller *ctrl) +{ + static const unsigned int block_sizes_v6[] = { 8, 16, 128, 256, 512, 1024, 2048, 0 }; + static const unsigned int block_sizes_v4[] = { 16, 128, 8, 512, 256, 1024, 2048, 0 }; + static const unsigned int page_sizes[] = { 512, 2048, 4096, 8192, 0 }; + + ctrl->nand_version = nand_readreg(ctrl, 0) & 0xffff; + + /* Only support v4.0+? */ + if (ctrl->nand_version < 0x0400) { + dev_err(ctrl->dev, "version %#x not supported\n", + ctrl->nand_version); + return -ENODEV; + } + + /* Register offsets */ + if (ctrl->nand_version >= 0x0600) + ctrl->reg_offsets = brcmnand_regs_v60; + else if (ctrl->nand_version >= 0x0500) + ctrl->reg_offsets = brcmnand_regs_v50; + else if (ctrl->nand_version >= 0x0400) + ctrl->reg_offsets = brcmnand_regs_v40; + + /* Chip-select stride */ + if (ctrl->nand_version >= 0x0701) + ctrl->reg_spacing = 0x14; + else + ctrl->reg_spacing = 0x10; + + /* Per chip-select registers */ + if (ctrl->nand_version >= 0x0701) { + ctrl->cs_offsets = brcmnand_cs_offsets_v71; + } else { + ctrl->cs_offsets = brcmnand_cs_offsets; + + /* v5.0 and earlier has a different CS0 offset layout */ + if (ctrl->nand_version <= 0x0500) + ctrl->cs0_offsets = brcmnand_cs_offsets_cs0; + } + + /* Page / block sizes */ + if (ctrl->nand_version >= 0x0701) { + /* >= v7.1 use nice power-of-2 values! */ + ctrl->max_page_size = 16 * 1024; + ctrl->max_block_size = 2 * 1024 * 1024; + } else { + ctrl->page_sizes = page_sizes; + if (ctrl->nand_version >= 0x0600) + ctrl->block_sizes = block_sizes_v6; + else + ctrl->block_sizes = block_sizes_v4; + + if (ctrl->nand_version < 0x0400) { + ctrl->max_page_size = 4096; + ctrl->max_block_size = 512 * 1024; + } + } + + /* Maximum spare area sector size (per 512B) */ + if (ctrl->nand_version >= 0x0600) + ctrl->max_oob = 64; + else if (ctrl->nand_version >= 0x0500) + ctrl->max_oob = 32; + else + ctrl->max_oob = 16; + + /* v6.0 and newer (except v6.1) have prefetch support */ + if (ctrl->nand_version >= 0x0600 && ctrl->nand_version != 0x0601) + ctrl->features |= BRCMNAND_HAS_PREFETCH; + + /* + * v6.x has cache mode, but it's implemented differently. Ignore it for + * now. + */ + if (ctrl->nand_version >= 0x0700) + ctrl->features |= BRCMNAND_HAS_CACHE_MODE; + + if (ctrl->nand_version >= 0x0500) + ctrl->features |= BRCMNAND_HAS_1K_SECTORS; + + if (ctrl->nand_version >= 0x0700) + ctrl->features |= BRCMNAND_HAS_WP; + else if (of_property_read_bool(ctrl->dev->of_node, "brcm,nand-has-wp")) + ctrl->features |= BRCMNAND_HAS_WP; + + return 0; +} + +static inline u32 brcmnand_read_reg(struct brcmnand_controller *ctrl, + enum brcmnand_reg reg) +{ + u16 offs = ctrl->reg_offsets[reg]; + + if (offs) + return nand_readreg(ctrl, offs); + else + return 0; +} + +static inline void brcmnand_write_reg(struct brcmnand_controller *ctrl, + enum brcmnand_reg reg, u32 val) +{ + u16 offs = ctrl->reg_offsets[reg]; + + if (offs) + nand_writereg(ctrl, offs, val); +} + +static inline void brcmnand_rmw_reg(struct brcmnand_controller *ctrl, + enum brcmnand_reg reg, u32 mask, unsigned + int shift, u32 val) +{ + u32 tmp = brcmnand_read_reg(ctrl, reg); + + tmp &= ~mask; + tmp |= val << shift; + brcmnand_write_reg(ctrl, reg, tmp); +} + +static inline u32 brcmnand_read_fc(struct brcmnand_controller *ctrl, int word) +{ + return __raw_readl(ctrl->nand_fc + word * 4); +} + +static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl, + int word, u32 val) +{ + __raw_writel(val, ctrl->nand_fc + word * 4); +} + +static inline u16 brcmnand_cs_offset(struct brcmnand_controller *ctrl, int cs, + enum brcmnand_cs_reg reg) +{ + u16 offs_cs0 = ctrl->reg_offsets[BRCMNAND_CS0_BASE]; + u16 offs_cs1 = ctrl->reg_offsets[BRCMNAND_CS1_BASE]; + u8 cs_offs; + + if (cs == 0 && ctrl->cs0_offsets) + cs_offs = ctrl->cs0_offsets[reg]; + else + cs_offs = ctrl->cs_offsets[reg]; + + if (cs && offs_cs1) + return offs_cs1 + (cs - 1) * ctrl->reg_spacing + cs_offs; + + return offs_cs0 + cs * ctrl->reg_spacing + cs_offs; +} + +static inline u32 brcmnand_count_corrected(struct brcmnand_controller *ctrl) +{ + if (ctrl->nand_version < 0x0600) + return 1; + return brcmnand_read_reg(ctrl, BRCMNAND_CORR_COUNT); +} + +static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val) +{ + struct brcmnand_controller *ctrl = host->ctrl; + unsigned int shift = 0, bits; + enum brcmnand_reg reg = BRCMNAND_CORR_THRESHOLD; + int cs = host->cs; + + if (ctrl->nand_version >= 0x0600) + bits = 6; + else if (ctrl->nand_version >= 0x0500) + bits = 5; + else + bits = 4; + + if (ctrl->nand_version >= 0x0600) { + if (cs >= 5) + reg = BRCMNAND_CORR_THRESHOLD_EXT; + shift = (cs % 5) * bits; + } + brcmnand_rmw_reg(ctrl, reg, (bits - 1) << shift, shift, val); +} + +static inline int brcmnand_cmd_shift(struct brcmnand_controller *ctrl) +{ + if (ctrl->nand_version < 0x0700) + return 24; + return 0; +} + +/*********************************************************************** + * NAND ACC CONTROL bitfield + * + * Some bits have remained constant throughout hardware revision, while + * others have shifted around. + ***********************************************************************/ + +/* Constant for all versions (where supported) */ +enum { + /* See BRCMNAND_HAS_CACHE_MODE */ + ACC_CONTROL_CACHE_MODE = BIT(22), + + /* See BRCMNAND_HAS_PREFETCH */ + ACC_CONTROL_PREFETCH = BIT(23), + + ACC_CONTROL_PAGE_HIT = BIT(24), + ACC_CONTROL_WR_PREEMPT = BIT(25), + ACC_CONTROL_PARTIAL_PAGE = BIT(26), + ACC_CONTROL_RD_ERASED = BIT(27), + ACC_CONTROL_FAST_PGM_RDIN = BIT(28), + ACC_CONTROL_WR_ECC = BIT(30), + ACC_CONTROL_RD_ECC = BIT(31), +}; + +static inline u32 brcmnand_spare_area_mask(struct brcmnand_controller *ctrl) +{ + if (ctrl->nand_version >= 0x0600) + return GENMASK(6, 0); + else + return GENMASK(5, 0); +} + +#define NAND_ACC_CONTROL_ECC_SHIFT 16 + +static inline u32 brcmnand_ecc_level_mask(struct brcmnand_controller *ctrl) +{ + u32 mask = (ctrl->nand_version >= 0x0600) ? 0x1f : 0x0f; + + return mask << NAND_ACC_CONTROL_ECC_SHIFT; +} + +static void brcmnand_set_ecc_enabled(struct brcmnand_host *host, int en) +{ + struct brcmnand_controller *ctrl = host->ctrl; + u16 offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL); + u32 acc_control = nand_readreg(ctrl, offs); + u32 ecc_flags = ACC_CONTROL_WR_ECC | ACC_CONTROL_RD_ECC; + + if (en) { + acc_control |= ecc_flags; /* enable RD/WR ECC */ + acc_control |= host->hwcfg.ecc_level + << NAND_ACC_CONTROL_ECC_SHIFT; + } else { + acc_control &= ~ecc_flags; /* disable RD/WR ECC */ + acc_control &= ~brcmnand_ecc_level_mask(ctrl); + } + + nand_writereg(ctrl, offs, acc_control); +} + +static inline int brcmnand_sector_1k_shift(struct brcmnand_controller *ctrl) +{ + if (ctrl->nand_version >= 0x0600) + return 7; + else if (ctrl->nand_version >= 0x0500) + return 6; + else + return -1; +} + +static int brcmnand_get_sector_size_1k(struct brcmnand_host *host) +{ + struct brcmnand_controller *ctrl = host->ctrl; + int shift = brcmnand_sector_1k_shift(ctrl); + u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_ACC_CONTROL); + + if (shift < 0) + return 0; + + return (nand_readreg(ctrl, acc_control_offs) >> shift) & 0x1; +} + +static void brcmnand_set_sector_size_1k(struct brcmnand_host *host, int val) +{ + struct brcmnand_controller *ctrl = host->ctrl; + int shift = brcmnand_sector_1k_shift(ctrl); + u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_ACC_CONTROL); + u32 tmp; + + if (shift < 0) + return; + + tmp = nand_readreg(ctrl, acc_control_offs); + tmp &= ~(1 << shift); + tmp |= (!!val) << shift; + nand_writereg(ctrl, acc_control_offs, tmp); +} + +/*********************************************************************** + * CS_NAND_SELECT + ***********************************************************************/ + +enum { + CS_SELECT_NAND_WP = BIT(29), + CS_SELECT_AUTO_DEVICE_ID_CFG = BIT(30), +}; + +static inline void brcmnand_set_wp(struct brcmnand_controller *ctrl, bool en) +{ + u32 val = en ? CS_SELECT_NAND_WP : 0; + + brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT, CS_SELECT_NAND_WP, 0, val); +} + +/*********************************************************************** + * Flash DMA + ***********************************************************************/ + +enum flash_dma_reg { + FLASH_DMA_REVISION = 0x00, + FLASH_DMA_FIRST_DESC = 0x04, + FLASH_DMA_FIRST_DESC_EXT = 0x08, + FLASH_DMA_CTRL = 0x0c, + FLASH_DMA_MODE = 0x10, + FLASH_DMA_STATUS = 0x14, + FLASH_DMA_INTERRUPT_DESC = 0x18, + FLASH_DMA_INTERRUPT_DESC_EXT = 0x1c, + FLASH_DMA_ERROR_STATUS = 0x20, + FLASH_DMA_CURRENT_DESC = 0x24, + FLASH_DMA_CURRENT_DESC_EXT = 0x28, +}; + +static inline bool has_flash_dma(struct brcmnand_controller *ctrl) +{ + return ctrl->flash_dma_base; +} + +static inline bool flash_dma_buf_ok(const void *buf) +{ + return buf && !is_vmalloc_addr(buf) && + likely(IS_ALIGNED((uintptr_t)buf, 4)); +} + +static inline void flash_dma_writel(struct brcmnand_controller *ctrl, u8 offs, + u32 val) +{ + __raw_writel(val, ctrl->flash_dma_base + offs); +} + +static inline u32 flash_dma_readl(struct brcmnand_controller *ctrl, u8 offs) +{ + return __raw_readl(ctrl->flash_dma_base + offs); +} + +/* Low-level operation types: command, address, write, or read */ +enum brcmnand_llop_type { + LL_OP_CMD, + LL_OP_ADDR, + LL_OP_WR, + LL_OP_RD, +}; + +/*********************************************************************** + * Internal support functions + ***********************************************************************/ + +static inline bool is_hamming_ecc(struct brcmnand_cfg *cfg) +{ + return cfg->sector_size_1k == 0 && cfg->spare_area_size == 16 && + cfg->ecc_level == 15; +} + +/* + * Returns a nand_ecclayout strucutre for the given layout/configuration. + * Returns NULL on failure. + */ +static struct nand_ecclayout *brcmnand_create_layout(int ecc_level, + struct brcmnand_host *host) +{ + struct brcmnand_cfg *cfg = &host->hwcfg; + int i, j; + struct nand_ecclayout *layout; + int req; + int sectors; + int sas; + int idx1, idx2; + + layout = devm_kzalloc(&host->pdev->dev, sizeof(*layout), GFP_KERNEL); + if (!layout) + return NULL; + + sectors = cfg->page_size / (512 << cfg->sector_size_1k); + sas = cfg->spare_area_size << cfg->sector_size_1k; + + /* Hamming */ + if (is_hamming_ecc(cfg)) { + for (i = 0, idx1 = 0, idx2 = 0; i < sectors; i++) { + /* First sector of each page may have BBI */ + if (i == 0) { + layout->oobfree[idx2].offset = i * sas + 1; + /* Small-page NAND use byte 6 for BBI */ + if (cfg->page_size == 512) + layout->oobfree[idx2].offset--; + layout->oobfree[idx2].length = 5; + } else { + layout->oobfree[idx2].offset = i * sas; + layout->oobfree[idx2].length = 6; + } + idx2++; + layout->eccpos[idx1++] = i * sas + 6; + layout->eccpos[idx1++] = i * sas + 7; + layout->eccpos[idx1++] = i * sas + 8; + layout->oobfree[idx2].offset = i * sas + 9; + layout->oobfree[idx2].length = 7; + idx2++; + /* Leave zero-terminated entry for OOBFREE */ + if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE || + idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1) + break; + } + goto out; + } + + /* + * CONTROLLER_VERSION: + * < v5.0: ECC_REQ = ceil(BCH_T * 13/8) + * >= v5.0: ECC_REQ = ceil(BCH_T * 14/8) + * But we will just be conservative. + */ + req = DIV_ROUND_UP(ecc_level * 14, 8); + if (req >= sas) { + dev_err(&host->pdev->dev, + "error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n", + req, sas); + return NULL; + } + + layout->eccbytes = req * sectors; + for (i = 0, idx1 = 0, idx2 = 0; i < sectors; i++) { + for (j = sas - req; j < sas && idx1 < + MTD_MAX_ECCPOS_ENTRIES_LARGE; j++, idx1++) + layout->eccpos[idx1] = i * sas + j; + + /* First sector of each page may have BBI */ + if (i == 0) { + if (cfg->page_size == 512 && (sas - req >= 6)) { + /* Small-page NAND use byte 6 for BBI */ + layout->oobfree[idx2].offset = 0; + layout->oobfree[idx2].length = 5; + idx2++; + if (sas - req > 6) { + layout->oobfree[idx2].offset = 6; + layout->oobfree[idx2].length = + sas - req - 6; + idx2++; + } + } else if (sas > req + 1) { + layout->oobfree[idx2].offset = i * sas + 1; + layout->oobfree[idx2].length = sas - req - 1; + idx2++; + } + } else if (sas > req) { + layout->oobfree[idx2].offset = i * sas; + layout->oobfree[idx2].length = sas - req; + idx2++; + } + /* Leave zero-terminated entry for OOBFREE */ + if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE || + idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1) + break; + } +out: + /* Sum available OOB */ + for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE; i++) + layout->oobavail += layout->oobfree[i].length; + return layout; +} + +static struct nand_ecclayout *brcmstb_choose_ecc_layout( + struct brcmnand_host *host) +{ + struct nand_ecclayout *layout; + struct brcmnand_cfg *p = &host->hwcfg; + unsigned int ecc_level = p->ecc_level; + + if (p->sector_size_1k) + ecc_level <<= 1; + + layout = brcmnand_create_layout(ecc_level, host); + if (!layout) { + dev_err(&host->pdev->dev, + "no proper ecc_layout for this NAND cfg\n"); + return NULL; + } + + return layout; +} + +static void brcmnand_wp(struct mtd_info *mtd, int wp) +{ + struct nand_chip *chip = mtd->priv; + struct brcmnand_host *host = chip->priv; + struct brcmnand_controller *ctrl = host->ctrl; + + if ((ctrl->features & BRCMNAND_HAS_WP) && wp_on == 1) { + static int old_wp = -1; + + if (old_wp != wp) { + dev_dbg(ctrl->dev, "WP %s\n", wp ? "on" : "off"); + old_wp = wp; + } + brcmnand_set_wp(ctrl, wp); + } +} + +/* Helper functions for reading and writing OOB registers */ +static inline u8 oob_reg_read(struct brcmnand_controller *ctrl, u32 offs) +{ + u16 offset0, offset10, reg_offs; + + offset0 = ctrl->reg_offsets[BRCMNAND_OOB_READ_BASE]; + offset10 = ctrl->reg_offsets[BRCMNAND_OOB_READ_10_BASE]; + + if (offs >= ctrl->max_oob) + return 0x77; + + if (offs >= 16 && offset10) + reg_offs = offset10 + ((offs - 0x10) & ~0x03); + else + reg_offs = offset0 + (offs & ~0x03); + + return nand_readreg(ctrl, reg_offs) >> (24 - ((offs & 0x03) << 3)); +} + +static inline void oob_reg_write(struct brcmnand_controller *ctrl, u32 offs, + u32 data) +{ + u16 offset0, offset10, reg_offs; + + offset0 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_BASE]; + offset10 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_10_BASE]; + + if (offs >= ctrl->max_oob) + return; + + if (offs >= 16 && offset10) + reg_offs = offset10 + ((offs - 0x10) & ~0x03); + else + reg_offs = offset0 + (offs & ~0x03); + + nand_writereg(ctrl, reg_offs, data); +} + +/* + * read_oob_from_regs - read data from OOB registers + * @ctrl: NAND controller + * @i: sub-page sector index + * @oob: buffer to read to + * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE) + * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal + */ +static int read_oob_from_regs(struct brcmnand_controller *ctrl, int i, u8 *oob, + int sas, int sector_1k) +{ + int tbytes = sas << sector_1k; + int j; + + /* Adjust OOB values for 1K sector size */ + if (sector_1k && (i & 0x01)) + tbytes = max(0, tbytes - (int)ctrl->max_oob); + tbytes = min_t(int, tbytes, ctrl->max_oob); + + for (j = 0; j < tbytes; j++) + oob[j] = oob_reg_read(ctrl, j); + return tbytes; +} + +/* + * write_oob_to_regs - write data to OOB registers + * @i: sub-page sector index + * @oob: buffer to write from + * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE) + * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal + */ +static int write_oob_to_regs(struct brcmnand_controller *ctrl, int i, + const u8 *oob, int sas, int sector_1k) +{ + int tbytes = sas << sector_1k; + int j; + + /* Adjust OOB values for 1K sector size */ + if (sector_1k && (i & 0x01)) + tbytes = max(0, tbytes - (int)ctrl->max_oob); + tbytes = min_t(int, tbytes, ctrl->max_oob); + + for (j = 0; j < tbytes; j += 4) + oob_reg_write(ctrl, j, + (oob[j + 0] << 24) | + (oob[j + 1] << 16) | + (oob[j + 2] << 8) | + (oob[j + 3] << 0)); + return tbytes; +} + +static irqreturn_t brcmnand_ctlrdy_irq(int irq, void *data) +{ + struct brcmnand_controller *ctrl = data; + + /* Discard all NAND_CTLRDY interrupts during DMA */ + if (ctrl->dma_pending) + return IRQ_HANDLED; + + complete(&ctrl->done); + return IRQ_HANDLED; +} + +static irqreturn_t brcmnand_dma_irq(int irq, void *data) +{ + struct brcmnand_controller *ctrl = data; + + complete(&ctrl->dma_done); + + return IRQ_HANDLED; +} + +static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd) +{ + struct brcmnand_controller *ctrl = host->ctrl; + u32 intfc; + + dev_dbg(ctrl->dev, "send native cmd %d addr_lo 0x%x\n", cmd, + brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS)); + BUG_ON(ctrl->cmd_pending != 0); + ctrl->cmd_pending = cmd; + + intfc = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS); + BUG_ON(!(intfc & INTFC_CTLR_READY)); + + mb(); /* flush previous writes */ + brcmnand_write_reg(ctrl, BRCMNAND_CMD_START, + cmd << brcmnand_cmd_shift(ctrl)); +} + +/*********************************************************************** + * NAND MTD API: read/program/erase + ***********************************************************************/ + +static void brcmnand_cmd_ctrl(struct mtd_info *mtd, int dat, + unsigned int ctrl) +{ + /* intentionally left blank */ +} + +static int brcmnand_waitfunc(struct mtd_info *mtd, struct nand_chip *this) +{ + struct nand_chip *chip = mtd->priv; + struct brcmnand_host *host = chip->priv; + struct brcmnand_controller *ctrl = host->ctrl; + unsigned long timeo = msecs_to_jiffies(100); + + dev_dbg(ctrl->dev, "wait on native cmd %d\n", ctrl->cmd_pending); + if (ctrl->cmd_pending && + wait_for_completion_timeout(&ctrl->done, timeo) <= 0) { + u32 cmd = brcmnand_read_reg(ctrl, BRCMNAND_CMD_START) + >> brcmnand_cmd_shift(ctrl); + + dev_err_ratelimited(ctrl->dev, + "timeout waiting for command %#02x\n", cmd); + dev_err_ratelimited(ctrl->dev, "intfc status %08x\n", + brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS)); + } + ctrl->cmd_pending = 0; + return brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) & + INTFC_FLASH_STATUS; +} + +enum { + LLOP_RE = BIT(16), + LLOP_WE = BIT(17), + LLOP_ALE = BIT(18), + LLOP_CLE = BIT(19), + LLOP_RETURN_IDLE = BIT(31), + + LLOP_DATA_MASK = GENMASK(15, 0), +}; + +static int brcmnand_low_level_op(struct brcmnand_host *host, + enum brcmnand_llop_type type, u32 data, + bool last_op) +{ + struct mtd_info *mtd = &host->mtd; + struct nand_chip *chip = &host->chip; + struct brcmnand_controller *ctrl = host->ctrl; + u32 tmp; + + tmp = data & LLOP_DATA_MASK; + switch (type) { + case LL_OP_CMD: + tmp |= LLOP_WE | LLOP_CLE; + break; + case LL_OP_ADDR: + /* WE | ALE */ + tmp |= LLOP_WE | LLOP_ALE; + break; + case LL_OP_WR: + /* WE */ + tmp |= LLOP_WE; + break; + case LL_OP_RD: + /* RE */ + tmp |= LLOP_RE; + break; + } + if (last_op) + /* RETURN_IDLE */ + tmp |= LLOP_RETURN_IDLE; + + dev_dbg(ctrl->dev, "ll_op cmd %#x\n", tmp); + + brcmnand_write_reg(ctrl, BRCMNAND_LL_OP, tmp); + (void)brcmnand_read_reg(ctrl, BRCMNAND_LL_OP); + + brcmnand_send_cmd(host, CMD_LOW_LEVEL_OP); + return brcmnand_waitfunc(mtd, chip); +} + +static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd->priv; + struct brcmnand_host *host = chip->priv; + struct brcmnand_controller *ctrl = host->ctrl; + u64 addr = (u64)page_addr << chip->page_shift; + int native_cmd = 0; + + if (command == NAND_CMD_READID || command == NAND_CMD_PARAM || + command == NAND_CMD_RNDOUT) + addr = (u64)column; + /* Avoid propagating a negative, don't-care address */ + else if (page_addr < 0) + addr = 0; + + dev_dbg(ctrl->dev, "cmd 0x%x addr 0x%llx\n", command, + (unsigned long long)addr); + + host->last_cmd = command; + host->last_byte = 0; + host->last_addr = addr; + + switch (command) { + case NAND_CMD_RESET: + native_cmd = CMD_FLASH_RESET; + break; + case NAND_CMD_STATUS: + native_cmd = CMD_STATUS_READ; + break; + case NAND_CMD_READID: + native_cmd = CMD_DEVICE_ID_READ; + break; + case NAND_CMD_READOOB: + native_cmd = CMD_SPARE_AREA_READ; + break; + case NAND_CMD_ERASE1: + native_cmd = CMD_BLOCK_ERASE; + brcmnand_wp(mtd, 0); + break; + case NAND_CMD_PARAM: + native_cmd = CMD_PARAMETER_READ; + break; + case NAND_CMD_SET_FEATURES: + case NAND_CMD_GET_FEATURES: + brcmnand_low_level_op(host, LL_OP_CMD, command, false); + brcmnand_low_level_op(host, LL_OP_ADDR, column, false); + break; + case NAND_CMD_RNDOUT: + native_cmd = CMD_PARAMETER_CHANGE_COL; + addr &= ~((u64)(FC_BYTES - 1)); + /* + * HW quirk: PARAMETER_CHANGE_COL requires SECTOR_SIZE_1K=0 + * NB: hwcfg.sector_size_1k may not be initialized yet + */ + if (brcmnand_get_sector_size_1k(host)) { + host->hwcfg.sector_size_1k = + brcmnand_get_sector_size_1k(host); + brcmnand_set_sector_size_1k(host, 0); + } + break; + } + + if (!native_cmd) + return; + + brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, + (host->cs << 16) | ((addr >> 32) & 0xffff)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); + brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, lower_32_bits(addr)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); + + brcmnand_send_cmd(host, native_cmd); + brcmnand_waitfunc(mtd, chip); + + if (native_cmd == CMD_PARAMETER_READ || + native_cmd == CMD_PARAMETER_CHANGE_COL) { + int i; + /* + * Must cache the FLASH_CACHE now, since changes in + * SECTOR_SIZE_1K may invalidate it + */ + for (i = 0; i < FC_WORDS; i++) + ctrl->flash_cache[i] = brcmnand_read_fc(ctrl, i); + /* Cleanup from HW quirk: restore SECTOR_SIZE_1K */ + if (host->hwcfg.sector_size_1k) + brcmnand_set_sector_size_1k(host, + host->hwcfg.sector_size_1k); + } + + /* Re-enable protection is necessary only after erase */ + if (command == NAND_CMD_ERASE1) + brcmnand_wp(mtd, 1); +} + +static uint8_t brcmnand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct brcmnand_host *host = chip->priv; + struct brcmnand_controller *ctrl = host->ctrl; + uint8_t ret = 0; + int addr, offs; + + switch (host->last_cmd) { + case NAND_CMD_READID: + if (host->last_byte < 4) + ret = brcmnand_read_reg(ctrl, BRCMNAND_ID) >> + (24 - (host->last_byte << 3)); + else if (host->last_byte < 8) + ret = brcmnand_read_reg(ctrl, BRCMNAND_ID_EXT) >> + (56 - (host->last_byte << 3)); + break; + + case NAND_CMD_READOOB: + ret = oob_reg_read(ctrl, host->last_byte); + break; + + case NAND_CMD_STATUS: + ret = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) & + INTFC_FLASH_STATUS; + if (wp_on) /* hide WP status */ + ret |= NAND_STATUS_WP; + break; + + case NAND_CMD_PARAM: + case NAND_CMD_RNDOUT: + addr = host->last_addr + host->last_byte; + offs = addr & (FC_BYTES - 1); + + /* At FC_BYTES boundary, switch to next column */ + if (host->last_byte > 0 && offs == 0) + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, addr, -1); + + ret = ctrl->flash_cache[offs >> 2] >> + (24 - ((offs & 0x03) << 3)); + break; + case NAND_CMD_GET_FEATURES: + if (host->last_byte >= ONFI_SUBFEATURE_PARAM_LEN) { + ret = 0; + } else { + bool last = host->last_byte == + ONFI_SUBFEATURE_PARAM_LEN - 1; + brcmnand_low_level_op(host, LL_OP_RD, 0, last); + ret = brcmnand_read_reg(ctrl, BRCMNAND_LL_RDATA) & 0xff; + } + } + + dev_dbg(ctrl->dev, "read byte = 0x%02x\n", ret); + host->last_byte++; + + return ret; +} + +static void brcmnand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + + for (i = 0; i < len; i++, buf++) + *buf = brcmnand_read_byte(mtd); +} + +static void brcmnand_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + struct brcmnand_host *host = chip->priv; + + switch (host->last_cmd) { + case NAND_CMD_SET_FEATURES: + for (i = 0; i < len; i++) + brcmnand_low_level_op(host, LL_OP_WR, buf[i], + (i + 1) == len); + break; + default: + BUG(); + break; + } +} + +/** + * Construct a FLASH_DMA descriptor as part of a linked list. You must know the + * following ahead of time: + * - Is this descriptor the beginning or end of a linked list? + * - What is the (DMA) address of the next descriptor in the linked list? + */ +static int brcmnand_fill_dma_desc(struct brcmnand_host *host, + struct brcm_nand_dma_desc *desc, u64 addr, + dma_addr_t buf, u32 len, u8 dma_cmd, + bool begin, bool end, + dma_addr_t next_desc) +{ + memset(desc, 0, sizeof(*desc)); + /* Descriptors are written in native byte order (wordwise) */ + desc->next_desc = lower_32_bits(next_desc); + desc->next_desc_ext = upper_32_bits(next_desc); + desc->cmd_irq = (dma_cmd << 24) | + (end ? (0x03 << 8) : 0) | /* IRQ | STOP */ + (!!begin) | ((!!end) << 1); /* head, tail */ +#ifdef CONFIG_CPU_BIG_ENDIAN + desc->cmd_irq |= 0x01 << 12; +#endif + desc->dram_addr = lower_32_bits(buf); + desc->dram_addr_ext = upper_32_bits(buf); + desc->tfr_len = len; + desc->total_len = len; + desc->flash_addr = lower_32_bits(addr); + desc->flash_addr_ext = upper_32_bits(addr); + desc->cs = host->cs; + desc->status_valid = 0x01; + return 0; +} + +/** + * Kick the FLASH_DMA engine, with a given DMA descriptor + */ +static void brcmnand_dma_run(struct brcmnand_host *host, dma_addr_t desc) +{ + struct brcmnand_controller *ctrl = host->ctrl; + unsigned long timeo = msecs_to_jiffies(100); + + flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC, lower_32_bits(desc)); + (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC); + flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC_EXT, upper_32_bits(desc)); + (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC_EXT); + + /* Start FLASH_DMA engine */ + ctrl->dma_pending = true; + mb(); /* flush previous writes */ + flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0x03); /* wake | run */ + + if (wait_for_completion_timeout(&ctrl->dma_done, timeo) <= 0) { + dev_err(ctrl->dev, + "timeout waiting for DMA; status %#x, error status %#x\n", + flash_dma_readl(ctrl, FLASH_DMA_STATUS), + flash_dma_readl(ctrl, FLASH_DMA_ERROR_STATUS)); + } + ctrl->dma_pending = false; + flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0); /* force stop */ +} + +static int brcmnand_dma_trans(struct brcmnand_host *host, u64 addr, u32 *buf, + u32 len, u8 dma_cmd) +{ + struct brcmnand_controller *ctrl = host->ctrl; + dma_addr_t buf_pa; + int dir = dma_cmd == CMD_PAGE_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + + buf_pa = dma_map_single(ctrl->dev, buf, len, dir); + if (dma_mapping_error(ctrl->dev, buf_pa)) { + dev_err(ctrl->dev, "unable to map buffer for DMA\n"); + return -ENOMEM; + } + + brcmnand_fill_dma_desc(host, ctrl->dma_desc, addr, buf_pa, len, + dma_cmd, true, true, 0); + + brcmnand_dma_run(host, ctrl->dma_pa); + + dma_unmap_single(ctrl->dev, buf_pa, len, dir); + + if (ctrl->dma_desc->status_valid & FLASH_DMA_ECC_ERROR) + return -EBADMSG; + else if (ctrl->dma_desc->status_valid & FLASH_DMA_CORR_ERROR) + return -EUCLEAN; + + return 0; +} + +/* + * Assumes proper CS is already set + */ +static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip, + u64 addr, unsigned int trans, u32 *buf, + u8 *oob, u64 *err_addr) +{ + struct brcmnand_host *host = chip->priv; + struct brcmnand_controller *ctrl = host->ctrl; + int i, j, ret = 0; + + /* Clear error addresses */ + brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_ADDR, 0); + brcmnand_write_reg(ctrl, BRCMNAND_CORR_ADDR, 0); + + brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, + (host->cs << 16) | ((addr >> 32) & 0xffff)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); + + for (i = 0; i < trans; i++, addr += FC_BYTES) { + brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, + lower_32_bits(addr)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); + /* SPARE_AREA_READ does not use ECC, so just use PAGE_READ */ + brcmnand_send_cmd(host, CMD_PAGE_READ); + brcmnand_waitfunc(mtd, chip); + + if (likely(buf)) + for (j = 0; j < FC_WORDS; j++, buf++) + *buf = brcmnand_read_fc(ctrl, j); + + if (oob) + oob += read_oob_from_regs(ctrl, i, oob, + mtd->oobsize / trans, + host->hwcfg.sector_size_1k); + + if (!ret) { + *err_addr = brcmnand_read_reg(ctrl, + BRCMNAND_UNCORR_ADDR) | + ((u64)(brcmnand_read_reg(ctrl, + BRCMNAND_UNCORR_EXT_ADDR) + & 0xffff) << 32); + if (*err_addr) + ret = -EBADMSG; + } + + if (!ret) { + *err_addr = brcmnand_read_reg(ctrl, + BRCMNAND_CORR_ADDR) | + ((u64)(brcmnand_read_reg(ctrl, + BRCMNAND_CORR_EXT_ADDR) + & 0xffff) << 32); + if (*err_addr) + ret = -EUCLEAN; + } + } + + return ret; +} + +static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip, + u64 addr, unsigned int trans, u32 *buf, u8 *oob) +{ + struct brcmnand_host *host = chip->priv; + struct brcmnand_controller *ctrl = host->ctrl; + u64 err_addr = 0; + int err; + + dev_dbg(ctrl->dev, "read %llx -> %p\n", (unsigned long long)addr, buf); + + brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_COUNT, 0); + + if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) { + err = brcmnand_dma_trans(host, addr, buf, trans * FC_BYTES, + CMD_PAGE_READ); + if (err) { + if (mtd_is_bitflip_or_eccerr(err)) + err_addr = addr; + else + return -EIO; + } + } else { + if (oob) + memset(oob, 0x99, mtd->oobsize); + + err = brcmnand_read_by_pio(mtd, chip, addr, trans, buf, + oob, &err_addr); + } + + if (mtd_is_eccerr(err)) { + dev_dbg(ctrl->dev, "uncorrectable error at 0x%llx\n", + (unsigned long long)err_addr); + mtd->ecc_stats.failed++; + /* NAND layer expects zero on ECC errors */ + return 0; + } + + if (mtd_is_bitflip(err)) { + unsigned int corrected = brcmnand_count_corrected(ctrl); + + dev_dbg(ctrl->dev, "corrected error at 0x%llx\n", + (unsigned long long)err_addr); + mtd->ecc_stats.corrected += corrected; + /* Always exceed the software-imposed threshold */ + return max(mtd->bitflip_threshold, corrected); + } + + return 0; +} + +static int brcmnand_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct brcmnand_host *host = chip->priv; + u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL; + + return brcmnand_read(mtd, chip, host->last_addr, + mtd->writesize >> FC_SHIFT, (u32 *)buf, oob); +} + +static int brcmnand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct brcmnand_host *host = chip->priv; + u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL; + int ret; + + brcmnand_set_ecc_enabled(host, 0); + ret = brcmnand_read(mtd, chip, host->last_addr, + mtd->writesize >> FC_SHIFT, (u32 *)buf, oob); + brcmnand_set_ecc_enabled(host, 1); + return ret; +} + +static int brcmnand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + return brcmnand_read(mtd, chip, (u64)page << chip->page_shift, + mtd->writesize >> FC_SHIFT, + NULL, (u8 *)chip->oob_poi); +} + +static int brcmnand_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct brcmnand_host *host = chip->priv; + + brcmnand_set_ecc_enabled(host, 0); + brcmnand_read(mtd, chip, (u64)page << chip->page_shift, + mtd->writesize >> FC_SHIFT, + NULL, (u8 *)chip->oob_poi); + brcmnand_set_ecc_enabled(host, 1); + return 0; +} + +static int brcmnand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, + uint32_t data_offs, uint32_t readlen, + uint8_t *bufpoi, int page) +{ + struct brcmnand_host *host = chip->priv; + + return brcmnand_read(mtd, chip, host->last_addr + data_offs, + readlen >> FC_SHIFT, (u32 *)bufpoi, NULL); +} + +static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip, + u64 addr, const u32 *buf, u8 *oob) +{ + struct brcmnand_host *host = chip->priv; + struct brcmnand_controller *ctrl = host->ctrl; + unsigned int i, j, trans = mtd->writesize >> FC_SHIFT; + int status, ret = 0; + + dev_dbg(ctrl->dev, "write %llx <- %p\n", (unsigned long long)addr, buf); + + if (unlikely((u32)buf & 0x03)) { + dev_warn(ctrl->dev, "unaligned buffer: %p\n", buf); + buf = (u32 *)((u32)buf & ~0x03); + } + + brcmnand_wp(mtd, 0); + + for (i = 0; i < ctrl->max_oob; i += 4) + oob_reg_write(ctrl, i, 0xffffffff); + + if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) { + if (brcmnand_dma_trans(host, addr, (u32 *)buf, + mtd->writesize, CMD_PROGRAM_PAGE)) + ret = -EIO; + goto out; + } + + brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, + (host->cs << 16) | ((addr >> 32) & 0xffff)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); + + for (i = 0; i < trans; i++, addr += FC_BYTES) { + /* full address MUST be set before populating FC */ + brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, + lower_32_bits(addr)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); + + if (buf) + for (j = 0; j < FC_WORDS; j++, buf++) + brcmnand_write_fc(ctrl, j, *buf); + else if (oob) + for (j = 0; j < FC_WORDS; j++) + brcmnand_write_fc(ctrl, j, 0xffffffff); + + if (oob) { + oob += write_oob_to_regs(ctrl, i, oob, + mtd->oobsize / trans, + host->hwcfg.sector_size_1k); + } + + /* we cannot use SPARE_AREA_PROGRAM when PARTIAL_PAGE_EN=0 */ + brcmnand_send_cmd(host, CMD_PROGRAM_PAGE); + status = brcmnand_waitfunc(mtd, chip); + + if (status & NAND_STATUS_FAIL) { + dev_info(ctrl->dev, "program failed at %llx\n", + (unsigned long long)addr); + ret = -EIO; + goto out; + } + } +out: + brcmnand_wp(mtd, 1); + return ret; +} + +static int brcmnand_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) +{ + struct brcmnand_host *host = chip->priv; + void *oob = oob_required ? chip->oob_poi : NULL; + + brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob); + return 0; +} + +static int brcmnand_write_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf, + int oob_required) +{ + struct brcmnand_host *host = chip->priv; + void *oob = oob_required ? chip->oob_poi : NULL; + + brcmnand_set_ecc_enabled(host, 0); + brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob); + brcmnand_set_ecc_enabled(host, 1); + return 0; +} + +static int brcmnand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + return brcmnand_write(mtd, chip, (u64)page << chip->page_shift, + NULL, chip->oob_poi); +} + +static int brcmnand_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct brcmnand_host *host = chip->priv; + int ret; + + brcmnand_set_ecc_enabled(host, 0); + ret = brcmnand_write(mtd, chip, (u64)page << chip->page_shift, NULL, + (u8 *)chip->oob_poi); + brcmnand_set_ecc_enabled(host, 1); + + return ret; +} + +/*********************************************************************** + * Per-CS setup (1 NAND device) + ***********************************************************************/ + +static int brcmnand_set_cfg(struct brcmnand_host *host, + struct brcmnand_cfg *cfg) +{ + struct brcmnand_controller *ctrl = host->ctrl; + struct nand_chip *chip = &host->chip; + u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG); + u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_CFG_EXT); + u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_ACC_CONTROL); + u8 block_size = 0, page_size = 0, device_size = 0; + u32 tmp; + + if (ctrl->block_sizes) { + int i, found; + + for (i = 0, found = 0; ctrl->block_sizes[i]; i++) + if (ctrl->block_sizes[i] * 1024 == cfg->block_size) { + block_size = i; + found = 1; + } + if (!found) { + dev_warn(ctrl->dev, "invalid block size %u\n", + cfg->block_size); + return -EINVAL; + } + } else { + block_size = ffs(cfg->block_size) - ffs(BRCMNAND_MIN_BLOCKSIZE); + } + + if (cfg->block_size < BRCMNAND_MIN_BLOCKSIZE || (ctrl->max_block_size && + cfg->block_size > ctrl->max_block_size)) { + dev_warn(ctrl->dev, "invalid block size %u\n", + cfg->block_size); + block_size = 0; + } + + if (ctrl->page_sizes) { + int i, found; + + for (i = 0, found = 0; ctrl->page_sizes[i]; i++) + if (ctrl->page_sizes[i] == cfg->page_size) { + page_size = i; + found = 1; + } + if (!found) { + dev_warn(ctrl->dev, "invalid page size %u\n", + cfg->page_size); + return -EINVAL; + } + } else { + page_size = ffs(cfg->page_size) - ffs(BRCMNAND_MIN_PAGESIZE); + } + + if (cfg->page_size < BRCMNAND_MIN_PAGESIZE || (ctrl->max_page_size && + cfg->page_size > ctrl->max_page_size)) { + dev_warn(ctrl->dev, "invalid page size %u\n", cfg->page_size); + return -EINVAL; + } + + if (fls64(cfg->device_size) < fls64(BRCMNAND_MIN_DEVSIZE)) { + dev_warn(ctrl->dev, "invalid device size 0x%llx\n", + (unsigned long long)cfg->device_size); + return -EINVAL; + } + device_size = fls64(cfg->device_size) - fls64(BRCMNAND_MIN_DEVSIZE); + + tmp = (cfg->blk_adr_bytes << 8) | + (cfg->col_adr_bytes << 12) | + (cfg->ful_adr_bytes << 16) | + (!!(cfg->device_width == 16) << 23) | + (device_size << 24); + if (cfg_offs == cfg_ext_offs) { + tmp |= (page_size << 20) | (block_size << 28); + nand_writereg(ctrl, cfg_offs, tmp); + } else { + nand_writereg(ctrl, cfg_offs, tmp); + tmp = page_size | (block_size << 4); + nand_writereg(ctrl, cfg_ext_offs, tmp); + } + + tmp = nand_readreg(ctrl, acc_control_offs); + tmp &= ~brcmnand_ecc_level_mask(ctrl); + tmp |= cfg->ecc_level << NAND_ACC_CONTROL_ECC_SHIFT; + tmp &= ~brcmnand_spare_area_mask(ctrl); + tmp |= cfg->spare_area_size; + nand_writereg(ctrl, acc_control_offs, tmp); + + brcmnand_set_sector_size_1k(host, cfg->sector_size_1k); + + /* threshold = ceil(BCH-level * 0.75) */ + brcmnand_wr_corr_thresh(host, DIV_ROUND_UP(chip->ecc.strength * 3, 4)); + + return 0; +} + +static void brcmnand_print_cfg(char *buf, struct brcmnand_cfg *cfg) +{ + buf += sprintf(buf, + "%lluMiB total, %uKiB blocks, %u%s pages, %uB OOB, %u-bit", + (unsigned long long)cfg->device_size >> 20, + cfg->block_size >> 10, + cfg->page_size >= 1024 ? cfg->page_size >> 10 : cfg->page_size, + cfg->page_size >= 1024 ? "KiB" : "B", + cfg->spare_area_size, cfg->device_width); + + /* Account for Hamming ECC and for BCH 512B vs 1KiB sectors */ + if (is_hamming_ecc(cfg)) + sprintf(buf, ", Hamming ECC"); + else if (cfg->sector_size_1k) + sprintf(buf, ", BCH-%u (1KiB sector)", cfg->ecc_level << 1); + else + sprintf(buf, ", BCH-%u\n", cfg->ecc_level); +} + +/* + * Minimum number of bytes to address a page. Calculated as: + * roundup(log2(size / page-size) / 8) + * + * NB: the following does not "round up" for non-power-of-2 'size'; but this is + * OK because many other things will break if 'size' is irregular... + */ +static inline int get_blk_adr_bytes(u64 size, u32 writesize) +{ + return ALIGN(ilog2(size) - ilog2(writesize), 8) >> 3; +} + +static int brcmnand_setup_dev(struct brcmnand_host *host) +{ + struct mtd_info *mtd = &host->mtd; + struct nand_chip *chip = &host->chip; + struct brcmnand_controller *ctrl = host->ctrl; + struct brcmnand_cfg *cfg = &host->hwcfg; + char msg[128]; + u32 offs, tmp, oob_sector; + int ret; + + memset(cfg, 0, sizeof(*cfg)); + + ret = of_property_read_u32(chip->dn, "brcm,nand-oob-sector-size", + &oob_sector); + if (ret) { + /* Use detected size */ + cfg->spare_area_size = mtd->oobsize / + (mtd->writesize >> FC_SHIFT); + } else { + cfg->spare_area_size = oob_sector; + } + if (cfg->spare_area_size > ctrl->max_oob) + cfg->spare_area_size = ctrl->max_oob; + /* + * Set oobsize to be consistent with controller's spare_area_size, as + * the rest is inaccessible. + */ + mtd->oobsize = cfg->spare_area_size * (mtd->writesize >> FC_SHIFT); + + cfg->device_size = mtd->size; + cfg->block_size = mtd->erasesize; + cfg->page_size = mtd->writesize; + cfg->device_width = (chip->options & NAND_BUSWIDTH_16) ? 16 : 8; + cfg->col_adr_bytes = 2; + cfg->blk_adr_bytes = get_blk_adr_bytes(mtd->size, mtd->writesize); + + switch (chip->ecc.size) { + case 512: + if (chip->ecc.strength == 1) /* Hamming */ + cfg->ecc_level = 15; + else + cfg->ecc_level = chip->ecc.strength; + cfg->sector_size_1k = 0; + break; + case 1024: + if (!(ctrl->features & BRCMNAND_HAS_1K_SECTORS)) { + dev_err(ctrl->dev, "1KB sectors not supported\n"); + return -EINVAL; + } + if (chip->ecc.strength & 0x1) { + dev_err(ctrl->dev, + "odd ECC not supported with 1KB sectors\n"); + return -EINVAL; + } + + cfg->ecc_level = chip->ecc.strength >> 1; + cfg->sector_size_1k = 1; + break; + default: + dev_err(ctrl->dev, "unsupported ECC size: %d\n", + chip->ecc.size); + return -EINVAL; + } + + cfg->ful_adr_bytes = cfg->blk_adr_bytes; + if (mtd->writesize > 512) + cfg->ful_adr_bytes += cfg->col_adr_bytes; + else + cfg->ful_adr_bytes += 1; + + ret = brcmnand_set_cfg(host, cfg); + if (ret) + return ret; + + brcmnand_set_ecc_enabled(host, 1); + + brcmnand_print_cfg(msg, cfg); + dev_info(ctrl->dev, "detected %s\n", msg); + + /* Configure ACC_CONTROL */ + offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL); + tmp = nand_readreg(ctrl, offs); + tmp &= ~ACC_CONTROL_PARTIAL_PAGE; + tmp &= ~ACC_CONTROL_RD_ERASED; + tmp &= ~ACC_CONTROL_FAST_PGM_RDIN; + if (ctrl->features & BRCMNAND_HAS_PREFETCH) { + /* + * FIXME: Flash DMA + prefetch may see spurious erased-page ECC + * errors + */ + if (has_flash_dma(ctrl)) + tmp &= ~ACC_CONTROL_PREFETCH; + else + tmp |= ACC_CONTROL_PREFETCH; + } + nand_writereg(ctrl, offs, tmp); + + return 0; +} + +static int brcmnand_init_cs(struct brcmnand_host *host) +{ + struct brcmnand_controller *ctrl = host->ctrl; + struct device_node *dn = host->of_node; + struct platform_device *pdev = host->pdev; + struct mtd_info *mtd; + struct nand_chip *chip; + int ret = 0; + struct mtd_part_parser_data ppdata = { .of_node = dn }; + + ret = of_property_read_u32(dn, "reg", &host->cs); + if (ret) { + dev_err(&pdev->dev, "can't get chip-select\n"); + return -ENXIO; + } + + mtd = &host->mtd; + chip = &host->chip; + + chip->dn = dn; + chip->priv = host; + mtd->priv = chip; + mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "brcmnand.%d", + host->cs); + mtd->owner = THIS_MODULE; + mtd->dev.parent = &pdev->dev; + + chip->IO_ADDR_R = (void __iomem *)0xdeadbeef; + chip->IO_ADDR_W = (void __iomem *)0xdeadbeef; + + chip->cmd_ctrl = brcmnand_cmd_ctrl; + chip->cmdfunc = brcmnand_cmdfunc; + chip->waitfunc = brcmnand_waitfunc; + chip->read_byte = brcmnand_read_byte; + chip->read_buf = brcmnand_read_buf; + chip->write_buf = brcmnand_write_buf; + + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.read_page = brcmnand_read_page; + chip->ecc.read_subpage = brcmnand_read_subpage; + chip->ecc.write_page = brcmnand_write_page; + chip->ecc.read_page_raw = brcmnand_read_page_raw; + chip->ecc.write_page_raw = brcmnand_write_page_raw; + chip->ecc.write_oob_raw = brcmnand_write_oob_raw; + chip->ecc.read_oob_raw = brcmnand_read_oob_raw; + chip->ecc.read_oob = brcmnand_read_oob; + chip->ecc.write_oob = brcmnand_write_oob; + + chip->controller = &ctrl->controller; + + if (nand_scan_ident(mtd, 1, NULL)) + return -ENXIO; + + chip->options |= NAND_NO_SUBPAGE_WRITE; + /* + * Avoid (for instance) kmap()'d buffers from JFFS2, which we can't DMA + * to/from, and have nand_base pass us a bounce buffer instead, as + * needed. + */ + chip->options |= NAND_USE_BOUNCE_BUFFER; + + if (of_get_nand_on_flash_bbt(dn)) + chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; + + if (brcmnand_setup_dev(host)) + return -ENXIO; + + chip->ecc.size = host->hwcfg.sector_size_1k ? 1024 : 512; + /* only use our internal HW threshold */ + mtd->bitflip_threshold = 1; + + chip->ecc.layout = brcmstb_choose_ecc_layout(host); + if (!chip->ecc.layout) + return -ENXIO; + + if (nand_scan_tail(mtd)) + return -ENXIO; + + return mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); +} + +static void brcmnand_save_restore_cs_config(struct brcmnand_host *host, + int restore) +{ + struct brcmnand_controller *ctrl = host->ctrl; + u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG); + u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_CFG_EXT); + u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_ACC_CONTROL); + u16 t1_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING1); + u16 t2_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING2); + + if (restore) { + nand_writereg(ctrl, cfg_offs, host->hwcfg.config); + if (cfg_offs != cfg_ext_offs) + nand_writereg(ctrl, cfg_ext_offs, + host->hwcfg.config_ext); + nand_writereg(ctrl, acc_control_offs, host->hwcfg.acc_control); + nand_writereg(ctrl, t1_offs, host->hwcfg.timing_1); + nand_writereg(ctrl, t2_offs, host->hwcfg.timing_2); + } else { + host->hwcfg.config = nand_readreg(ctrl, cfg_offs); + if (cfg_offs != cfg_ext_offs) + host->hwcfg.config_ext = + nand_readreg(ctrl, cfg_ext_offs); + host->hwcfg.acc_control = nand_readreg(ctrl, acc_control_offs); + host->hwcfg.timing_1 = nand_readreg(ctrl, t1_offs); + host->hwcfg.timing_2 = nand_readreg(ctrl, t2_offs); + } +} + +static int brcmnand_suspend(struct device *dev) +{ + struct brcmnand_controller *ctrl = dev_get_drvdata(dev); + struct brcmnand_host *host; + + list_for_each_entry(host, &ctrl->host_list, node) + brcmnand_save_restore_cs_config(host, 0); + + ctrl->nand_cs_nand_select = brcmnand_read_reg(ctrl, BRCMNAND_CS_SELECT); + ctrl->nand_cs_nand_xor = brcmnand_read_reg(ctrl, BRCMNAND_CS_XOR); + ctrl->corr_stat_threshold = + brcmnand_read_reg(ctrl, BRCMNAND_CORR_THRESHOLD); + + if (has_flash_dma(ctrl)) + ctrl->flash_dma_mode = flash_dma_readl(ctrl, FLASH_DMA_MODE); + + return 0; +} + +static int brcmnand_resume(struct device *dev) +{ + struct brcmnand_controller *ctrl = dev_get_drvdata(dev); + struct brcmnand_host *host; + + if (has_flash_dma(ctrl)) { + flash_dma_writel(ctrl, FLASH_DMA_MODE, ctrl->flash_dma_mode); + flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0); + } + + brcmnand_write_reg(ctrl, BRCMNAND_CS_SELECT, ctrl->nand_cs_nand_select); + brcmnand_write_reg(ctrl, BRCMNAND_CS_XOR, ctrl->nand_cs_nand_xor); + brcmnand_write_reg(ctrl, BRCMNAND_CORR_THRESHOLD, + ctrl->corr_stat_threshold); + + list_for_each_entry(host, &ctrl->host_list, node) { + struct mtd_info *mtd = &host->mtd; + struct nand_chip *chip = mtd->priv; + + brcmnand_save_restore_cs_config(host, 1); + + /* Reset the chip, required by some chips after power-up */ + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + } + + return 0; +} + +static const struct dev_pm_ops brcmnand_pm_ops = { + .suspend = brcmnand_suspend, + .resume = brcmnand_resume, +}; + +/*********************************************************************** + * Platform driver setup (per controller) + ***********************************************************************/ + +static int brcmnand_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *dn = dev->of_node, *child; + static struct brcmnand_controller *ctrl; + struct resource *res; + int ret; + + /* We only support device-tree instantiation */ + if (!dn) + return -ENODEV; + + ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + dev_set_drvdata(dev, ctrl); + ctrl->dev = dev; + + init_completion(&ctrl->done); + init_completion(&ctrl->dma_done); + spin_lock_init(&ctrl->controller.lock); + init_waitqueue_head(&ctrl->controller.wq); + INIT_LIST_HEAD(&ctrl->host_list); + + /* NAND register range */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ctrl->nand_base = devm_ioremap_resource(dev, res); + if (IS_ERR(ctrl->nand_base)) + return PTR_ERR(ctrl->nand_base); + + /* Initialize NAND revision */ + ret = brcmnand_revision_init(ctrl); + if (ret) + return ret; + + /* + * Most chips have this cache at a fixed offset within 'nand' block. + * Some must specify this region separately. + */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-cache"); + if (res) { + ctrl->nand_fc = devm_ioremap_resource(dev, res); + if (IS_ERR(ctrl->nand_fc)) + return PTR_ERR(ctrl->nand_fc); + } else { + ctrl->nand_fc = ctrl->nand_base + + ctrl->reg_offsets[BRCMNAND_FC_BASE]; + } + + /* FLASH_DMA */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "flash-dma"); + if (res) { + ctrl->flash_dma_base = devm_ioremap_resource(dev, res); + if (IS_ERR(ctrl->flash_dma_base)) + return PTR_ERR(ctrl->flash_dma_base); + + flash_dma_writel(ctrl, FLASH_DMA_MODE, 1); /* linked-list */ + flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0); + + /* Allocate descriptor(s) */ + ctrl->dma_desc = dmam_alloc_coherent(dev, + sizeof(*ctrl->dma_desc), + &ctrl->dma_pa, GFP_KERNEL); + if (!ctrl->dma_desc) + return -ENOMEM; + + ctrl->dma_irq = platform_get_irq(pdev, 1); + if ((int)ctrl->dma_irq < 0) { + dev_err(dev, "missing FLASH_DMA IRQ\n"); + return -ENODEV; + } + + ret = devm_request_irq(dev, ctrl->dma_irq, + brcmnand_dma_irq, 0, DRV_NAME, + ctrl); + if (ret < 0) { + dev_err(dev, "can't allocate IRQ %d: error %d\n", + ctrl->dma_irq, ret); + return ret; + } + + dev_info(dev, "enabling FLASH_DMA\n"); + } + + /* Disable automatic device ID config, direct addressing */ + brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT, + CS_SELECT_AUTO_DEVICE_ID_CFG | 0xff, 0, 0); + /* Disable XOR addressing */ + brcmnand_rmw_reg(ctrl, BRCMNAND_CS_XOR, 0xff, 0, 0); + + if (ctrl->features & BRCMNAND_HAS_WP) { + /* Permanently disable write protection */ + if (wp_on == 2) + brcmnand_set_wp(ctrl, false); + } else { + wp_on = 0; + } + + /* IRQ */ + ctrl->irq = platform_get_irq(pdev, 0); + if ((int)ctrl->irq < 0) { + dev_err(dev, "no IRQ defined\n"); + return -ENODEV; + } + + ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0, + DRV_NAME, ctrl); + if (ret < 0) { + dev_err(dev, "can't allocate IRQ %d: error %d\n", + ctrl->irq, ret); + return ret; + } + + for_each_available_child_of_node(dn, child) { + if (of_device_is_compatible(child, "brcm,nandcs")) { + struct brcmnand_host *host; + + host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); + if (!host) + return -ENOMEM; + host->pdev = pdev; + host->ctrl = ctrl; + host->of_node = child; + + ret = brcmnand_init_cs(host); + if (ret) + continue; /* Try all chip-selects */ + + list_add_tail(&host->node, &ctrl->host_list); + } + } + + /* No chip-selects could initialize properly */ + if (list_empty(&ctrl->host_list)) + return -ENODEV; + + return 0; +} + +static int brcmnand_remove(struct platform_device *pdev) +{ + struct brcmnand_controller *ctrl = dev_get_drvdata(&pdev->dev); + struct brcmnand_host *host; + + list_for_each_entry(host, &ctrl->host_list, node) + nand_release(&host->mtd); + + dev_set_drvdata(&pdev->dev, NULL); + + return 0; +} + +static const struct of_device_id brcmnand_of_match[] = { + { .compatible = "brcm,brcmnand-v4.0" }, + { .compatible = "brcm,brcmnand-v5.0" }, + { .compatible = "brcm,brcmnand-v6.0" }, + { .compatible = "brcm,brcmnand-v6.1" }, + { .compatible = "brcm,brcmnand-v7.0" }, + { .compatible = "brcm,brcmnand-v7.1" }, + {}, +}; +MODULE_DEVICE_TABLE(of, brcmnand_of_match); + +static struct platform_driver brcmstb_nand_driver = { + .probe = brcmnand_probe, + .remove = brcmnand_remove, + .driver = { + .name = DRV_NAME, + .pm = &brcmnand_pm_ops, + .of_match_table = of_match_ptr(brcmnand_of_match), + } +}; +module_platform_driver(brcmstb_nand_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Kevin Cernekee"); +MODULE_AUTHOR("Brian Norris"); +MODULE_DESCRIPTION("NAND driver for Broadcom STB chips"); +MODULE_ALIAS("platform:brcmnand"); -- 1.9.1 From mboxrd@z Thu Jan 1 00:00:00 1970 From: Brian Norris Subject: [PATCH v3 03/10] mtd: nand: add NAND driver for Broadcom STB NAND controller Date: Wed, 6 May 2015 10:59:47 -0700 Message-ID: <1430935194-7579-4-git-send-email-computersforpeace@gmail.com> References: <1430935194-7579-1-git-send-email-computersforpeace@gmail.com> Mime-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: base64 Return-path: In-Reply-To: <1430935194-7579-1-git-send-email-computersforpeace@gmail.com> List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-mtd" Errors-To: linux-mtd-bounces+gldm-linux-mtd-36=gmane.org@lists.infradead.org To: linux-mtd@lists.infradead.org Cc: devicetree@vger.kernel.org, Florian Fainelli , Scott Branden , Kevin Cernekee , Corneliu Doban , Ray Jui , =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= , linux-kernel@vger.kernel.org, Dan Ehrenberg , Jonathan Richardson , Anatol Pomazao , Gregory Fong , bcm-kernel-feedback-list@broadcom.com, Brian Norris , Dmitry Torokhov List-Id: devicetree@vger.kernel.org VGhpcyBjb3JlIG9yaWdpbmF0ZWQgaW4gU2V0LVRvcCBCb3ggY2hpcHMgKEJDTTd4eHgpIGJ1dCBp cyB1c2VkIGluIGEKdmFyaWV0eSBvZiBvdGhlciBCcm9hZGNvbSBjaGlwcywgaW5jbHVkaW5nIHNv bWUgQkNNNjN4eHgsIEJDTTMzeHgsIGFuZAppUHJvYy9DeWdudXMuIEl0J3MgYmVlbiB1c2VkIG9u bHkgb24gQVJNIGFuZCBNSVBTIFNvQ3MsIHNvIHJlc3RyaWN0IGl0CnRvIHRob3NlIGFyY2hpdGVj dHVyZXMuCgpUaGVyZSBhcmUgbXVsdGlwbGUgcmV2aXNpb25zIG9mIHRoaXMgY29yZSB0aHJvdWdo b3V0IHRoZSB5ZWFycywgYW5kCmFsbW9zdCBldmVyeSB2ZXJzaW9uIGJyb2tlIHJlZ2lzdGVyIGNv bXBhdGliaWxpdHkgaW4gc29tZSBzbWFsbCB3YXksIGJ1dAp3aXRoIHNvbWUgZWZmb3J0LCB0aGlz IGRyaXZlciBpcyBhYmxlIHRvIHN1cHBvcnQgdjQuMCwgdjUuMCwgdjYueCwgdjcuMCwKYW5kIHY3 LjEuIEl0J3MgYmVlbiB0ZXN0ZWQgb24gdjUuMCwgdjYuMCwgdjcuMCwgYW5kIHY3LjEgcmVjZW50 bHksIHNvCnRoZXJlIGhvcGVmdWxseSBhcmUgbm8gbW9yZSBsdXJraW5nIGluY29uc2lzdGVuY2ll cy4KClNpZ25lZC1vZmYtYnk6IEJyaWFuIE5vcnJpcyA8Y29tcHV0ZXJzZm9ycGVhY2VAZ21haWwu Y29tPgotLS0KIGRyaXZlcnMvbXRkL25hbmQvS2NvbmZpZyAgICAgICAgfCAgICA4ICsKIGRyaXZl cnMvbXRkL25hbmQvTWFrZWZpbGUgICAgICAgfCAgICAxICsKIGRyaXZlcnMvbXRkL25hbmQvYnJj bXN0Yl9uYW5kLmMgfCAyMTk4ICsrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysr KwogMyBmaWxlcyBjaGFuZ2VkLCAyMjA3IGluc2VydGlvbnMoKykKIGNyZWF0ZSBtb2RlIDEwMDY0 NCBkcml2ZXJzL210ZC9uYW5kL2JyY21zdGJfbmFuZC5jCgpkaWZmIC0tZ2l0IGEvZHJpdmVycy9t dGQvbmFuZC9LY29uZmlnIGIvZHJpdmVycy9tdGQvbmFuZC9LY29uZmlnCmluZGV4IDU4OTdkOGQ4 ZmE1YS4uMzU4NzAxN2IyMDlhIDEwMDY0NAotLS0gYS9kcml2ZXJzL210ZC9uYW5kL0tjb25maWcK KysrIGIvZHJpdmVycy9tdGQvbmFuZC9LY29uZmlnCkBAIC0zOTQsNiArMzk0LDE0IEBAIGNvbmZp ZyBNVERfTkFORF9HUE1JX05BTkQKIAkgYmxvY2ssIHN1Y2ggYXMgU0QgY2FyZC4gU28gcGF5IGF0 dGVudGlvbiB0byBpdCB3aGVuIHlvdSBlbmFibGUKIAkgdGhlIEdQTUkuCiAKK2NvbmZpZyBNVERf TkFORF9CUkNNU1RCCisJdHJpc3RhdGUgIkJyb2FkY29tIFNUQiBOQU5EIGNvbnRyb2xsZXIiCisJ ZGVwZW5kcyBvbiBBUk0gfHwgTUlQUworCWhlbHAKKwkgIEVuYWJsZXMgdGhlIEJyb2FkY29tIE5B TkQgY29udHJvbGxlciBkcml2ZXIuIFRoZSBjb250cm9sbGVyIHdhcworCSAgb3JpZ2luYWxseSBk ZXNpZ25lZCBmb3IgU2V0LVRvcCBCb3ggYnV0IGlzIHVzZWQgb24gdmFyaW91cyBCQ003eHh4LAor CSAgQkNNM3h4eCwgQkNNNjN4eHgsIGlQcm9jL0N5Z251cyBhbmQgbW9yZS4KKwogY29uZmlnIE1U RF9OQU5EX0JDTTQ3WFhORkxBU0gKIAl0cmlzdGF0ZSAiU3VwcG9ydCBmb3IgTkFORCBmbGFzaCBv biBCQ000NzA2IEJDTUEgYnVzIgogCWRlcGVuZHMgb24gQkNNQV9ORkxBU0gKZGlmZiAtLWdpdCBh L2RyaXZlcnMvbXRkL25hbmQvTWFrZWZpbGUgYi9kcml2ZXJzL210ZC9uYW5kL01ha2VmaWxlCmlu ZGV4IDU4MmJiZDA1YWZmNy4uM2IxYWRkZGM4M2RkIDEwMDY0NAotLS0gYS9kcml2ZXJzL210ZC9u YW5kL01ha2VmaWxlCisrKyBiL2RyaXZlcnMvbXRkL25hbmQvTWFrZWZpbGUKQEAgLTUyLDUgKzUy LDYgQEAgb2JqLSQoQ09ORklHX01URF9OQU5EX1hXQVkpCQkrPSB4d2F5X25hbmQubwogb2JqLSQo Q09ORklHX01URF9OQU5EX0JDTTQ3WFhORkxBU0gpCSs9IGJjbTQ3eHhuZmxhc2gvCiBvYmotJChD T05GSUdfTVREX05BTkRfU1VOWEkpCQkrPSBzdW54aV9uYW5kLm8KIG9iai0kKENPTkZJR19NVERf TkFORF9ISVNJNTA0KQkgICAgICAgICs9IGhpc2k1MDRfbmFuZC5vCitvYmotJChDT05GSUdfTVRE X05BTkRfQlJDTVNUQikJCSs9IGJyY21zdGJfbmFuZC5vCiAKIG5hbmQtb2JqcyA6PSBuYW5kX2Jh c2UubyBuYW5kX2JidC5vIG5hbmRfdGltaW5ncy5vCmRpZmYgLS1naXQgYS9kcml2ZXJzL210ZC9u YW5kL2JyY21zdGJfbmFuZC5jIGIvZHJpdmVycy9tdGQvbmFuZC9icmNtc3RiX25hbmQuYwpuZXcg ZmlsZSBtb2RlIDEwMDY0NAppbmRleCAwMDAwMDAwMDAwMDAuLmVjNjVhNDhkMjQ4NwotLS0gL2Rl di9udWxsCisrKyBiL2RyaXZlcnMvbXRkL25hbmQvYnJjbXN0Yl9uYW5kLmMKQEAgLTAsMCArMSwy MTk4IEBACisvKgorICogQ29weXJpZ2h0IMKpIDIwMTAtMjAxNSBCcm9hZGNvbSBDb3Jwb3JhdGlv bgorICoKKyAqIFRoaXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJp YnV0ZSBpdCBhbmQvb3IgbW9kaWZ5CisgKiBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBH ZW5lcmFsIFB1YmxpYyBMaWNlbnNlIHZlcnNpb24gMiBhcworICogcHVibGlzaGVkIGJ5IHRoZSBG cmVlIFNvZnR3YXJlIEZvdW5kYXRpb24uCisgKgorICogVGhpcyBwcm9ncmFtIGlzIGRpc3RyaWJ1 dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsCisgKiBidXQgV0lUSE9VVCBB TlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZgorICogTUVS Q0hBTlRBQklMSVRZIG9yIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiBTZWUgdGhl CisgKiBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBmb3IgbW9yZSBkZXRhaWxzLgorICovCisK KyNpbmNsdWRlIDxsaW51eC92ZXJzaW9uLmg+CisjaW5jbHVkZSA8bGludXgvbW9kdWxlLmg+Cisj aW5jbHVkZSA8bGludXgvaW5pdC5oPgorI2luY2x1ZGUgPGxpbnV4L2RlbGF5Lmg+CisjaW5jbHVk ZSA8bGludXgvZGV2aWNlLmg+CisjaW5jbHVkZSA8bGludXgvcGxhdGZvcm1fZGV2aWNlLmg+Cisj aW5jbHVkZSA8bGludXgvZXJyLmg+CisjaW5jbHVkZSA8bGludXgvY29tcGxldGlvbi5oPgorI2lu Y2x1ZGUgPGxpbnV4L2ludGVycnVwdC5oPgorI2luY2x1ZGUgPGxpbnV4L3NwaW5sb2NrLmg+Cisj aW5jbHVkZSA8bGludXgvZG1hLW1hcHBpbmcuaD4KKyNpbmNsdWRlIDxsaW51eC9pb3BvcnQuaD4K KyNpbmNsdWRlIDxsaW51eC9idWcuaD4KKyNpbmNsdWRlIDxsaW51eC9rZXJuZWwuaD4KKyNpbmNs dWRlIDxsaW51eC9iaXRvcHMuaD4KKyNpbmNsdWRlIDxsaW51eC9tbS5oPgorI2luY2x1ZGUgPGxp bnV4L210ZC9tdGQuaD4KKyNpbmNsdWRlIDxsaW51eC9tdGQvbmFuZC5oPgorI2luY2x1ZGUgPGxp bnV4L210ZC9wYXJ0aXRpb25zLmg+CisjaW5jbHVkZSA8bGludXgvb2YuaD4KKyNpbmNsdWRlIDxs aW51eC9vZl9tdGQuaD4KKyNpbmNsdWRlIDxsaW51eC9vZl9wbGF0Zm9ybS5oPgorI2luY2x1ZGUg PGxpbnV4L3NsYWIuaD4KKyNpbmNsdWRlIDxsaW51eC9saXN0Lmg+CisjaW5jbHVkZSA8bGludXgv bG9nMi5oPgorCisvKgorICogVGhpcyBmbGFnIGNvbnRyb2xzIGlmIFdQIHN0YXlzIG9uIGJldHdl ZW4gZXJhc2Uvd3JpdGUgY29tbWFuZHMgdG8gbWl0aWdhdGUKKyAqIGZsYXNoIGNvcnJ1cHRpb24g ZHVlIHRvIHBvd2VyIGdsaXRjaGVzLiBWYWx1ZXM6CisgKiAwOiBOQU5EX1dQIGlzIG5vdCB1c2Vk IG9yIG5vdCBhdmFpbGFibGUKKyAqIDE6IE5BTkRfV1AgaXMgc2V0IGJ5IGRlZmF1bHQsIGNsZWFy ZWQgZm9yIGVyYXNlL3dyaXRlIG9wZXJhdGlvbnMKKyAqIDI6IE5BTkRfV1AgaXMgYWx3YXlzIGNs ZWFyZWQKKyAqLworc3RhdGljIGludCB3cF9vbiA9IDE7Cittb2R1bGVfcGFyYW0od3Bfb24sIGlu dCwgMDQ0NCk7CisKKy8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioq KioqKioqKioqKioqKioqKioqKioqKioqKioqKgorICogRGVmaW5pdGlvbnMKKyAqKioqKioqKioq KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioq KioqKi8KKworI2RlZmluZSBEUlZfTkFNRQkJCSJicmNtc3RiX25hbmQiCisKKyNkZWZpbmUgQ01E X05VTEwJCQkweDAwCisjZGVmaW5lIENNRF9QQUdFX1JFQUQJCQkweDAxCisjZGVmaW5lIENNRF9T UEFSRV9BUkVBX1JFQUQJCTB4MDIKKyNkZWZpbmUgQ01EX1NUQVRVU19SRUFECQkJMHgwMworI2Rl ZmluZSBDTURfUFJPR1JBTV9QQUdFCQkweDA0CisjZGVmaW5lIENNRF9QUk9HUkFNX1NQQVJFX0FS RUEJCTB4MDUKKyNkZWZpbmUgQ01EX0NPUFlfQkFDSwkJCTB4MDYKKyNkZWZpbmUgQ01EX0RFVklD RV9JRF9SRUFECQkweDA3CisjZGVmaW5lIENNRF9CTE9DS19FUkFTRQkJCTB4MDgKKyNkZWZpbmUg Q01EX0ZMQVNIX1JFU0VUCQkJMHgwOQorI2RlZmluZSBDTURfQkxPQ0tTX0xPQ0sJCQkweDBhCisj ZGVmaW5lIENNRF9CTE9DS1NfTE9DS19ET1dOCQkweDBiCisjZGVmaW5lIENNRF9CTE9DS1NfVU5M T0NLCQkweDBjCisjZGVmaW5lIENNRF9SRUFEX0JMT0NLU19MT0NLX1NUQVRVUwkweDBkCisjZGVm aW5lIENNRF9QQVJBTUVURVJfUkVBRAkJMHgwZQorI2RlZmluZSBDTURfUEFSQU1FVEVSX0NIQU5H RV9DT0wJMHgwZgorI2RlZmluZSBDTURfTE9XX0xFVkVMX09QCQkweDEwCisKK3N0cnVjdCBicmNt X25hbmRfZG1hX2Rlc2MgeworCXUzMiBuZXh0X2Rlc2M7CisJdTMyIG5leHRfZGVzY19leHQ7CisJ dTMyIGNtZF9pcnE7CisJdTMyIGRyYW1fYWRkcjsKKwl1MzIgZHJhbV9hZGRyX2V4dDsKKwl1MzIg dGZyX2xlbjsKKwl1MzIgdG90YWxfbGVuOworCXUzMiBmbGFzaF9hZGRyOworCXUzMiBmbGFzaF9h ZGRyX2V4dDsKKwl1MzIgY3M7CisJdTMyIHBhZDJbNV07CisJdTMyIHN0YXR1c192YWxpZDsKK30g X19wYWNrZWQ7CisKKy8qIEJpdGZpZWxkcyBmb3IgYnJjbV9uYW5kX2RtYV9kZXNjOjpzdGF0dXNf dmFsaWQgKi8KKyNkZWZpbmUgRkxBU0hfRE1BX0VDQ19FUlJPUgkoMSA8PCA4KQorI2RlZmluZSBG TEFTSF9ETUFfQ09SUl9FUlJPUgkoMSA8PCA5KQorCisvKiA1MTJCIGZsYXNoIGNhY2hlIGluIHRo ZSBOQU5EIGNvbnRyb2xsZXIgSFcgKi8KKyNkZWZpbmUgRkNfU0hJRlQJCTlVCisjZGVmaW5lIEZD X0JZVEVTCQk1MTJVCisjZGVmaW5lIEZDX1dPUkRTCQkoRkNfQllURVMgPj4gMikKKworI2RlZmlu ZSBCUkNNTkFORF9NSU5fUEFHRVNJWkUJNTEyCisjZGVmaW5lIEJSQ01OQU5EX01JTl9CTE9DS1NJ WkUJKDggKiAxMDI0KQorI2RlZmluZSBCUkNNTkFORF9NSU5fREVWU0laRQkoNFVMTCAqIDEwMjQg KiAxMDI0KQorCisvKiBDb250cm9sbGVyIGZlYXR1cmUgZmxhZ3MgKi8KK2VudW0geworCUJSQ01O QU5EX0hBU18xS19TRUNUT1JTCQkJPSBCSVQoMCksCisJQlJDTU5BTkRfSEFTX1BSRUZFVENICQkJ PSBCSVQoMSksCisJQlJDTU5BTkRfSEFTX0NBQ0hFX01PREUJCQk9IEJJVCgyKSwKKwlCUkNNTkFO RF9IQVNfV1AJCQkJPSBCSVQoMyksCit9OworCitzdHJ1Y3QgYnJjbW5hbmRfY29udHJvbGxlciB7 CisJc3RydWN0IGRldmljZQkJKmRldjsKKwlzdHJ1Y3QgbmFuZF9od19jb250cm9sCWNvbnRyb2xs ZXI7CisJdm9pZCBfX2lvbWVtCQkqbmFuZF9iYXNlOworCXZvaWQgX19pb21lbQkJKm5hbmRfZmM7 IC8qIGZsYXNoIGNhY2hlICovCisJdm9pZCBfX2lvbWVtCQkqZmxhc2hfZG1hX2Jhc2U7CisJdW5z aWduZWQgaW50CQlpcnE7CisJdW5zaWduZWQgaW50CQlkbWFfaXJxOworCWludAkJCW5hbmRfdmVy c2lvbjsKKworCWludAkJCWNtZF9wZW5kaW5nOworCWJvb2wJCQlkbWFfcGVuZGluZzsKKwlzdHJ1 Y3QgY29tcGxldGlvbglkb25lOworCXN0cnVjdCBjb21wbGV0aW9uCWRtYV9kb25lOworCisJLyog TGlzdCBvZiBOQU5EIGhvc3RzIChvbmUgZm9yIGVhY2ggY2hpcC1zZWxlY3QpICovCisJc3RydWN0 IGxpc3RfaGVhZCBob3N0X2xpc3Q7CisKKwlzdHJ1Y3QgYnJjbV9uYW5kX2RtYV9kZXNjICpkbWFf ZGVzYzsKKwlkbWFfYWRkcl90CQlkbWFfcGE7CisKKwkvKiBpbi1tZW1vcnkgY2FjaGUgb2YgdGhl IEZMQVNIX0NBQ0hFLCB1c2VkIG9ubHkgZm9yIHNvbWUgY29tbWFuZHMgKi8KKwl1MzIJCQlmbGFz aF9jYWNoZVtGQ19XT1JEU107CisKKwkvKiBDb250cm9sbGVyIHJldmlzaW9uIGRldGFpbHMgKi8K Kwljb25zdCB1MTYJCSpyZWdfb2Zmc2V0czsKKwl1bnNpZ25lZCBpbnQJCXJlZ19zcGFjaW5nOyAv KiBiZXR3ZWVuIENTMSwgQ1MyLCAuLi4gcmVncyAqLworCWNvbnN0IHU4CQkqY3Nfb2Zmc2V0czsg Lyogd2l0aGluIGVhY2ggY2hpcC1zZWxlY3QgKi8KKwljb25zdCB1OAkJKmNzMF9vZmZzZXRzOyAv KiB3aXRoaW4gQ1MwLCBpZiBkaWZmZXJlbnQgKi8KKwl1bnNpZ25lZCBpbnQJCW1heF9ibG9ja19z aXplOworCWNvbnN0IHVuc2lnbmVkIGludAkqYmxvY2tfc2l6ZXM7CisJdW5zaWduZWQgaW50CQlt YXhfcGFnZV9zaXplOworCWNvbnN0IHVuc2lnbmVkIGludAkqcGFnZV9zaXplczsKKwl1bnNpZ25l ZCBpbnQJCW1heF9vb2I7CisJdTMyCQkJZmVhdHVyZXM7CisKKwkvKiBmb3IgbG93LXBvd2VyIHN0 YW5kYnkvcmVzdW1lIG9ubHkgKi8KKwl1MzIJCQluYW5kX2NzX25hbmRfc2VsZWN0OworCXUzMgkJ CW5hbmRfY3NfbmFuZF94b3I7CisJdTMyCQkJY29ycl9zdGF0X3RocmVzaG9sZDsKKwl1MzIJCQlm bGFzaF9kbWFfbW9kZTsKK307CisKK3N0cnVjdCBicmNtbmFuZF9jZmcgeworCXU2NAkJCWRldmlj ZV9zaXplOworCXVuc2lnbmVkIGludAkJYmxvY2tfc2l6ZTsKKwl1bnNpZ25lZCBpbnQJCXBhZ2Vf c2l6ZTsKKwl1bnNpZ25lZCBpbnQJCXNwYXJlX2FyZWFfc2l6ZTsKKwl1bnNpZ25lZCBpbnQJCWRl dmljZV93aWR0aDsKKwl1bnNpZ25lZCBpbnQJCWNvbF9hZHJfYnl0ZXM7CisJdW5zaWduZWQgaW50 CQlibGtfYWRyX2J5dGVzOworCXVuc2lnbmVkIGludAkJZnVsX2Fkcl9ieXRlczsKKwl1bnNpZ25l ZCBpbnQJCXNlY3Rvcl9zaXplXzFrOworCXVuc2lnbmVkIGludAkJZWNjX2xldmVsOworCS8qIHVz ZSBmb3IgbG93LXBvd2VyIHN0YW5kYnkvcmVzdW1lIG9ubHkgKi8KKwl1MzIJCQlhY2NfY29udHJv bDsKKwl1MzIJCQljb25maWc7CisJdTMyCQkJY29uZmlnX2V4dDsKKwl1MzIJCQl0aW1pbmdfMTsK Kwl1MzIJCQl0aW1pbmdfMjsKK307CisKK3N0cnVjdCBicmNtbmFuZF9ob3N0IHsKKwlzdHJ1Y3Qg bGlzdF9oZWFkCW5vZGU7CisJc3RydWN0IGRldmljZV9ub2RlCSpvZl9ub2RlOworCisJc3RydWN0 IG5hbmRfY2hpcAljaGlwOworCXN0cnVjdCBtdGRfaW5mbwkJbXRkOworCXN0cnVjdCBwbGF0Zm9y bV9kZXZpY2UJKnBkZXY7CisJaW50CQkJY3M7CisKKwl1bnNpZ25lZCBpbnQJCWxhc3RfY21kOwor CXVuc2lnbmVkIGludAkJbGFzdF9ieXRlOworCXU2NAkJCWxhc3RfYWRkcjsKKwlzdHJ1Y3QgYnJj bW5hbmRfY2ZnCWh3Y2ZnOworCXN0cnVjdCBicmNtbmFuZF9jb250cm9sbGVyICpjdHJsOworfTsK KworZW51bSBicmNtbmFuZF9yZWcgeworCUJSQ01OQU5EX0NNRF9TVEFSVCA9IDAsCisJQlJDTU5B TkRfQ01EX0VYVF9BRERSRVNTLAorCUJSQ01OQU5EX0NNRF9BRERSRVNTLAorCUJSQ01OQU5EX0lO VEZDX1NUQVRVUywKKwlCUkNNTkFORF9DU19TRUxFQ1QsCisJQlJDTU5BTkRfQ1NfWE9SLAorCUJS Q01OQU5EX0xMX09QLAorCUJSQ01OQU5EX0NTMF9CQVNFLAorCUJSQ01OQU5EX0NTMV9CQVNFLAkJ LyogQ1MxIHJlZ3MsIGlmIG5vbi1jb250aWd1b3VzICovCisJQlJDTU5BTkRfQ09SUl9USFJFU0hP TEQsCisJQlJDTU5BTkRfQ09SUl9USFJFU0hPTERfRVhULAorCUJSQ01OQU5EX1VOQ09SUl9DT1VO VCwKKwlCUkNNTkFORF9DT1JSX0NPVU5ULAorCUJSQ01OQU5EX0NPUlJfRVhUX0FERFIsCisJQlJD TU5BTkRfQ09SUl9BRERSLAorCUJSQ01OQU5EX1VOQ09SUl9FWFRfQUREUiwKKwlCUkNNTkFORF9V TkNPUlJfQUREUiwKKwlCUkNNTkFORF9TRU1BUEhPUkUsCisJQlJDTU5BTkRfSUQsCisJQlJDTU5B TkRfSURfRVhULAorCUJSQ01OQU5EX0xMX1JEQVRBLAorCUJSQ01OQU5EX09PQl9SRUFEX0JBU0Us CisJQlJDTU5BTkRfT09CX1JFQURfMTBfQkFTRSwJLyogb2Zmc2V0IDB4MTAsIGlmIG5vbi1jb250 aWd1b3VzICovCisJQlJDTU5BTkRfT09CX1dSSVRFX0JBU0UsCisJQlJDTU5BTkRfT09CX1dSSVRF XzEwX0JBU0UsCS8qIG9mZnNldCAweDEwLCBpZiBub24tY29udGlndW91cyAqLworCUJSQ01OQU5E X0ZDX0JBU0UsCit9OworCisvKiBCUkNNTkFORCB2NC4wICovCitzdGF0aWMgY29uc3QgdTE2IGJy Y21uYW5kX3JlZ3NfdjQwW10gPSB7CisJW0JSQ01OQU5EX0NNRF9TVEFSVF0JCT0gIDB4MDQsCisJ W0JSQ01OQU5EX0NNRF9FWFRfQUREUkVTU10JPSAgMHgwOCwKKwlbQlJDTU5BTkRfQ01EX0FERFJF U1NdCQk9ICAweDBjLAorCVtCUkNNTkFORF9JTlRGQ19TVEFUVVNdCQk9ICAweDZjLAorCVtCUkNN TkFORF9DU19TRUxFQ1RdCQk9ICAweDE0LAorCVtCUkNNTkFORF9DU19YT1JdCQk9ICAweDE4LAor CVtCUkNNTkFORF9MTF9PUF0JCT0gMHgxNzgsCisJW0JSQ01OQU5EX0NTMF9CQVNFXQkJPSAgMHg0 MCwKKwlbQlJDTU5BTkRfQ1MxX0JBU0VdCQk9ICAweGQwLAorCVtCUkNNTkFORF9DT1JSX1RIUkVT SE9MRF0JPSAgMHg4NCwKKwlbQlJDTU5BTkRfQ09SUl9USFJFU0hPTERfRVhUXQk9ICAgICAwLAor CVtCUkNNTkFORF9VTkNPUlJfQ09VTlRdCQk9ICAgICAwLAorCVtCUkNNTkFORF9DT1JSX0NPVU5U XQkJPSAgICAgMCwKKwlbQlJDTU5BTkRfQ09SUl9FWFRfQUREUl0JPSAgMHg3MCwKKwlbQlJDTU5B TkRfQ09SUl9BRERSXQkJPSAgMHg3NCwKKwlbQlJDTU5BTkRfVU5DT1JSX0VYVF9BRERSXQk9ICAw eDc4LAorCVtCUkNNTkFORF9VTkNPUlJfQUREUl0JCT0gIDB4N2MsCisJW0JSQ01OQU5EX1NFTUFQ SE9SRV0JCT0gIDB4NTgsCisJW0JSQ01OQU5EX0lEXQkJCT0gIDB4NjAsCisJW0JSQ01OQU5EX0lE X0VYVF0JCT0gIDB4NjQsCisJW0JSQ01OQU5EX0xMX1JEQVRBXQkJPSAweDE3YywKKwlbQlJDTU5B TkRfT09CX1JFQURfQkFTRV0JPSAgMHgyMCwKKwlbQlJDTU5BTkRfT09CX1JFQURfMTBfQkFTRV0J PSAweDEzMCwKKwlbQlJDTU5BTkRfT09CX1dSSVRFX0JBU0VdCT0gIDB4MzAsCisJW0JSQ01OQU5E X09PQl9XUklURV8xMF9CQVNFXQk9ICAgICAwLAorCVtCUkNNTkFORF9GQ19CQVNFXQkJPSAweDIw MCwKK307CisKKy8qIEJSQ01OQU5EIHY1LjAgKi8KK3N0YXRpYyBjb25zdCB1MTYgYnJjbW5hbmRf cmVnc192NTBbXSA9IHsKKwlbQlJDTU5BTkRfQ01EX1NUQVJUXQkJPSAgMHgwNCwKKwlbQlJDTU5B TkRfQ01EX0VYVF9BRERSRVNTXQk9ICAweDA4LAorCVtCUkNNTkFORF9DTURfQUREUkVTU10JCT0g IDB4MGMsCisJW0JSQ01OQU5EX0lOVEZDX1NUQVRVU10JCT0gIDB4NmMsCisJW0JSQ01OQU5EX0NT X1NFTEVDVF0JCT0gIDB4MTQsCisJW0JSQ01OQU5EX0NTX1hPUl0JCT0gIDB4MTgsCisJW0JSQ01O QU5EX0xMX09QXQkJPSAweDE3OCwKKwlbQlJDTU5BTkRfQ1MwX0JBU0VdCQk9ICAweDQwLAorCVtC UkNNTkFORF9DUzFfQkFTRV0JCT0gIDB4ZDAsCisJW0JSQ01OQU5EX0NPUlJfVEhSRVNIT0xEXQk9 ICAweDg0LAorCVtCUkNNTkFORF9DT1JSX1RIUkVTSE9MRF9FWFRdCT0gICAgIDAsCisJW0JSQ01O QU5EX1VOQ09SUl9DT1VOVF0JCT0gICAgIDAsCisJW0JSQ01OQU5EX0NPUlJfQ09VTlRdCQk9ICAg ICAwLAorCVtCUkNNTkFORF9DT1JSX0VYVF9BRERSXQk9ICAweDcwLAorCVtCUkNNTkFORF9DT1JS X0FERFJdCQk9ICAweDc0LAorCVtCUkNNTkFORF9VTkNPUlJfRVhUX0FERFJdCT0gIDB4NzgsCisJ W0JSQ01OQU5EX1VOQ09SUl9BRERSXQkJPSAgMHg3YywKKwlbQlJDTU5BTkRfU0VNQVBIT1JFXQkJ PSAgMHg1OCwKKwlbQlJDTU5BTkRfSURdCQkJPSAgMHg2MCwKKwlbQlJDTU5BTkRfSURfRVhUXQkJ PSAgMHg2NCwKKwlbQlJDTU5BTkRfTExfUkRBVEFdCQk9IDB4MTdjLAorCVtCUkNNTkFORF9PT0Jf UkVBRF9CQVNFXQk9ICAweDIwLAorCVtCUkNNTkFORF9PT0JfUkVBRF8xMF9CQVNFXQk9IDB4MTMw LAorCVtCUkNNTkFORF9PT0JfV1JJVEVfQkFTRV0JPSAgMHgzMCwKKwlbQlJDTU5BTkRfT09CX1dS SVRFXzEwX0JBU0VdCT0gMHgxNDAsCisJW0JSQ01OQU5EX0ZDX0JBU0VdCQk9IDB4MjAwLAorfTsK KworLyogQlJDTU5BTkQgdjYuMCAtIHY3LjEgKi8KK3N0YXRpYyBjb25zdCB1MTYgYnJjbW5hbmRf cmVnc192NjBbXSA9IHsKKwlbQlJDTU5BTkRfQ01EX1NUQVJUXQkJPSAgMHgwNCwKKwlbQlJDTU5B TkRfQ01EX0VYVF9BRERSRVNTXQk9ICAweDA4LAorCVtCUkNNTkFORF9DTURfQUREUkVTU10JCT0g IDB4MGMsCisJW0JSQ01OQU5EX0lOVEZDX1NUQVRVU10JCT0gIDB4MTQsCisJW0JSQ01OQU5EX0NT X1NFTEVDVF0JCT0gIDB4MTgsCisJW0JSQ01OQU5EX0NTX1hPUl0JCT0gIDB4MWMsCisJW0JSQ01O QU5EX0xMX09QXQkJPSAgMHgyMCwKKwlbQlJDTU5BTkRfQ1MwX0JBU0VdCQk9ICAweDUwLAorCVtC UkNNTkFORF9DUzFfQkFTRV0JCT0gICAgIDAsCisJW0JSQ01OQU5EX0NPUlJfVEhSRVNIT0xEXQk9 ICAweGMwLAorCVtCUkNNTkFORF9DT1JSX1RIUkVTSE9MRF9FWFRdCT0gIDB4YzQsCisJW0JSQ01O QU5EX1VOQ09SUl9DT1VOVF0JCT0gIDB4ZmMsCisJW0JSQ01OQU5EX0NPUlJfQ09VTlRdCQk9IDB4 MTAwLAorCVtCUkNNTkFORF9DT1JSX0VYVF9BRERSXQk9IDB4MTBjLAorCVtCUkNNTkFORF9DT1JS X0FERFJdCQk9IDB4MTEwLAorCVtCUkNNTkFORF9VTkNPUlJfRVhUX0FERFJdCT0gMHgxMTQsCisJ W0JSQ01OQU5EX1VOQ09SUl9BRERSXQkJPSAweDExOCwKKwlbQlJDTU5BTkRfU0VNQVBIT1JFXQkJ PSAweDE1MCwKKwlbQlJDTU5BTkRfSURdCQkJPSAweDE5NCwKKwlbQlJDTU5BTkRfSURfRVhUXQkJ PSAweDE5OCwKKwlbQlJDTU5BTkRfTExfUkRBVEFdCQk9IDB4MTljLAorCVtCUkNNTkFORF9PT0Jf UkVBRF9CQVNFXQk9IDB4MjAwLAorCVtCUkNNTkFORF9PT0JfUkVBRF8xMF9CQVNFXQk9ICAgICAw LAorCVtCUkNNTkFORF9PT0JfV1JJVEVfQkFTRV0JPSAweDI4MCwKKwlbQlJDTU5BTkRfT09CX1dS SVRFXzEwX0JBU0VdCT0gICAgIDAsCisJW0JSQ01OQU5EX0ZDX0JBU0VdCQk9IDB4NDAwLAorfTsK KworZW51bSBicmNtbmFuZF9jc19yZWcgeworCUJSQ01OQU5EX0NTX0NGR19FWFQgPSAwLAorCUJS Q01OQU5EX0NTX0NGRywKKwlCUkNNTkFORF9DU19BQ0NfQ09OVFJPTCwKKwlCUkNNTkFORF9DU19U SU1JTkcxLAorCUJSQ01OQU5EX0NTX1RJTUlORzIsCit9OworCisvKiBQZXIgY2hpcC1zZWxlY3Qg b2Zmc2V0cyBmb3IgdjcuMSAqLworc3RhdGljIGNvbnN0IHU4IGJyY21uYW5kX2NzX29mZnNldHNf djcxW10gPSB7CisJW0JSQ01OQU5EX0NTX0FDQ19DT05UUk9MXQk9IDB4MDAsCisJW0JSQ01OQU5E X0NTX0NGR19FWFRdCQk9IDB4MDQsCisJW0JSQ01OQU5EX0NTX0NGR10JCT0gMHgwOCwKKwlbQlJD TU5BTkRfQ1NfVElNSU5HMV0JCT0gMHgwYywKKwlbQlJDTU5BTkRfQ1NfVElNSU5HMl0JCT0gMHgx MCwKK307CisKKy8qIFBlciBjaGlwLXNlbGVjdCBvZmZzZXRzIGZvciBwcmUgdjcuMSwgZXhjZXB0 IENTMCBvbiA8PSB2NS4wICovCitzdGF0aWMgY29uc3QgdTggYnJjbW5hbmRfY3Nfb2Zmc2V0c1td ID0geworCVtCUkNNTkFORF9DU19BQ0NfQ09OVFJPTF0JPSAweDAwLAorCVtCUkNNTkFORF9DU19D RkdfRVhUXQkJPSAweDA0LAorCVtCUkNNTkFORF9DU19DRkddCQk9IDB4MDQsCisJW0JSQ01OQU5E X0NTX1RJTUlORzFdCQk9IDB4MDgsCisJW0JSQ01OQU5EX0NTX1RJTUlORzJdCQk9IDB4MGMsCit9 OworCisvKiBQZXIgY2hpcC1zZWxlY3Qgb2Zmc2V0IGZvciA8PSB2NS4wIG9uIENTMCBvbmx5ICov CitzdGF0aWMgY29uc3QgdTggYnJjbW5hbmRfY3Nfb2Zmc2V0c19jczBbXSA9IHsKKwlbQlJDTU5B TkRfQ1NfQUNDX0NPTlRST0xdCT0gMHgwMCwKKwlbQlJDTU5BTkRfQ1NfQ0ZHX0VYVF0JCT0gMHgw OCwKKwlbQlJDTU5BTkRfQ1NfQ0ZHXQkJPSAweDA4LAorCVtCUkNNTkFORF9DU19USU1JTkcxXQkJ PSAweDEwLAorCVtCUkNNTkFORF9DU19USU1JTkcyXQkJPSAweDE0LAorfTsKKworLyogQlJDTU5B TkRfSU5URkNfU1RBVFVTICovCitlbnVtIHsKKwlJTlRGQ19GTEFTSF9TVEFUVVMJCT0gR0VOTUFT Syg3LCAwKSwKKworCUlOVEZDX0VSQVNFRAkJCT0gQklUKDI3KSwKKwlJTlRGQ19PT0JfVkFMSUQJ CQk9IEJJVCgyOCksCisJSU5URkNfQ0FDSEVfVkFMSUQJCT0gQklUKDI5KSwKKwlJTlRGQ19GTEFT SF9SRUFEWQkJPSBCSVQoMzApLAorCUlOVEZDX0NUTFJfUkVBRFkJCT0gQklUKDMxKSwKK307CisK K3N0YXRpYyBpbmxpbmUgdTMyIG5hbmRfcmVhZHJlZyhzdHJ1Y3QgYnJjbW5hbmRfY29udHJvbGxl ciAqY3RybCwgdTMyIG9mZnMpCit7CisJcmV0dXJuIF9fcmF3X3JlYWRsKGN0cmwtPm5hbmRfYmFz ZSArIG9mZnMpOworfQorCitzdGF0aWMgaW5saW5lIHZvaWQgbmFuZF93cml0ZXJlZyhzdHJ1Y3Qg YnJjbW5hbmRfY29udHJvbGxlciAqY3RybCwgdTMyIG9mZnMsCisJCQkJIHUzMiB2YWwpCit7CisJ X19yYXdfd3JpdGVsKHZhbCwgY3RybC0+bmFuZF9iYXNlICsgb2Zmcyk7Cit9CisKK3N0YXRpYyBp bnQgYnJjbW5hbmRfcmV2aXNpb25faW5pdChzdHJ1Y3QgYnJjbW5hbmRfY29udHJvbGxlciAqY3Ry bCkKK3sKKwlzdGF0aWMgY29uc3QgdW5zaWduZWQgaW50IGJsb2NrX3NpemVzX3Y2W10gPSB7IDgs IDE2LCAxMjgsIDI1NiwgNTEyLCAxMDI0LCAyMDQ4LCAwIH07CisJc3RhdGljIGNvbnN0IHVuc2ln bmVkIGludCBibG9ja19zaXplc192NFtdID0geyAxNiwgMTI4LCA4LCA1MTIsIDI1NiwgMTAyNCwg MjA0OCwgMCB9OworCXN0YXRpYyBjb25zdCB1bnNpZ25lZCBpbnQgcGFnZV9zaXplc1tdID0geyA1 MTIsIDIwNDgsIDQwOTYsIDgxOTIsIDAgfTsKKworCWN0cmwtPm5hbmRfdmVyc2lvbiA9IG5hbmRf cmVhZHJlZyhjdHJsLCAwKSAmIDB4ZmZmZjsKKworCS8qIE9ubHkgc3VwcG9ydCB2NC4wKz8gKi8K KwlpZiAoY3RybC0+bmFuZF92ZXJzaW9uIDwgMHgwNDAwKSB7CisJCWRldl9lcnIoY3RybC0+ZGV2 LCAidmVyc2lvbiAlI3ggbm90IHN1cHBvcnRlZFxuIiwKKwkJCWN0cmwtPm5hbmRfdmVyc2lvbik7 CisJCXJldHVybiAtRU5PREVWOworCX0KKworCS8qIFJlZ2lzdGVyIG9mZnNldHMgKi8KKwlpZiAo Y3RybC0+bmFuZF92ZXJzaW9uID49IDB4MDYwMCkKKwkJY3RybC0+cmVnX29mZnNldHMgPSBicmNt bmFuZF9yZWdzX3Y2MDsKKwllbHNlIGlmIChjdHJsLT5uYW5kX3ZlcnNpb24gPj0gMHgwNTAwKQor CQljdHJsLT5yZWdfb2Zmc2V0cyA9IGJyY21uYW5kX3JlZ3NfdjUwOworCWVsc2UgaWYgKGN0cmwt Pm5hbmRfdmVyc2lvbiA+PSAweDA0MDApCisJCWN0cmwtPnJlZ19vZmZzZXRzID0gYnJjbW5hbmRf cmVnc192NDA7CisKKwkvKiBDaGlwLXNlbGVjdCBzdHJpZGUgKi8KKwlpZiAoY3RybC0+bmFuZF92 ZXJzaW9uID49IDB4MDcwMSkKKwkJY3RybC0+cmVnX3NwYWNpbmcgPSAweDE0OworCWVsc2UKKwkJ Y3RybC0+cmVnX3NwYWNpbmcgPSAweDEwOworCisJLyogUGVyIGNoaXAtc2VsZWN0IHJlZ2lzdGVy cyAqLworCWlmIChjdHJsLT5uYW5kX3ZlcnNpb24gPj0gMHgwNzAxKSB7CisJCWN0cmwtPmNzX29m ZnNldHMgPSBicmNtbmFuZF9jc19vZmZzZXRzX3Y3MTsKKwl9IGVsc2UgeworCQljdHJsLT5jc19v ZmZzZXRzID0gYnJjbW5hbmRfY3Nfb2Zmc2V0czsKKworCQkvKiB2NS4wIGFuZCBlYXJsaWVyIGhh cyBhIGRpZmZlcmVudCBDUzAgb2Zmc2V0IGxheW91dCAqLworCQlpZiAoY3RybC0+bmFuZF92ZXJz aW9uIDw9IDB4MDUwMCkKKwkJCWN0cmwtPmNzMF9vZmZzZXRzID0gYnJjbW5hbmRfY3Nfb2Zmc2V0 c19jczA7CisJfQorCisJLyogUGFnZSAvIGJsb2NrIHNpemVzICovCisJaWYgKGN0cmwtPm5hbmRf dmVyc2lvbiA+PSAweDA3MDEpIHsKKwkJLyogPj0gdjcuMSB1c2UgbmljZSBwb3dlci1vZi0yIHZh bHVlcyEgKi8KKwkJY3RybC0+bWF4X3BhZ2Vfc2l6ZSA9IDE2ICogMTAyNDsKKwkJY3RybC0+bWF4 X2Jsb2NrX3NpemUgPSAyICogMTAyNCAqIDEwMjQ7CisJfSBlbHNlIHsKKwkJY3RybC0+cGFnZV9z aXplcyA9IHBhZ2Vfc2l6ZXM7CisJCWlmIChjdHJsLT5uYW5kX3ZlcnNpb24gPj0gMHgwNjAwKQor CQkJY3RybC0+YmxvY2tfc2l6ZXMgPSBibG9ja19zaXplc192NjsKKwkJZWxzZQorCQkJY3RybC0+ YmxvY2tfc2l6ZXMgPSBibG9ja19zaXplc192NDsKKworCQlpZiAoY3RybC0+bmFuZF92ZXJzaW9u IDwgMHgwNDAwKSB7CisJCQljdHJsLT5tYXhfcGFnZV9zaXplID0gNDA5NjsKKwkJCWN0cmwtPm1h eF9ibG9ja19zaXplID0gNTEyICogMTAyNDsKKwkJfQorCX0KKworCS8qIE1heGltdW0gc3BhcmUg YXJlYSBzZWN0b3Igc2l6ZSAocGVyIDUxMkIpICovCisJaWYgKGN0cmwtPm5hbmRfdmVyc2lvbiA+ PSAweDA2MDApCisJCWN0cmwtPm1heF9vb2IgPSA2NDsKKwllbHNlIGlmIChjdHJsLT5uYW5kX3Zl cnNpb24gPj0gMHgwNTAwKQorCQljdHJsLT5tYXhfb29iID0gMzI7CisJZWxzZQorCQljdHJsLT5t YXhfb29iID0gMTY7CisKKwkvKiB2Ni4wIGFuZCBuZXdlciAoZXhjZXB0IHY2LjEpIGhhdmUgcHJl ZmV0Y2ggc3VwcG9ydCAqLworCWlmIChjdHJsLT5uYW5kX3ZlcnNpb24gPj0gMHgwNjAwICYmIGN0 cmwtPm5hbmRfdmVyc2lvbiAhPSAweDA2MDEpCisJCWN0cmwtPmZlYXR1cmVzIHw9IEJSQ01OQU5E X0hBU19QUkVGRVRDSDsKKworCS8qCisJICogdjYueCBoYXMgY2FjaGUgbW9kZSwgYnV0IGl0J3Mg aW1wbGVtZW50ZWQgZGlmZmVyZW50bHkuIElnbm9yZSBpdCBmb3IKKwkgKiBub3cuCisJICovCisJ aWYgKGN0cmwtPm5hbmRfdmVyc2lvbiA+PSAweDA3MDApCisJCWN0cmwtPmZlYXR1cmVzIHw9IEJS Q01OQU5EX0hBU19DQUNIRV9NT0RFOworCisJaWYgKGN0cmwtPm5hbmRfdmVyc2lvbiA+PSAweDA1 MDApCisJCWN0cmwtPmZlYXR1cmVzIHw9IEJSQ01OQU5EX0hBU18xS19TRUNUT1JTOworCisJaWYg KGN0cmwtPm5hbmRfdmVyc2lvbiA+PSAweDA3MDApCisJCWN0cmwtPmZlYXR1cmVzIHw9IEJSQ01O QU5EX0hBU19XUDsKKwllbHNlIGlmIChvZl9wcm9wZXJ0eV9yZWFkX2Jvb2woY3RybC0+ZGV2LT5v Zl9ub2RlLCAiYnJjbSxuYW5kLWhhcy13cCIpKQorCQljdHJsLT5mZWF0dXJlcyB8PSBCUkNNTkFO RF9IQVNfV1A7CisKKwlyZXR1cm4gMDsKK30KKworc3RhdGljIGlubGluZSB1MzIgYnJjbW5hbmRf cmVhZF9yZWcoc3RydWN0IGJyY21uYW5kX2NvbnRyb2xsZXIgKmN0cmwsCisJCWVudW0gYnJjbW5h bmRfcmVnIHJlZykKK3sKKwl1MTYgb2ZmcyA9IGN0cmwtPnJlZ19vZmZzZXRzW3JlZ107CisKKwlp ZiAob2ZmcykKKwkJcmV0dXJuIG5hbmRfcmVhZHJlZyhjdHJsLCBvZmZzKTsKKwllbHNlCisJCXJl dHVybiAwOworfQorCitzdGF0aWMgaW5saW5lIHZvaWQgYnJjbW5hbmRfd3JpdGVfcmVnKHN0cnVj dCBicmNtbmFuZF9jb250cm9sbGVyICpjdHJsLAorCQkJCSAgICAgIGVudW0gYnJjbW5hbmRfcmVn IHJlZywgdTMyIHZhbCkKK3sKKwl1MTYgb2ZmcyA9IGN0cmwtPnJlZ19vZmZzZXRzW3JlZ107CisK KwlpZiAob2ZmcykKKwkJbmFuZF93cml0ZXJlZyhjdHJsLCBvZmZzLCB2YWwpOworfQorCitzdGF0 aWMgaW5saW5lIHZvaWQgYnJjbW5hbmRfcm13X3JlZyhzdHJ1Y3QgYnJjbW5hbmRfY29udHJvbGxl ciAqY3RybCwKKwkJCQkgICAgZW51bSBicmNtbmFuZF9yZWcgcmVnLCB1MzIgbWFzaywgdW5zaWdu ZWQKKwkJCQkgICAgaW50IHNoaWZ0LCB1MzIgdmFsKQoreworCXUzMiB0bXAgPSBicmNtbmFuZF9y ZWFkX3JlZyhjdHJsLCByZWcpOworCisJdG1wICY9IH5tYXNrOworCXRtcCB8PSB2YWwgPDwgc2hp ZnQ7CisJYnJjbW5hbmRfd3JpdGVfcmVnKGN0cmwsIHJlZywgdG1wKTsKK30KKworc3RhdGljIGlu bGluZSB1MzIgYnJjbW5hbmRfcmVhZF9mYyhzdHJ1Y3QgYnJjbW5hbmRfY29udHJvbGxlciAqY3Ry bCwgaW50IHdvcmQpCit7CisJcmV0dXJuIF9fcmF3X3JlYWRsKGN0cmwtPm5hbmRfZmMgKyB3b3Jk ICogNCk7Cit9CisKK3N0YXRpYyBpbmxpbmUgdm9pZCBicmNtbmFuZF93cml0ZV9mYyhzdHJ1Y3Qg YnJjbW5hbmRfY29udHJvbGxlciAqY3RybCwKKwkJCQkgICAgIGludCB3b3JkLCB1MzIgdmFsKQor eworCV9fcmF3X3dyaXRlbCh2YWwsIGN0cmwtPm5hbmRfZmMgKyB3b3JkICogNCk7Cit9CisKK3N0 YXRpYyBpbmxpbmUgdTE2IGJyY21uYW5kX2NzX29mZnNldChzdHJ1Y3QgYnJjbW5hbmRfY29udHJv bGxlciAqY3RybCwgaW50IGNzLAorCQkJCSAgICAgZW51bSBicmNtbmFuZF9jc19yZWcgcmVnKQor eworCXUxNiBvZmZzX2NzMCA9IGN0cmwtPnJlZ19vZmZzZXRzW0JSQ01OQU5EX0NTMF9CQVNFXTsK Kwl1MTYgb2Zmc19jczEgPSBjdHJsLT5yZWdfb2Zmc2V0c1tCUkNNTkFORF9DUzFfQkFTRV07CisJ dTggY3Nfb2ZmczsKKworCWlmIChjcyA9PSAwICYmIGN0cmwtPmNzMF9vZmZzZXRzKQorCQljc19v ZmZzID0gY3RybC0+Y3MwX29mZnNldHNbcmVnXTsKKwllbHNlCisJCWNzX29mZnMgPSBjdHJsLT5j c19vZmZzZXRzW3JlZ107CisKKwlpZiAoY3MgJiYgb2Zmc19jczEpCisJCXJldHVybiBvZmZzX2Nz MSArIChjcyAtIDEpICogY3RybC0+cmVnX3NwYWNpbmcgKyBjc19vZmZzOworCisJcmV0dXJuIG9m ZnNfY3MwICsgY3MgKiBjdHJsLT5yZWdfc3BhY2luZyArIGNzX29mZnM7Cit9CisKK3N0YXRpYyBp bmxpbmUgdTMyIGJyY21uYW5kX2NvdW50X2NvcnJlY3RlZChzdHJ1Y3QgYnJjbW5hbmRfY29udHJv bGxlciAqY3RybCkKK3sKKwlpZiAoY3RybC0+bmFuZF92ZXJzaW9uIDwgMHgwNjAwKQorCQlyZXR1 cm4gMTsKKwlyZXR1cm4gYnJjbW5hbmRfcmVhZF9yZWcoY3RybCwgQlJDTU5BTkRfQ09SUl9DT1VO VCk7Cit9CisKK3N0YXRpYyB2b2lkIGJyY21uYW5kX3dyX2NvcnJfdGhyZXNoKHN0cnVjdCBicmNt bmFuZF9ob3N0ICpob3N0LCB1OCB2YWwpCit7CisJc3RydWN0IGJyY21uYW5kX2NvbnRyb2xsZXIg KmN0cmwgPSBob3N0LT5jdHJsOworCXVuc2lnbmVkIGludCBzaGlmdCA9IDAsIGJpdHM7CisJZW51 bSBicmNtbmFuZF9yZWcgcmVnID0gQlJDTU5BTkRfQ09SUl9USFJFU0hPTEQ7CisJaW50IGNzID0g aG9zdC0+Y3M7CisKKwlpZiAoY3RybC0+bmFuZF92ZXJzaW9uID49IDB4MDYwMCkKKwkJYml0cyA9 IDY7CisJZWxzZSBpZiAoY3RybC0+bmFuZF92ZXJzaW9uID49IDB4MDUwMCkKKwkJYml0cyA9IDU7 CisJZWxzZQorCQliaXRzID0gNDsKKworCWlmIChjdHJsLT5uYW5kX3ZlcnNpb24gPj0gMHgwNjAw KSB7CisJCWlmIChjcyA+PSA1KQorCQkJcmVnID0gQlJDTU5BTkRfQ09SUl9USFJFU0hPTERfRVhU OworCQlzaGlmdCA9IChjcyAlIDUpICogYml0czsKKwl9CisJYnJjbW5hbmRfcm13X3JlZyhjdHJs LCByZWcsIChiaXRzIC0gMSkgPDwgc2hpZnQsIHNoaWZ0LCB2YWwpOworfQorCitzdGF0aWMgaW5s aW5lIGludCBicmNtbmFuZF9jbWRfc2hpZnQoc3RydWN0IGJyY21uYW5kX2NvbnRyb2xsZXIgKmN0 cmwpCit7CisJaWYgKGN0cmwtPm5hbmRfdmVyc2lvbiA8IDB4MDcwMCkKKwkJcmV0dXJuIDI0Owor CXJldHVybiAwOworfQorCisvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioq KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioKKyAqIE5BTkQgQUNDIENPTlRST0wgYml0 ZmllbGQKKyAqCisgKiBTb21lIGJpdHMgaGF2ZSByZW1haW5lZCBjb25zdGFudCB0aHJvdWdob3V0 IGhhcmR3YXJlIHJldmlzaW9uLCB3aGlsZQorICogb3RoZXJzIGhhdmUgc2hpZnRlZCBhcm91bmQu CisgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioq KioqKioqKioqKioqKioqKiovCisKKy8qIENvbnN0YW50IGZvciBhbGwgdmVyc2lvbnMgKHdoZXJl IHN1cHBvcnRlZCkgKi8KK2VudW0geworCS8qIFNlZSBCUkNNTkFORF9IQVNfQ0FDSEVfTU9ERSAq LworCUFDQ19DT05UUk9MX0NBQ0hFX01PREUJCQkJPSBCSVQoMjIpLAorCisJLyogU2VlIEJSQ01O QU5EX0hBU19QUkVGRVRDSCAqLworCUFDQ19DT05UUk9MX1BSRUZFVENICQkJCT0gQklUKDIzKSwK KworCUFDQ19DT05UUk9MX1BBR0VfSElUCQkJCT0gQklUKDI0KSwKKwlBQ0NfQ09OVFJPTF9XUl9Q UkVFTVBUCQkJCT0gQklUKDI1KSwKKwlBQ0NfQ09OVFJPTF9QQVJUSUFMX1BBR0UJCQk9IEJJVCgy NiksCisJQUNDX0NPTlRST0xfUkRfRVJBU0VECQkJCT0gQklUKDI3KSwKKwlBQ0NfQ09OVFJPTF9G QVNUX1BHTV9SRElOCQkJPSBCSVQoMjgpLAorCUFDQ19DT05UUk9MX1dSX0VDQwkJCQk9IEJJVCgz MCksCisJQUNDX0NPTlRST0xfUkRfRUNDCQkJCT0gQklUKDMxKSwKK307CisKK3N0YXRpYyBpbmxp bmUgdTMyIGJyY21uYW5kX3NwYXJlX2FyZWFfbWFzayhzdHJ1Y3QgYnJjbW5hbmRfY29udHJvbGxl ciAqY3RybCkKK3sKKwlpZiAoY3RybC0+bmFuZF92ZXJzaW9uID49IDB4MDYwMCkKKwkJcmV0dXJu IEdFTk1BU0soNiwgMCk7CisJZWxzZQorCQlyZXR1cm4gR0VOTUFTSyg1LCAwKTsKK30KKworI2Rl ZmluZSBOQU5EX0FDQ19DT05UUk9MX0VDQ19TSElGVAkxNgorCitzdGF0aWMgaW5saW5lIHUzMiBi cmNtbmFuZF9lY2NfbGV2ZWxfbWFzayhzdHJ1Y3QgYnJjbW5hbmRfY29udHJvbGxlciAqY3RybCkK K3sKKwl1MzIgbWFzayA9IChjdHJsLT5uYW5kX3ZlcnNpb24gPj0gMHgwNjAwKSA/IDB4MWYgOiAw eDBmOworCisJcmV0dXJuIG1hc2sgPDwgTkFORF9BQ0NfQ09OVFJPTF9FQ0NfU0hJRlQ7Cit9CisK K3N0YXRpYyB2b2lkIGJyY21uYW5kX3NldF9lY2NfZW5hYmxlZChzdHJ1Y3QgYnJjbW5hbmRfaG9z dCAqaG9zdCwgaW50IGVuKQoreworCXN0cnVjdCBicmNtbmFuZF9jb250cm9sbGVyICpjdHJsID0g aG9zdC0+Y3RybDsKKwl1MTYgb2ZmcyA9IGJyY21uYW5kX2NzX29mZnNldChjdHJsLCBob3N0LT5j cywgQlJDTU5BTkRfQ1NfQUNDX0NPTlRST0wpOworCXUzMiBhY2NfY29udHJvbCA9IG5hbmRfcmVh ZHJlZyhjdHJsLCBvZmZzKTsKKwl1MzIgZWNjX2ZsYWdzID0gQUNDX0NPTlRST0xfV1JfRUNDIHwg QUNDX0NPTlRST0xfUkRfRUNDOworCisJaWYgKGVuKSB7CisJCWFjY19jb250cm9sIHw9IGVjY19m bGFnczsgLyogZW5hYmxlIFJEL1dSIEVDQyAqLworCQlhY2NfY29udHJvbCB8PSBob3N0LT5od2Nm Zy5lY2NfbGV2ZWwKKwkJCSAgICAgICA8PCBOQU5EX0FDQ19DT05UUk9MX0VDQ19TSElGVDsKKwl9 IGVsc2UgeworCQlhY2NfY29udHJvbCAmPSB+ZWNjX2ZsYWdzOyAvKiBkaXNhYmxlIFJEL1dSIEVD QyAqLworCQlhY2NfY29udHJvbCAmPSB+YnJjbW5hbmRfZWNjX2xldmVsX21hc2soY3RybCk7CisJ fQorCisJbmFuZF93cml0ZXJlZyhjdHJsLCBvZmZzLCBhY2NfY29udHJvbCk7Cit9CisKK3N0YXRp YyBpbmxpbmUgaW50IGJyY21uYW5kX3NlY3Rvcl8xa19zaGlmdChzdHJ1Y3QgYnJjbW5hbmRfY29u dHJvbGxlciAqY3RybCkKK3sKKwlpZiAoY3RybC0+bmFuZF92ZXJzaW9uID49IDB4MDYwMCkKKwkJ cmV0dXJuIDc7CisJZWxzZSBpZiAoY3RybC0+bmFuZF92ZXJzaW9uID49IDB4MDUwMCkKKwkJcmV0 dXJuIDY7CisJZWxzZQorCQlyZXR1cm4gLTE7Cit9CisKK3N0YXRpYyBpbnQgYnJjbW5hbmRfZ2V0 X3NlY3Rvcl9zaXplXzFrKHN0cnVjdCBicmNtbmFuZF9ob3N0ICpob3N0KQoreworCXN0cnVjdCBi cmNtbmFuZF9jb250cm9sbGVyICpjdHJsID0gaG9zdC0+Y3RybDsKKwlpbnQgc2hpZnQgPSBicmNt bmFuZF9zZWN0b3JfMWtfc2hpZnQoY3RybCk7CisJdTE2IGFjY19jb250cm9sX29mZnMgPSBicmNt bmFuZF9jc19vZmZzZXQoY3RybCwgaG9zdC0+Y3MsCisJCQkJCQkgIEJSQ01OQU5EX0NTX0FDQ19D T05UUk9MKTsKKworCWlmIChzaGlmdCA8IDApCisJCXJldHVybiAwOworCisJcmV0dXJuIChuYW5k X3JlYWRyZWcoY3RybCwgYWNjX2NvbnRyb2xfb2ZmcykgPj4gc2hpZnQpICYgMHgxOworfQorCitz dGF0aWMgdm9pZCBicmNtbmFuZF9zZXRfc2VjdG9yX3NpemVfMWsoc3RydWN0IGJyY21uYW5kX2hv c3QgKmhvc3QsIGludCB2YWwpCit7CisJc3RydWN0IGJyY21uYW5kX2NvbnRyb2xsZXIgKmN0cmwg PSBob3N0LT5jdHJsOworCWludCBzaGlmdCA9IGJyY21uYW5kX3NlY3Rvcl8xa19zaGlmdChjdHJs KTsKKwl1MTYgYWNjX2NvbnRyb2xfb2ZmcyA9IGJyY21uYW5kX2NzX29mZnNldChjdHJsLCBob3N0 LT5jcywKKwkJCQkJCSAgQlJDTU5BTkRfQ1NfQUNDX0NPTlRST0wpOworCXUzMiB0bXA7CisKKwlp ZiAoc2hpZnQgPCAwKQorCQlyZXR1cm47CisKKwl0bXAgPSBuYW5kX3JlYWRyZWcoY3RybCwgYWNj X2NvbnRyb2xfb2Zmcyk7CisJdG1wICY9IH4oMSA8PCBzaGlmdCk7CisJdG1wIHw9ICghIXZhbCkg PDwgc2hpZnQ7CisJbmFuZF93cml0ZXJlZyhjdHJsLCBhY2NfY29udHJvbF9vZmZzLCB0bXApOwor fQorCisvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioq KioqKioqKioqKioqKioqKioqKioKKyAqIENTX05BTkRfU0VMRUNUCisgKioqKioqKioqKioqKioq KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiov CisKK2VudW0geworCUNTX1NFTEVDVF9OQU5EX1dQCQkJPSBCSVQoMjkpLAorCUNTX1NFTEVDVF9B VVRPX0RFVklDRV9JRF9DRkcJCT0gQklUKDMwKSwKK307CisKK3N0YXRpYyBpbmxpbmUgdm9pZCBi cmNtbmFuZF9zZXRfd3Aoc3RydWN0IGJyY21uYW5kX2NvbnRyb2xsZXIgKmN0cmwsIGJvb2wgZW4p Cit7CisJdTMyIHZhbCA9IGVuID8gQ1NfU0VMRUNUX05BTkRfV1AgOiAwOworCisJYnJjbW5hbmRf cm13X3JlZyhjdHJsLCBCUkNNTkFORF9DU19TRUxFQ1QsIENTX1NFTEVDVF9OQU5EX1dQLCAwLCB2 YWwpOworfQorCisvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioq KioqKioqKioqKioqKioqKioqKioqKioqKioKKyAqIEZsYXNoIERNQQorICoqKioqKioqKioqKioq KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioq LworCitlbnVtIGZsYXNoX2RtYV9yZWcgeworCUZMQVNIX0RNQV9SRVZJU0lPTgkJPSAweDAwLAor CUZMQVNIX0RNQV9GSVJTVF9ERVNDCQk9IDB4MDQsCisJRkxBU0hfRE1BX0ZJUlNUX0RFU0NfRVhU CT0gMHgwOCwKKwlGTEFTSF9ETUFfQ1RSTAkJCT0gMHgwYywKKwlGTEFTSF9ETUFfTU9ERQkJCT0g MHgxMCwKKwlGTEFTSF9ETUFfU1RBVFVTCQk9IDB4MTQsCisJRkxBU0hfRE1BX0lOVEVSUlVQVF9E RVNDCT0gMHgxOCwKKwlGTEFTSF9ETUFfSU5URVJSVVBUX0RFU0NfRVhUCT0gMHgxYywKKwlGTEFT SF9ETUFfRVJST1JfU1RBVFVTCQk9IDB4MjAsCisJRkxBU0hfRE1BX0NVUlJFTlRfREVTQwkJPSAw eDI0LAorCUZMQVNIX0RNQV9DVVJSRU5UX0RFU0NfRVhUCT0gMHgyOCwKK307CisKK3N0YXRpYyBp bmxpbmUgYm9vbCBoYXNfZmxhc2hfZG1hKHN0cnVjdCBicmNtbmFuZF9jb250cm9sbGVyICpjdHJs KQoreworCXJldHVybiBjdHJsLT5mbGFzaF9kbWFfYmFzZTsKK30KKworc3RhdGljIGlubGluZSBi b29sIGZsYXNoX2RtYV9idWZfb2soY29uc3Qgdm9pZCAqYnVmKQoreworCXJldHVybiBidWYgJiYg IWlzX3ZtYWxsb2NfYWRkcihidWYpICYmCisJCWxpa2VseShJU19BTElHTkVEKCh1aW50cHRyX3Qp YnVmLCA0KSk7Cit9CisKK3N0YXRpYyBpbmxpbmUgdm9pZCBmbGFzaF9kbWFfd3JpdGVsKHN0cnVj dCBicmNtbmFuZF9jb250cm9sbGVyICpjdHJsLCB1OCBvZmZzLAorCQkJCSAgICB1MzIgdmFsKQor eworCV9fcmF3X3dyaXRlbCh2YWwsIGN0cmwtPmZsYXNoX2RtYV9iYXNlICsgb2Zmcyk7Cit9CisK K3N0YXRpYyBpbmxpbmUgdTMyIGZsYXNoX2RtYV9yZWFkbChzdHJ1Y3QgYnJjbW5hbmRfY29udHJv bGxlciAqY3RybCwgdTggb2ZmcykKK3sKKwlyZXR1cm4gX19yYXdfcmVhZGwoY3RybC0+Zmxhc2hf ZG1hX2Jhc2UgKyBvZmZzKTsKK30KKworLyogTG93LWxldmVsIG9wZXJhdGlvbiB0eXBlczogY29t bWFuZCwgYWRkcmVzcywgd3JpdGUsIG9yIHJlYWQgKi8KK2VudW0gYnJjbW5hbmRfbGxvcF90eXBl IHsKKwlMTF9PUF9DTUQsCisJTExfT1BfQUREUiwKKwlMTF9PUF9XUiwKKwlMTF9PUF9SRCwKK307 CisKKy8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioq KioqKioqKioqKioqKioqKioqKgorICogSW50ZXJuYWwgc3VwcG9ydCBmdW5jdGlvbnMKKyAqKioq KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioq KioqKioqKioqKi8KKworc3RhdGljIGlubGluZSBib29sIGlzX2hhbW1pbmdfZWNjKHN0cnVjdCBi cmNtbmFuZF9jZmcgKmNmZykKK3sKKwlyZXR1cm4gY2ZnLT5zZWN0b3Jfc2l6ZV8xayA9PSAwICYm IGNmZy0+c3BhcmVfYXJlYV9zaXplID09IDE2ICYmCisJCWNmZy0+ZWNjX2xldmVsID09IDE1Owor fQorCisvKgorICogUmV0dXJucyBhIG5hbmRfZWNjbGF5b3V0IHN0cnVjdXRyZSBmb3IgdGhlIGdp dmVuIGxheW91dC9jb25maWd1cmF0aW9uLgorICogUmV0dXJucyBOVUxMIG9uIGZhaWx1cmUuCisg Ki8KK3N0YXRpYyBzdHJ1Y3QgbmFuZF9lY2NsYXlvdXQgKmJyY21uYW5kX2NyZWF0ZV9sYXlvdXQo aW50IGVjY19sZXZlbCwKKwkJCQkJCSAgICAgc3RydWN0IGJyY21uYW5kX2hvc3QgKmhvc3QpCit7 CisJc3RydWN0IGJyY21uYW5kX2NmZyAqY2ZnID0gJmhvc3QtPmh3Y2ZnOworCWludCBpLCBqOwor CXN0cnVjdCBuYW5kX2VjY2xheW91dCAqbGF5b3V0OworCWludCByZXE7CisJaW50IHNlY3RvcnM7 CisJaW50IHNhczsKKwlpbnQgaWR4MSwgaWR4MjsKKworCWxheW91dCA9IGRldm1fa3phbGxvYygm aG9zdC0+cGRldi0+ZGV2LCBzaXplb2YoKmxheW91dCksIEdGUF9LRVJORUwpOworCWlmICghbGF5 b3V0KQorCQlyZXR1cm4gTlVMTDsKKworCXNlY3RvcnMgPSBjZmctPnBhZ2Vfc2l6ZSAvICg1MTIg PDwgY2ZnLT5zZWN0b3Jfc2l6ZV8xayk7CisJc2FzID0gY2ZnLT5zcGFyZV9hcmVhX3NpemUgPDwg Y2ZnLT5zZWN0b3Jfc2l6ZV8xazsKKworCS8qIEhhbW1pbmcgKi8KKwlpZiAoaXNfaGFtbWluZ19l Y2MoY2ZnKSkgeworCQlmb3IgKGkgPSAwLCBpZHgxID0gMCwgaWR4MiA9IDA7IGkgPCBzZWN0b3Jz OyBpKyspIHsKKwkJCS8qIEZpcnN0IHNlY3RvciBvZiBlYWNoIHBhZ2UgbWF5IGhhdmUgQkJJICov CisJCQlpZiAoaSA9PSAwKSB7CisJCQkJbGF5b3V0LT5vb2JmcmVlW2lkeDJdLm9mZnNldCA9IGkg KiBzYXMgKyAxOworCQkJCS8qIFNtYWxsLXBhZ2UgTkFORCB1c2UgYnl0ZSA2IGZvciBCQkkgKi8K KwkJCQlpZiAoY2ZnLT5wYWdlX3NpemUgPT0gNTEyKQorCQkJCQlsYXlvdXQtPm9vYmZyZWVbaWR4 Ml0ub2Zmc2V0LS07CisJCQkJbGF5b3V0LT5vb2JmcmVlW2lkeDJdLmxlbmd0aCA9IDU7CisJCQl9 IGVsc2UgeworCQkJCWxheW91dC0+b29iZnJlZVtpZHgyXS5vZmZzZXQgPSBpICogc2FzOworCQkJ CWxheW91dC0+b29iZnJlZVtpZHgyXS5sZW5ndGggPSA2OworCQkJfQorCQkJaWR4MisrOworCQkJ bGF5b3V0LT5lY2Nwb3NbaWR4MSsrXSA9IGkgKiBzYXMgKyA2OworCQkJbGF5b3V0LT5lY2Nwb3Nb aWR4MSsrXSA9IGkgKiBzYXMgKyA3OworCQkJbGF5b3V0LT5lY2Nwb3NbaWR4MSsrXSA9IGkgKiBz YXMgKyA4OworCQkJbGF5b3V0LT5vb2JmcmVlW2lkeDJdLm9mZnNldCA9IGkgKiBzYXMgKyA5Owor CQkJbGF5b3V0LT5vb2JmcmVlW2lkeDJdLmxlbmd0aCA9IDc7CisJCQlpZHgyKys7CisJCQkvKiBM ZWF2ZSB6ZXJvLXRlcm1pbmF0ZWQgZW50cnkgZm9yIE9PQkZSRUUgKi8KKwkJCWlmIChpZHgxID49 IE1URF9NQVhfRUNDUE9TX0VOVFJJRVNfTEFSR0UgfHwKKwkJCQkgICAgaWR4MiA+PSBNVERfTUFY X09PQkZSRUVfRU5UUklFU19MQVJHRSAtIDEpCisJCQkJYnJlYWs7CisJCX0KKwkJZ290byBvdXQ7 CisJfQorCisJLyoKKwkgKiBDT05UUk9MTEVSX1ZFUlNJT046CisJICogICA8IHY1LjA6IEVDQ19S RVEgPSBjZWlsKEJDSF9UICogMTMvOCkKKwkgKiAgPj0gdjUuMDogRUNDX1JFUSA9IGNlaWwoQkNI X1QgKiAxNC84KQorCSAqIEJ1dCB3ZSB3aWxsIGp1c3QgYmUgY29uc2VydmF0aXZlLgorCSAqLwor CXJlcSA9IERJVl9ST1VORF9VUChlY2NfbGV2ZWwgKiAxNCwgOCk7CisJaWYgKHJlcSA+PSBzYXMp IHsKKwkJZGV2X2VycigmaG9zdC0+cGRldi0+ZGV2LAorCQkJImVycm9yOiBFQ0MgdG9vIGxhcmdl IGZvciBPT0IgKEVDQyBieXRlcyAlZCwgc3BhcmUgc2VjdG9yICVkKVxuIiwKKwkJCXJlcSwgc2Fz KTsKKwkJcmV0dXJuIE5VTEw7CisJfQorCisJbGF5b3V0LT5lY2NieXRlcyA9IHJlcSAqIHNlY3Rv cnM7CisJZm9yIChpID0gMCwgaWR4MSA9IDAsIGlkeDIgPSAwOyBpIDwgc2VjdG9yczsgaSsrKSB7 CisJCWZvciAoaiA9IHNhcyAtIHJlcTsgaiA8IHNhcyAmJiBpZHgxIDwKKwkJCQlNVERfTUFYX0VD Q1BPU19FTlRSSUVTX0xBUkdFOyBqKyssIGlkeDErKykKKwkJCWxheW91dC0+ZWNjcG9zW2lkeDFd ID0gaSAqIHNhcyArIGo7CisKKwkJLyogRmlyc3Qgc2VjdG9yIG9mIGVhY2ggcGFnZSBtYXkgaGF2 ZSBCQkkgKi8KKwkJaWYgKGkgPT0gMCkgeworCQkJaWYgKGNmZy0+cGFnZV9zaXplID09IDUxMiAm JiAoc2FzIC0gcmVxID49IDYpKSB7CisJCQkJLyogU21hbGwtcGFnZSBOQU5EIHVzZSBieXRlIDYg Zm9yIEJCSSAqLworCQkJCWxheW91dC0+b29iZnJlZVtpZHgyXS5vZmZzZXQgPSAwOworCQkJCWxh eW91dC0+b29iZnJlZVtpZHgyXS5sZW5ndGggPSA1OworCQkJCWlkeDIrKzsKKwkJCQlpZiAoc2Fz IC0gcmVxID4gNikgeworCQkJCQlsYXlvdXQtPm9vYmZyZWVbaWR4Ml0ub2Zmc2V0ID0gNjsKKwkJ CQkJbGF5b3V0LT5vb2JmcmVlW2lkeDJdLmxlbmd0aCA9CisJCQkJCQlzYXMgLSByZXEgLSA2Owor CQkJCQlpZHgyKys7CisJCQkJfQorCQkJfSBlbHNlIGlmIChzYXMgPiByZXEgKyAxKSB7CisJCQkJ bGF5b3V0LT5vb2JmcmVlW2lkeDJdLm9mZnNldCA9IGkgKiBzYXMgKyAxOworCQkJCWxheW91dC0+ b29iZnJlZVtpZHgyXS5sZW5ndGggPSBzYXMgLSByZXEgLSAxOworCQkJCWlkeDIrKzsKKwkJCX0K KwkJfSBlbHNlIGlmIChzYXMgPiByZXEpIHsKKwkJCWxheW91dC0+b29iZnJlZVtpZHgyXS5vZmZz ZXQgPSBpICogc2FzOworCQkJbGF5b3V0LT5vb2JmcmVlW2lkeDJdLmxlbmd0aCA9IHNhcyAtIHJl cTsKKwkJCWlkeDIrKzsKKwkJfQorCQkvKiBMZWF2ZSB6ZXJvLXRlcm1pbmF0ZWQgZW50cnkgZm9y IE9PQkZSRUUgKi8KKwkJaWYgKGlkeDEgPj0gTVREX01BWF9FQ0NQT1NfRU5UUklFU19MQVJHRSB8 fAorCQkJCWlkeDIgPj0gTVREX01BWF9PT0JGUkVFX0VOVFJJRVNfTEFSR0UgLSAxKQorCQkJYnJl YWs7CisJfQorb3V0OgorCS8qIFN1bSBhdmFpbGFibGUgT09CICovCisJZm9yIChpID0gMDsgaSA8 IE1URF9NQVhfT09CRlJFRV9FTlRSSUVTX0xBUkdFOyBpKyspCisJCWxheW91dC0+b29iYXZhaWwg Kz0gbGF5b3V0LT5vb2JmcmVlW2ldLmxlbmd0aDsKKwlyZXR1cm4gbGF5b3V0OworfQorCitzdGF0 aWMgc3RydWN0IG5hbmRfZWNjbGF5b3V0ICpicmNtc3RiX2Nob29zZV9lY2NfbGF5b3V0KAorCQlz dHJ1Y3QgYnJjbW5hbmRfaG9zdCAqaG9zdCkKK3sKKwlzdHJ1Y3QgbmFuZF9lY2NsYXlvdXQgKmxh eW91dDsKKwlzdHJ1Y3QgYnJjbW5hbmRfY2ZnICpwID0gJmhvc3QtPmh3Y2ZnOworCXVuc2lnbmVk IGludCBlY2NfbGV2ZWwgPSBwLT5lY2NfbGV2ZWw7CisKKwlpZiAocC0+c2VjdG9yX3NpemVfMWsp CisJCWVjY19sZXZlbCA8PD0gMTsKKworCWxheW91dCA9IGJyY21uYW5kX2NyZWF0ZV9sYXlvdXQo ZWNjX2xldmVsLCBob3N0KTsKKwlpZiAoIWxheW91dCkgeworCQlkZXZfZXJyKCZob3N0LT5wZGV2 LT5kZXYsCisJCQkJIm5vIHByb3BlciBlY2NfbGF5b3V0IGZvciB0aGlzIE5BTkQgY2ZnXG4iKTsK KwkJcmV0dXJuIE5VTEw7CisJfQorCisJcmV0dXJuIGxheW91dDsKK30KKworc3RhdGljIHZvaWQg YnJjbW5hbmRfd3Aoc3RydWN0IG10ZF9pbmZvICptdGQsIGludCB3cCkKK3sKKwlzdHJ1Y3QgbmFu ZF9jaGlwICpjaGlwID0gbXRkLT5wcml2OworCXN0cnVjdCBicmNtbmFuZF9ob3N0ICpob3N0ID0g Y2hpcC0+cHJpdjsKKwlzdHJ1Y3QgYnJjbW5hbmRfY29udHJvbGxlciAqY3RybCA9IGhvc3QtPmN0 cmw7CisKKwlpZiAoKGN0cmwtPmZlYXR1cmVzICYgQlJDTU5BTkRfSEFTX1dQKSAmJiB3cF9vbiA9 PSAxKSB7CisJCXN0YXRpYyBpbnQgb2xkX3dwID0gLTE7CisKKwkJaWYgKG9sZF93cCAhPSB3cCkg eworCQkJZGV2X2RiZyhjdHJsLT5kZXYsICJXUCAlc1xuIiwgd3AgPyAib24iIDogIm9mZiIpOwor CQkJb2xkX3dwID0gd3A7CisJCX0KKwkJYnJjbW5hbmRfc2V0X3dwKGN0cmwsIHdwKTsKKwl9Cit9 CisKKy8qIEhlbHBlciBmdW5jdGlvbnMgZm9yIHJlYWRpbmcgYW5kIHdyaXRpbmcgT09CIHJlZ2lz dGVycyAqLworc3RhdGljIGlubGluZSB1OCBvb2JfcmVnX3JlYWQoc3RydWN0IGJyY21uYW5kX2Nv bnRyb2xsZXIgKmN0cmwsIHUzMiBvZmZzKQoreworCXUxNiBvZmZzZXQwLCBvZmZzZXQxMCwgcmVn X29mZnM7CisKKwlvZmZzZXQwID0gY3RybC0+cmVnX29mZnNldHNbQlJDTU5BTkRfT09CX1JFQURf QkFTRV07CisJb2Zmc2V0MTAgPSBjdHJsLT5yZWdfb2Zmc2V0c1tCUkNNTkFORF9PT0JfUkVBRF8x MF9CQVNFXTsKKworCWlmIChvZmZzID49IGN0cmwtPm1heF9vb2IpCisJCXJldHVybiAweDc3Owor CisJaWYgKG9mZnMgPj0gMTYgJiYgb2Zmc2V0MTApCisJCXJlZ19vZmZzID0gb2Zmc2V0MTAgKyAo KG9mZnMgLSAweDEwKSAmIH4weDAzKTsKKwllbHNlCisJCXJlZ19vZmZzID0gb2Zmc2V0MCArIChv ZmZzICYgfjB4MDMpOworCisJcmV0dXJuIG5hbmRfcmVhZHJlZyhjdHJsLCByZWdfb2ZmcykgPj4g KDI0IC0gKChvZmZzICYgMHgwMykgPDwgMykpOworfQorCitzdGF0aWMgaW5saW5lIHZvaWQgb29i X3JlZ193cml0ZShzdHJ1Y3QgYnJjbW5hbmRfY29udHJvbGxlciAqY3RybCwgdTMyIG9mZnMsCisJ CQkJIHUzMiBkYXRhKQoreworCXUxNiBvZmZzZXQwLCBvZmZzZXQxMCwgcmVnX29mZnM7CisKKwlv ZmZzZXQwID0gY3RybC0+cmVnX29mZnNldHNbQlJDTU5BTkRfT09CX1dSSVRFX0JBU0VdOworCW9m ZnNldDEwID0gY3RybC0+cmVnX29mZnNldHNbQlJDTU5BTkRfT09CX1dSSVRFXzEwX0JBU0VdOwor CisJaWYgKG9mZnMgPj0gY3RybC0+bWF4X29vYikKKwkJcmV0dXJuOworCisJaWYgKG9mZnMgPj0g MTYgJiYgb2Zmc2V0MTApCisJCXJlZ19vZmZzID0gb2Zmc2V0MTAgKyAoKG9mZnMgLSAweDEwKSAm IH4weDAzKTsKKwllbHNlCisJCXJlZ19vZmZzID0gb2Zmc2V0MCArIChvZmZzICYgfjB4MDMpOwor CisJbmFuZF93cml0ZXJlZyhjdHJsLCByZWdfb2ZmcywgZGF0YSk7Cit9CisKKy8qCisgKiByZWFk X29vYl9mcm9tX3JlZ3MgLSByZWFkIGRhdGEgZnJvbSBPT0IgcmVnaXN0ZXJzCisgKiBAY3RybDog TkFORCBjb250cm9sbGVyCisgKiBAaTogc3ViLXBhZ2Ugc2VjdG9yIGluZGV4CisgKiBAb29iOiBi dWZmZXIgdG8gcmVhZCB0bworICogQHNhczogc3BhcmUgYXJlYSBzZWN0b3Igc2l6ZSAoaS5lLiwg T09CIHNpemUgcGVyIEZMQVNIX0NBQ0hFKQorICogQHNlY3Rvcl8xazogMSBmb3IgMUtpQiBzZWN0 b3JzLCAwIGZvciA1MTJCLCBvdGhlciB2YWx1ZXMgYXJlIGlsbGVnYWwKKyAqLworc3RhdGljIGlu dCByZWFkX29vYl9mcm9tX3JlZ3Moc3RydWN0IGJyY21uYW5kX2NvbnRyb2xsZXIgKmN0cmwsIGlu dCBpLCB1OCAqb29iLAorCQkJICAgICAgaW50IHNhcywgaW50IHNlY3Rvcl8xaykKK3sKKwlpbnQg dGJ5dGVzID0gc2FzIDw8IHNlY3Rvcl8xazsKKwlpbnQgajsKKworCS8qIEFkanVzdCBPT0IgdmFs dWVzIGZvciAxSyBzZWN0b3Igc2l6ZSAqLworCWlmIChzZWN0b3JfMWsgJiYgKGkgJiAweDAxKSkK KwkJdGJ5dGVzID0gbWF4KDAsIHRieXRlcyAtIChpbnQpY3RybC0+bWF4X29vYik7CisJdGJ5dGVz ID0gbWluX3QoaW50LCB0Ynl0ZXMsIGN0cmwtPm1heF9vb2IpOworCisJZm9yIChqID0gMDsgaiA8 IHRieXRlczsgaisrKQorCQlvb2Jbal0gPSBvb2JfcmVnX3JlYWQoY3RybCwgaik7CisJcmV0dXJu IHRieXRlczsKK30KKworLyoKKyAqIHdyaXRlX29vYl90b19yZWdzIC0gd3JpdGUgZGF0YSB0byBP T0IgcmVnaXN0ZXJzCisgKiBAaTogc3ViLXBhZ2Ugc2VjdG9yIGluZGV4CisgKiBAb29iOiBidWZm ZXIgdG8gd3JpdGUgZnJvbQorICogQHNhczogc3BhcmUgYXJlYSBzZWN0b3Igc2l6ZSAoaS5lLiwg T09CIHNpemUgcGVyIEZMQVNIX0NBQ0hFKQorICogQHNlY3Rvcl8xazogMSBmb3IgMUtpQiBzZWN0 b3JzLCAwIGZvciA1MTJCLCBvdGhlciB2YWx1ZXMgYXJlIGlsbGVnYWwKKyAqLworc3RhdGljIGlu dCB3cml0ZV9vb2JfdG9fcmVncyhzdHJ1Y3QgYnJjbW5hbmRfY29udHJvbGxlciAqY3RybCwgaW50 IGksCisJCQkgICAgIGNvbnN0IHU4ICpvb2IsIGludCBzYXMsIGludCBzZWN0b3JfMWspCit7CisJ aW50IHRieXRlcyA9IHNhcyA8PCBzZWN0b3JfMWs7CisJaW50IGo7CisKKwkvKiBBZGp1c3QgT09C IHZhbHVlcyBmb3IgMUsgc2VjdG9yIHNpemUgKi8KKwlpZiAoc2VjdG9yXzFrICYmIChpICYgMHgw MSkpCisJCXRieXRlcyA9IG1heCgwLCB0Ynl0ZXMgLSAoaW50KWN0cmwtPm1heF9vb2IpOworCXRi eXRlcyA9IG1pbl90KGludCwgdGJ5dGVzLCBjdHJsLT5tYXhfb29iKTsKKworCWZvciAoaiA9IDA7 IGogPCB0Ynl0ZXM7IGogKz0gNCkKKwkJb29iX3JlZ193cml0ZShjdHJsLCBqLAorCQkJCShvb2Jb aiArIDBdIDw8IDI0KSB8CisJCQkJKG9vYltqICsgMV0gPDwgMTYpIHwKKwkJCQkob29iW2ogKyAy XSA8PCAgOCkgfAorCQkJCShvb2JbaiArIDNdIDw8ICAwKSk7CisJcmV0dXJuIHRieXRlczsKK30K Kworc3RhdGljIGlycXJldHVybl90IGJyY21uYW5kX2N0bHJkeV9pcnEoaW50IGlycSwgdm9pZCAq ZGF0YSkKK3sKKwlzdHJ1Y3QgYnJjbW5hbmRfY29udHJvbGxlciAqY3RybCA9IGRhdGE7CisKKwkv KiBEaXNjYXJkIGFsbCBOQU5EX0NUTFJEWSBpbnRlcnJ1cHRzIGR1cmluZyBETUEgKi8KKwlpZiAo Y3RybC0+ZG1hX3BlbmRpbmcpCisJCXJldHVybiBJUlFfSEFORExFRDsKKworCWNvbXBsZXRlKCZj dHJsLT5kb25lKTsKKwlyZXR1cm4gSVJRX0hBTkRMRUQ7Cit9CisKK3N0YXRpYyBpcnFyZXR1cm5f dCBicmNtbmFuZF9kbWFfaXJxKGludCBpcnEsIHZvaWQgKmRhdGEpCit7CisJc3RydWN0IGJyY21u YW5kX2NvbnRyb2xsZXIgKmN0cmwgPSBkYXRhOworCisJY29tcGxldGUoJmN0cmwtPmRtYV9kb25l KTsKKworCXJldHVybiBJUlFfSEFORExFRDsKK30KKworc3RhdGljIHZvaWQgYnJjbW5hbmRfc2Vu ZF9jbWQoc3RydWN0IGJyY21uYW5kX2hvc3QgKmhvc3QsIGludCBjbWQpCit7CisJc3RydWN0IGJy Y21uYW5kX2NvbnRyb2xsZXIgKmN0cmwgPSBob3N0LT5jdHJsOworCXUzMiBpbnRmYzsKKworCWRl dl9kYmcoY3RybC0+ZGV2LCAic2VuZCBuYXRpdmUgY21kICVkIGFkZHJfbG8gMHgleFxuIiwgY21k LAorCQlicmNtbmFuZF9yZWFkX3JlZyhjdHJsLCBCUkNNTkFORF9DTURfQUREUkVTUykpOworCUJV R19PTihjdHJsLT5jbWRfcGVuZGluZyAhPSAwKTsKKwljdHJsLT5jbWRfcGVuZGluZyA9IGNtZDsK KworCWludGZjID0gYnJjbW5hbmRfcmVhZF9yZWcoY3RybCwgQlJDTU5BTkRfSU5URkNfU1RBVFVT KTsKKwlCVUdfT04oIShpbnRmYyAmIElOVEZDX0NUTFJfUkVBRFkpKTsKKworCW1iKCk7IC8qIGZs dXNoIHByZXZpb3VzIHdyaXRlcyAqLworCWJyY21uYW5kX3dyaXRlX3JlZyhjdHJsLCBCUkNNTkFO RF9DTURfU1RBUlQsCisJCQkgICBjbWQgPDwgYnJjbW5hbmRfY21kX3NoaWZ0KGN0cmwpKTsKK30K KworLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioq KioqKioqKioqKioqKioqKioqCisgKiBOQU5EIE1URCBBUEk6IHJlYWQvcHJvZ3JhbS9lcmFzZQor ICoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioq KioqKioqKioqKioqKioqLworCitzdGF0aWMgdm9pZCBicmNtbmFuZF9jbWRfY3RybChzdHJ1Y3Qg bXRkX2luZm8gKm10ZCwgaW50IGRhdCwKKwl1bnNpZ25lZCBpbnQgY3RybCkKK3sKKwkvKiBpbnRl bnRpb25hbGx5IGxlZnQgYmxhbmsgKi8KK30KKworc3RhdGljIGludCBicmNtbmFuZF93YWl0ZnVu YyhzdHJ1Y3QgbXRkX2luZm8gKm10ZCwgc3RydWN0IG5hbmRfY2hpcCAqdGhpcykKK3sKKwlzdHJ1 Y3QgbmFuZF9jaGlwICpjaGlwID0gbXRkLT5wcml2OworCXN0cnVjdCBicmNtbmFuZF9ob3N0ICpo b3N0ID0gY2hpcC0+cHJpdjsKKwlzdHJ1Y3QgYnJjbW5hbmRfY29udHJvbGxlciAqY3RybCA9IGhv c3QtPmN0cmw7CisJdW5zaWduZWQgbG9uZyB0aW1lbyA9IG1zZWNzX3RvX2ppZmZpZXMoMTAwKTsK KworCWRldl9kYmcoY3RybC0+ZGV2LCAid2FpdCBvbiBuYXRpdmUgY21kICVkXG4iLCBjdHJsLT5j bWRfcGVuZGluZyk7CisJaWYgKGN0cmwtPmNtZF9wZW5kaW5nICYmCisJCQl3YWl0X2Zvcl9jb21w bGV0aW9uX3RpbWVvdXQoJmN0cmwtPmRvbmUsIHRpbWVvKSA8PSAwKSB7CisJCXUzMiBjbWQgPSBi cmNtbmFuZF9yZWFkX3JlZyhjdHJsLCBCUkNNTkFORF9DTURfU1RBUlQpCisJCQkJCT4+IGJyY21u YW5kX2NtZF9zaGlmdChjdHJsKTsKKworCQlkZXZfZXJyX3JhdGVsaW1pdGVkKGN0cmwtPmRldiwK KwkJCSJ0aW1lb3V0IHdhaXRpbmcgZm9yIGNvbW1hbmQgJSMwMnhcbiIsIGNtZCk7CisJCWRldl9l cnJfcmF0ZWxpbWl0ZWQoY3RybC0+ZGV2LCAiaW50ZmMgc3RhdHVzICUwOHhcbiIsCisJCQlicmNt bmFuZF9yZWFkX3JlZyhjdHJsLCBCUkNNTkFORF9JTlRGQ19TVEFUVVMpKTsKKwl9CisJY3RybC0+ Y21kX3BlbmRpbmcgPSAwOworCXJldHVybiBicmNtbmFuZF9yZWFkX3JlZyhjdHJsLCBCUkNNTkFO RF9JTlRGQ19TVEFUVVMpICYKKwkJCQkgSU5URkNfRkxBU0hfU1RBVFVTOworfQorCitlbnVtIHsK KwlMTE9QX1JFCQkJCT0gQklUKDE2KSwKKwlMTE9QX1dFCQkJCT0gQklUKDE3KSwKKwlMTE9QX0FM RQkJCT0gQklUKDE4KSwKKwlMTE9QX0NMRQkJCT0gQklUKDE5KSwKKwlMTE9QX1JFVFVSTl9JRExF CQk9IEJJVCgzMSksCisKKwlMTE9QX0RBVEFfTUFTSwkJCT0gR0VOTUFTSygxNSwgMCksCit9Owor CitzdGF0aWMgaW50IGJyY21uYW5kX2xvd19sZXZlbF9vcChzdHJ1Y3QgYnJjbW5hbmRfaG9zdCAq aG9zdCwKKwkJCQkgZW51bSBicmNtbmFuZF9sbG9wX3R5cGUgdHlwZSwgdTMyIGRhdGEsCisJCQkJ IGJvb2wgbGFzdF9vcCkKK3sKKwlzdHJ1Y3QgbXRkX2luZm8gKm10ZCA9ICZob3N0LT5tdGQ7CisJ c3RydWN0IG5hbmRfY2hpcCAqY2hpcCA9ICZob3N0LT5jaGlwOworCXN0cnVjdCBicmNtbmFuZF9j b250cm9sbGVyICpjdHJsID0gaG9zdC0+Y3RybDsKKwl1MzIgdG1wOworCisJdG1wID0gZGF0YSAm IExMT1BfREFUQV9NQVNLOworCXN3aXRjaCAodHlwZSkgeworCWNhc2UgTExfT1BfQ01EOgorCQl0 bXAgfD0gTExPUF9XRSB8IExMT1BfQ0xFOworCQlicmVhazsKKwljYXNlIExMX09QX0FERFI6CisJ CS8qIFdFIHwgQUxFICovCisJCXRtcCB8PSBMTE9QX1dFIHwgTExPUF9BTEU7CisJCWJyZWFrOwor CWNhc2UgTExfT1BfV1I6CisJCS8qIFdFICovCisJCXRtcCB8PSBMTE9QX1dFOworCQlicmVhazsK KwljYXNlIExMX09QX1JEOgorCQkvKiBSRSAqLworCQl0bXAgfD0gTExPUF9SRTsKKwkJYnJlYWs7 CisJfQorCWlmIChsYXN0X29wKQorCQkvKiBSRVRVUk5fSURMRSAqLworCQl0bXAgfD0gTExPUF9S RVRVUk5fSURMRTsKKworCWRldl9kYmcoY3RybC0+ZGV2LCAibGxfb3AgY21kICUjeFxuIiwgdG1w KTsKKworCWJyY21uYW5kX3dyaXRlX3JlZyhjdHJsLCBCUkNNTkFORF9MTF9PUCwgdG1wKTsKKwko dm9pZClicmNtbmFuZF9yZWFkX3JlZyhjdHJsLCBCUkNNTkFORF9MTF9PUCk7CisKKwlicmNtbmFu ZF9zZW5kX2NtZChob3N0LCBDTURfTE9XX0xFVkVMX09QKTsKKwlyZXR1cm4gYnJjbW5hbmRfd2Fp dGZ1bmMobXRkLCBjaGlwKTsKK30KKworc3RhdGljIHZvaWQgYnJjbW5hbmRfY21kZnVuYyhzdHJ1 Y3QgbXRkX2luZm8gKm10ZCwgdW5zaWduZWQgY29tbWFuZCwKKwkJCSAgICAgaW50IGNvbHVtbiwg aW50IHBhZ2VfYWRkcikKK3sKKwlzdHJ1Y3QgbmFuZF9jaGlwICpjaGlwID0gbXRkLT5wcml2Owor CXN0cnVjdCBicmNtbmFuZF9ob3N0ICpob3N0ID0gY2hpcC0+cHJpdjsKKwlzdHJ1Y3QgYnJjbW5h bmRfY29udHJvbGxlciAqY3RybCA9IGhvc3QtPmN0cmw7CisJdTY0IGFkZHIgPSAodTY0KXBhZ2Vf YWRkciA8PCBjaGlwLT5wYWdlX3NoaWZ0OworCWludCBuYXRpdmVfY21kID0gMDsKKworCWlmIChj b21tYW5kID09IE5BTkRfQ01EX1JFQURJRCB8fCBjb21tYW5kID09IE5BTkRfQ01EX1BBUkFNIHx8 CisJCQljb21tYW5kID09IE5BTkRfQ01EX1JORE9VVCkKKwkJYWRkciA9ICh1NjQpY29sdW1uOwor CS8qIEF2b2lkIHByb3BhZ2F0aW5nIGEgbmVnYXRpdmUsIGRvbid0LWNhcmUgYWRkcmVzcyAqLwor CWVsc2UgaWYgKHBhZ2VfYWRkciA8IDApCisJCWFkZHIgPSAwOworCisJZGV2X2RiZyhjdHJsLT5k ZXYsICJjbWQgMHgleCBhZGRyIDB4JWxseFxuIiwgY29tbWFuZCwKKwkJKHVuc2lnbmVkIGxvbmcg bG9uZylhZGRyKTsKKworCWhvc3QtPmxhc3RfY21kID0gY29tbWFuZDsKKwlob3N0LT5sYXN0X2J5 dGUgPSAwOworCWhvc3QtPmxhc3RfYWRkciA9IGFkZHI7CisKKwlzd2l0Y2ggKGNvbW1hbmQpIHsK KwljYXNlIE5BTkRfQ01EX1JFU0VUOgorCQluYXRpdmVfY21kID0gQ01EX0ZMQVNIX1JFU0VUOwor CQlicmVhazsKKwljYXNlIE5BTkRfQ01EX1NUQVRVUzoKKwkJbmF0aXZlX2NtZCA9IENNRF9TVEFU VVNfUkVBRDsKKwkJYnJlYWs7CisJY2FzZSBOQU5EX0NNRF9SRUFESUQ6CisJCW5hdGl2ZV9jbWQg PSBDTURfREVWSUNFX0lEX1JFQUQ7CisJCWJyZWFrOworCWNhc2UgTkFORF9DTURfUkVBRE9PQjoK KwkJbmF0aXZlX2NtZCA9IENNRF9TUEFSRV9BUkVBX1JFQUQ7CisJCWJyZWFrOworCWNhc2UgTkFO RF9DTURfRVJBU0UxOgorCQluYXRpdmVfY21kID0gQ01EX0JMT0NLX0VSQVNFOworCQlicmNtbmFu ZF93cChtdGQsIDApOworCQlicmVhazsKKwljYXNlIE5BTkRfQ01EX1BBUkFNOgorCQluYXRpdmVf Y21kID0gQ01EX1BBUkFNRVRFUl9SRUFEOworCQlicmVhazsKKwljYXNlIE5BTkRfQ01EX1NFVF9G RUFUVVJFUzoKKwljYXNlIE5BTkRfQ01EX0dFVF9GRUFUVVJFUzoKKwkJYnJjbW5hbmRfbG93X2xl dmVsX29wKGhvc3QsIExMX09QX0NNRCwgY29tbWFuZCwgZmFsc2UpOworCQlicmNtbmFuZF9sb3df bGV2ZWxfb3AoaG9zdCwgTExfT1BfQUREUiwgY29sdW1uLCBmYWxzZSk7CisJCWJyZWFrOworCWNh c2UgTkFORF9DTURfUk5ET1VUOgorCQluYXRpdmVfY21kID0gQ01EX1BBUkFNRVRFUl9DSEFOR0Vf Q09MOworCQlhZGRyICY9IH4oKHU2NCkoRkNfQllURVMgLSAxKSk7CisJCS8qCisJCSAqIEhXIHF1 aXJrOiBQQVJBTUVURVJfQ0hBTkdFX0NPTCByZXF1aXJlcyBTRUNUT1JfU0laRV8xSz0wCisJCSAq IE5COiBod2NmZy5zZWN0b3Jfc2l6ZV8xayBtYXkgbm90IGJlIGluaXRpYWxpemVkIHlldAorCQkg Ki8KKwkJaWYgKGJyY21uYW5kX2dldF9zZWN0b3Jfc2l6ZV8xayhob3N0KSkgeworCQkJaG9zdC0+ aHdjZmcuc2VjdG9yX3NpemVfMWsgPQorCQkJCWJyY21uYW5kX2dldF9zZWN0b3Jfc2l6ZV8xayho b3N0KTsKKwkJCWJyY21uYW5kX3NldF9zZWN0b3Jfc2l6ZV8xayhob3N0LCAwKTsKKwkJfQorCQli cmVhazsKKwl9CisKKwlpZiAoIW5hdGl2ZV9jbWQpCisJCXJldHVybjsKKworCWJyY21uYW5kX3dy aXRlX3JlZyhjdHJsLCBCUkNNTkFORF9DTURfRVhUX0FERFJFU1MsCisJCShob3N0LT5jcyA8PCAx NikgfCAoKGFkZHIgPj4gMzIpICYgMHhmZmZmKSk7CisJKHZvaWQpYnJjbW5hbmRfcmVhZF9yZWco Y3RybCwgQlJDTU5BTkRfQ01EX0VYVF9BRERSRVNTKTsKKwlicmNtbmFuZF93cml0ZV9yZWcoY3Ry bCwgQlJDTU5BTkRfQ01EX0FERFJFU1MsIGxvd2VyXzMyX2JpdHMoYWRkcikpOworCSh2b2lkKWJy Y21uYW5kX3JlYWRfcmVnKGN0cmwsIEJSQ01OQU5EX0NNRF9BRERSRVNTKTsKKworCWJyY21uYW5k X3NlbmRfY21kKGhvc3QsIG5hdGl2ZV9jbWQpOworCWJyY21uYW5kX3dhaXRmdW5jKG10ZCwgY2hp cCk7CisKKwlpZiAobmF0aXZlX2NtZCA9PSBDTURfUEFSQU1FVEVSX1JFQUQgfHwKKwkJCW5hdGl2 ZV9jbWQgPT0gQ01EX1BBUkFNRVRFUl9DSEFOR0VfQ09MKSB7CisJCWludCBpOworCQkvKgorCQkg KiBNdXN0IGNhY2hlIHRoZSBGTEFTSF9DQUNIRSBub3csIHNpbmNlIGNoYW5nZXMgaW4KKwkJICog U0VDVE9SX1NJWkVfMUsgbWF5IGludmFsaWRhdGUgaXQKKwkJICovCisJCWZvciAoaSA9IDA7IGkg PCBGQ19XT1JEUzsgaSsrKQorCQkJY3RybC0+Zmxhc2hfY2FjaGVbaV0gPSBicmNtbmFuZF9yZWFk X2ZjKGN0cmwsIGkpOworCQkvKiBDbGVhbnVwIGZyb20gSFcgcXVpcms6IHJlc3RvcmUgU0VDVE9S X1NJWkVfMUsgKi8KKwkJaWYgKGhvc3QtPmh3Y2ZnLnNlY3Rvcl9zaXplXzFrKQorCQkJYnJjbW5h bmRfc2V0X3NlY3Rvcl9zaXplXzFrKGhvc3QsCisJCQkJCQkgICAgaG9zdC0+aHdjZmcuc2VjdG9y X3NpemVfMWspOworCX0KKworCS8qIFJlLWVuYWJsZSBwcm90ZWN0aW9uIGlzIG5lY2Vzc2FyeSBv bmx5IGFmdGVyIGVyYXNlICovCisJaWYgKGNvbW1hbmQgPT0gTkFORF9DTURfRVJBU0UxKQorCQli cmNtbmFuZF93cChtdGQsIDEpOworfQorCitzdGF0aWMgdWludDhfdCBicmNtbmFuZF9yZWFkX2J5 dGUoc3RydWN0IG10ZF9pbmZvICptdGQpCit7CisJc3RydWN0IG5hbmRfY2hpcCAqY2hpcCA9IG10 ZC0+cHJpdjsKKwlzdHJ1Y3QgYnJjbW5hbmRfaG9zdCAqaG9zdCA9IGNoaXAtPnByaXY7CisJc3Ry dWN0IGJyY21uYW5kX2NvbnRyb2xsZXIgKmN0cmwgPSBob3N0LT5jdHJsOworCXVpbnQ4X3QgcmV0 ID0gMDsKKwlpbnQgYWRkciwgb2ZmczsKKworCXN3aXRjaCAoaG9zdC0+bGFzdF9jbWQpIHsKKwlj YXNlIE5BTkRfQ01EX1JFQURJRDoKKwkJaWYgKGhvc3QtPmxhc3RfYnl0ZSA8IDQpCisJCQlyZXQg PSBicmNtbmFuZF9yZWFkX3JlZyhjdHJsLCBCUkNNTkFORF9JRCkgPj4KKwkJCQkoMjQgLSAoaG9z dC0+bGFzdF9ieXRlIDw8IDMpKTsKKwkJZWxzZSBpZiAoaG9zdC0+bGFzdF9ieXRlIDwgOCkKKwkJ CXJldCA9IGJyY21uYW5kX3JlYWRfcmVnKGN0cmwsIEJSQ01OQU5EX0lEX0VYVCkgPj4KKwkJCQko NTYgLSAoaG9zdC0+bGFzdF9ieXRlIDw8IDMpKTsKKwkJYnJlYWs7CisKKwljYXNlIE5BTkRfQ01E X1JFQURPT0I6CisJCXJldCA9IG9vYl9yZWdfcmVhZChjdHJsLCBob3N0LT5sYXN0X2J5dGUpOwor CQlicmVhazsKKworCWNhc2UgTkFORF9DTURfU1RBVFVTOgorCQlyZXQgPSBicmNtbmFuZF9yZWFk X3JlZyhjdHJsLCBCUkNNTkFORF9JTlRGQ19TVEFUVVMpICYKKwkJCQkJSU5URkNfRkxBU0hfU1RB VFVTOworCQlpZiAod3Bfb24pIC8qIGhpZGUgV1Agc3RhdHVzICovCisJCQlyZXQgfD0gTkFORF9T VEFUVVNfV1A7CisJCWJyZWFrOworCisJY2FzZSBOQU5EX0NNRF9QQVJBTToKKwljYXNlIE5BTkRf Q01EX1JORE9VVDoKKwkJYWRkciA9IGhvc3QtPmxhc3RfYWRkciArIGhvc3QtPmxhc3RfYnl0ZTsK KwkJb2ZmcyA9IGFkZHIgJiAoRkNfQllURVMgLSAxKTsKKworCQkvKiBBdCBGQ19CWVRFUyBib3Vu ZGFyeSwgc3dpdGNoIHRvIG5leHQgY29sdW1uICovCisJCWlmIChob3N0LT5sYXN0X2J5dGUgPiAw ICYmIG9mZnMgPT0gMCkKKwkJCWNoaXAtPmNtZGZ1bmMobXRkLCBOQU5EX0NNRF9STkRPVVQsIGFk ZHIsIC0xKTsKKworCQlyZXQgPSBjdHJsLT5mbGFzaF9jYWNoZVtvZmZzID4+IDJdID4+CisJCQkJ CSgyNCAtICgob2ZmcyAmIDB4MDMpIDw8IDMpKTsKKwkJYnJlYWs7CisJY2FzZSBOQU5EX0NNRF9H RVRfRkVBVFVSRVM6CisJCWlmIChob3N0LT5sYXN0X2J5dGUgPj0gT05GSV9TVUJGRUFUVVJFX1BB UkFNX0xFTikgeworCQkJcmV0ID0gMDsKKwkJfSBlbHNlIHsKKwkJCWJvb2wgbGFzdCA9IGhvc3Qt Pmxhc3RfYnl0ZSA9PQorCQkJCU9ORklfU1VCRkVBVFVSRV9QQVJBTV9MRU4gLSAxOworCQkJYnJj bW5hbmRfbG93X2xldmVsX29wKGhvc3QsIExMX09QX1JELCAwLCBsYXN0KTsKKwkJCXJldCA9IGJy Y21uYW5kX3JlYWRfcmVnKGN0cmwsIEJSQ01OQU5EX0xMX1JEQVRBKSAmIDB4ZmY7CisJCX0KKwl9 CisKKwlkZXZfZGJnKGN0cmwtPmRldiwgInJlYWQgYnl0ZSA9IDB4JTAyeFxuIiwgcmV0KTsKKwlo b3N0LT5sYXN0X2J5dGUrKzsKKworCXJldHVybiByZXQ7Cit9CisKK3N0YXRpYyB2b2lkIGJyY21u YW5kX3JlYWRfYnVmKHN0cnVjdCBtdGRfaW5mbyAqbXRkLCB1aW50OF90ICpidWYsIGludCBsZW4p Cit7CisJaW50IGk7CisKKwlmb3IgKGkgPSAwOyBpIDwgbGVuOyBpKyssIGJ1ZisrKQorCQkqYnVm ID0gYnJjbW5hbmRfcmVhZF9ieXRlKG10ZCk7Cit9CisKK3N0YXRpYyB2b2lkIGJyY21uYW5kX3dy aXRlX2J1ZihzdHJ1Y3QgbXRkX2luZm8gKm10ZCwgY29uc3QgdWludDhfdCAqYnVmLAorCQkJCSAg IGludCBsZW4pCit7CisJaW50IGk7CisJc3RydWN0IG5hbmRfY2hpcCAqY2hpcCA9IG10ZC0+cHJp djsKKwlzdHJ1Y3QgYnJjbW5hbmRfaG9zdCAqaG9zdCA9IGNoaXAtPnByaXY7CisKKwlzd2l0Y2gg KGhvc3QtPmxhc3RfY21kKSB7CisJY2FzZSBOQU5EX0NNRF9TRVRfRkVBVFVSRVM6CisJCWZvciAo aSA9IDA7IGkgPCBsZW47IGkrKykKKwkJCWJyY21uYW5kX2xvd19sZXZlbF9vcChob3N0LCBMTF9P UF9XUiwgYnVmW2ldLAorCQkJCQkJICAoaSArIDEpID09IGxlbik7CisJCWJyZWFrOworCWRlZmF1 bHQ6CisJCUJVRygpOworCQlicmVhazsKKwl9Cit9CisKKy8qKgorICogQ29uc3RydWN0IGEgRkxB U0hfRE1BIGRlc2NyaXB0b3IgYXMgcGFydCBvZiBhIGxpbmtlZCBsaXN0LiBZb3UgbXVzdCBrbm93 IHRoZQorICogZm9sbG93aW5nIGFoZWFkIG9mIHRpbWU6CisgKiAgLSBJcyB0aGlzIGRlc2NyaXB0 b3IgdGhlIGJlZ2lubmluZyBvciBlbmQgb2YgYSBsaW5rZWQgbGlzdD8KKyAqICAtIFdoYXQgaXMg dGhlIChETUEpIGFkZHJlc3Mgb2YgdGhlIG5leHQgZGVzY3JpcHRvciBpbiB0aGUgbGlua2VkIGxp c3Q/CisgKi8KK3N0YXRpYyBpbnQgYnJjbW5hbmRfZmlsbF9kbWFfZGVzYyhzdHJ1Y3QgYnJjbW5h bmRfaG9zdCAqaG9zdCwKKwkJCQkgIHN0cnVjdCBicmNtX25hbmRfZG1hX2Rlc2MgKmRlc2MsIHU2 NCBhZGRyLAorCQkJCSAgZG1hX2FkZHJfdCBidWYsIHUzMiBsZW4sIHU4IGRtYV9jbWQsCisJCQkJ ICBib29sIGJlZ2luLCBib29sIGVuZCwKKwkJCQkgIGRtYV9hZGRyX3QgbmV4dF9kZXNjKQorewor CW1lbXNldChkZXNjLCAwLCBzaXplb2YoKmRlc2MpKTsKKwkvKiBEZXNjcmlwdG9ycyBhcmUgd3Jp dHRlbiBpbiBuYXRpdmUgYnl0ZSBvcmRlciAod29yZHdpc2UpICovCisJZGVzYy0+bmV4dF9kZXNj ID0gbG93ZXJfMzJfYml0cyhuZXh0X2Rlc2MpOworCWRlc2MtPm5leHRfZGVzY19leHQgPSB1cHBl cl8zMl9iaXRzKG5leHRfZGVzYyk7CisJZGVzYy0+Y21kX2lycSA9IChkbWFfY21kIDw8IDI0KSB8 CisJCShlbmQgPyAoMHgwMyA8PCA4KSA6IDApIHwgLyogSVJRIHwgU1RPUCAqLworCQkoISFiZWdp bikgfCAoKCEhZW5kKSA8PCAxKTsgLyogaGVhZCwgdGFpbCAqLworI2lmZGVmIENPTkZJR19DUFVf QklHX0VORElBTgorCWRlc2MtPmNtZF9pcnEgfD0gMHgwMSA8PCAxMjsKKyNlbmRpZgorCWRlc2Mt PmRyYW1fYWRkciA9IGxvd2VyXzMyX2JpdHMoYnVmKTsKKwlkZXNjLT5kcmFtX2FkZHJfZXh0ID0g dXBwZXJfMzJfYml0cyhidWYpOworCWRlc2MtPnRmcl9sZW4gPSBsZW47CisJZGVzYy0+dG90YWxf bGVuID0gbGVuOworCWRlc2MtPmZsYXNoX2FkZHIgPSBsb3dlcl8zMl9iaXRzKGFkZHIpOworCWRl c2MtPmZsYXNoX2FkZHJfZXh0ID0gdXBwZXJfMzJfYml0cyhhZGRyKTsKKwlkZXNjLT5jcyA9IGhv c3QtPmNzOworCWRlc2MtPnN0YXR1c192YWxpZCA9IDB4MDE7CisJcmV0dXJuIDA7Cit9CisKKy8q KgorICogS2ljayB0aGUgRkxBU0hfRE1BIGVuZ2luZSwgd2l0aCBhIGdpdmVuIERNQSBkZXNjcmlw dG9yCisgKi8KK3N0YXRpYyB2b2lkIGJyY21uYW5kX2RtYV9ydW4oc3RydWN0IGJyY21uYW5kX2hv c3QgKmhvc3QsIGRtYV9hZGRyX3QgZGVzYykKK3sKKwlzdHJ1Y3QgYnJjbW5hbmRfY29udHJvbGxl ciAqY3RybCA9IGhvc3QtPmN0cmw7CisJdW5zaWduZWQgbG9uZyB0aW1lbyA9IG1zZWNzX3RvX2pp ZmZpZXMoMTAwKTsKKworCWZsYXNoX2RtYV93cml0ZWwoY3RybCwgRkxBU0hfRE1BX0ZJUlNUX0RF U0MsIGxvd2VyXzMyX2JpdHMoZGVzYykpOworCSh2b2lkKWZsYXNoX2RtYV9yZWFkbChjdHJsLCBG TEFTSF9ETUFfRklSU1RfREVTQyk7CisJZmxhc2hfZG1hX3dyaXRlbChjdHJsLCBGTEFTSF9ETUFf RklSU1RfREVTQ19FWFQsIHVwcGVyXzMyX2JpdHMoZGVzYykpOworCSh2b2lkKWZsYXNoX2RtYV9y ZWFkbChjdHJsLCBGTEFTSF9ETUFfRklSU1RfREVTQ19FWFQpOworCisJLyogU3RhcnQgRkxBU0hf RE1BIGVuZ2luZSAqLworCWN0cmwtPmRtYV9wZW5kaW5nID0gdHJ1ZTsKKwltYigpOyAvKiBmbHVz aCBwcmV2aW91cyB3cml0ZXMgKi8KKwlmbGFzaF9kbWFfd3JpdGVsKGN0cmwsIEZMQVNIX0RNQV9D VFJMLCAweDAzKTsgLyogd2FrZSB8IHJ1biAqLworCisJaWYgKHdhaXRfZm9yX2NvbXBsZXRpb25f dGltZW91dCgmY3RybC0+ZG1hX2RvbmUsIHRpbWVvKSA8PSAwKSB7CisJCWRldl9lcnIoY3RybC0+ ZGV2LAorCQkJCSJ0aW1lb3V0IHdhaXRpbmcgZm9yIERNQTsgc3RhdHVzICUjeCwgZXJyb3Igc3Rh dHVzICUjeFxuIiwKKwkJCQlmbGFzaF9kbWFfcmVhZGwoY3RybCwgRkxBU0hfRE1BX1NUQVRVUyks CisJCQkJZmxhc2hfZG1hX3JlYWRsKGN0cmwsIEZMQVNIX0RNQV9FUlJPUl9TVEFUVVMpKTsKKwl9 CisJY3RybC0+ZG1hX3BlbmRpbmcgPSBmYWxzZTsKKwlmbGFzaF9kbWFfd3JpdGVsKGN0cmwsIEZM QVNIX0RNQV9DVFJMLCAwKTsgLyogZm9yY2Ugc3RvcCAqLworfQorCitzdGF0aWMgaW50IGJyY21u YW5kX2RtYV90cmFucyhzdHJ1Y3QgYnJjbW5hbmRfaG9zdCAqaG9zdCwgdTY0IGFkZHIsIHUzMiAq YnVmLAorCQkJICAgICAgdTMyIGxlbiwgdTggZG1hX2NtZCkKK3sKKwlzdHJ1Y3QgYnJjbW5hbmRf Y29udHJvbGxlciAqY3RybCA9IGhvc3QtPmN0cmw7CisJZG1hX2FkZHJfdCBidWZfcGE7CisJaW50 IGRpciA9IGRtYV9jbWQgPT0gQ01EX1BBR0VfUkVBRCA/IERNQV9GUk9NX0RFVklDRSA6IERNQV9U T19ERVZJQ0U7CisKKwlidWZfcGEgPSBkbWFfbWFwX3NpbmdsZShjdHJsLT5kZXYsIGJ1ZiwgbGVu LCBkaXIpOworCWlmIChkbWFfbWFwcGluZ19lcnJvcihjdHJsLT5kZXYsIGJ1Zl9wYSkpIHsKKwkJ ZGV2X2VycihjdHJsLT5kZXYsICJ1bmFibGUgdG8gbWFwIGJ1ZmZlciBmb3IgRE1BXG4iKTsKKwkJ cmV0dXJuIC1FTk9NRU07CisJfQorCisJYnJjbW5hbmRfZmlsbF9kbWFfZGVzYyhob3N0LCBjdHJs LT5kbWFfZGVzYywgYWRkciwgYnVmX3BhLCBsZW4sCisJCQkJICAgZG1hX2NtZCwgdHJ1ZSwgdHJ1 ZSwgMCk7CisKKwlicmNtbmFuZF9kbWFfcnVuKGhvc3QsIGN0cmwtPmRtYV9wYSk7CisKKwlkbWFf dW5tYXBfc2luZ2xlKGN0cmwtPmRldiwgYnVmX3BhLCBsZW4sIGRpcik7CisKKwlpZiAoY3RybC0+ ZG1hX2Rlc2MtPnN0YXR1c192YWxpZCAmIEZMQVNIX0RNQV9FQ0NfRVJST1IpCisJCXJldHVybiAt RUJBRE1TRzsKKwllbHNlIGlmIChjdHJsLT5kbWFfZGVzYy0+c3RhdHVzX3ZhbGlkICYgRkxBU0hf RE1BX0NPUlJfRVJST1IpCisJCXJldHVybiAtRVVDTEVBTjsKKworCXJldHVybiAwOworfQorCisv KgorICogQXNzdW1lcyBwcm9wZXIgQ1MgaXMgYWxyZWFkeSBzZXQKKyAqLworc3RhdGljIGludCBi cmNtbmFuZF9yZWFkX2J5X3BpbyhzdHJ1Y3QgbXRkX2luZm8gKm10ZCwgc3RydWN0IG5hbmRfY2hp cCAqY2hpcCwKKwkJCQl1NjQgYWRkciwgdW5zaWduZWQgaW50IHRyYW5zLCB1MzIgKmJ1ZiwKKwkJ CQl1OCAqb29iLCB1NjQgKmVycl9hZGRyKQoreworCXN0cnVjdCBicmNtbmFuZF9ob3N0ICpob3N0 ID0gY2hpcC0+cHJpdjsKKwlzdHJ1Y3QgYnJjbW5hbmRfY29udHJvbGxlciAqY3RybCA9IGhvc3Qt PmN0cmw7CisJaW50IGksIGosIHJldCA9IDA7CisKKwkvKiBDbGVhciBlcnJvciBhZGRyZXNzZXMg Ki8KKwlicmNtbmFuZF93cml0ZV9yZWcoY3RybCwgQlJDTU5BTkRfVU5DT1JSX0FERFIsIDApOwor CWJyY21uYW5kX3dyaXRlX3JlZyhjdHJsLCBCUkNNTkFORF9DT1JSX0FERFIsIDApOworCisJYnJj bW5hbmRfd3JpdGVfcmVnKGN0cmwsIEJSQ01OQU5EX0NNRF9FWFRfQUREUkVTUywKKwkJCShob3N0 LT5jcyA8PCAxNikgfCAoKGFkZHIgPj4gMzIpICYgMHhmZmZmKSk7CisJKHZvaWQpYnJjbW5hbmRf cmVhZF9yZWcoY3RybCwgQlJDTU5BTkRfQ01EX0VYVF9BRERSRVNTKTsKKworCWZvciAoaSA9IDA7 IGkgPCB0cmFuczsgaSsrLCBhZGRyICs9IEZDX0JZVEVTKSB7CisJCWJyY21uYW5kX3dyaXRlX3Jl ZyhjdHJsLCBCUkNNTkFORF9DTURfQUREUkVTUywKKwkJCQkgICBsb3dlcl8zMl9iaXRzKGFkZHIp KTsKKwkJKHZvaWQpYnJjbW5hbmRfcmVhZF9yZWcoY3RybCwgQlJDTU5BTkRfQ01EX0FERFJFU1Mp OworCQkvKiBTUEFSRV9BUkVBX1JFQUQgZG9lcyBub3QgdXNlIEVDQywgc28ganVzdCB1c2UgUEFH RV9SRUFEICovCisJCWJyY21uYW5kX3NlbmRfY21kKGhvc3QsIENNRF9QQUdFX1JFQUQpOworCQli cmNtbmFuZF93YWl0ZnVuYyhtdGQsIGNoaXApOworCisJCWlmIChsaWtlbHkoYnVmKSkKKwkJCWZv ciAoaiA9IDA7IGogPCBGQ19XT1JEUzsgaisrLCBidWYrKykKKwkJCQkqYnVmID0gYnJjbW5hbmRf cmVhZF9mYyhjdHJsLCBqKTsKKworCQlpZiAob29iKQorCQkJb29iICs9IHJlYWRfb29iX2Zyb21f cmVncyhjdHJsLCBpLCBvb2IsCisJCQkJCW10ZC0+b29ic2l6ZSAvIHRyYW5zLAorCQkJCQlob3N0 LT5od2NmZy5zZWN0b3Jfc2l6ZV8xayk7CisKKwkJaWYgKCFyZXQpIHsKKwkJCSplcnJfYWRkciA9 IGJyY21uYW5kX3JlYWRfcmVnKGN0cmwsCisJCQkJCUJSQ01OQU5EX1VOQ09SUl9BRERSKSB8CisJ CQkJKCh1NjQpKGJyY21uYW5kX3JlYWRfcmVnKGN0cmwsCisJCQkJCQlCUkNNTkFORF9VTkNPUlJf RVhUX0FERFIpCisJCQkJCSYgMHhmZmZmKSA8PCAzMik7CisJCQlpZiAoKmVycl9hZGRyKQorCQkJ CXJldCA9IC1FQkFETVNHOworCQl9CisKKwkJaWYgKCFyZXQpIHsKKwkJCSplcnJfYWRkciA9IGJy Y21uYW5kX3JlYWRfcmVnKGN0cmwsCisJCQkJCUJSQ01OQU5EX0NPUlJfQUREUikgfAorCQkJCSgo dTY0KShicmNtbmFuZF9yZWFkX3JlZyhjdHJsLAorCQkJCQkJQlJDTU5BTkRfQ09SUl9FWFRfQURE UikKKwkJCQkJJiAweGZmZmYpIDw8IDMyKTsKKwkJCWlmICgqZXJyX2FkZHIpCisJCQkJcmV0ID0g LUVVQ0xFQU47CisJCX0KKwl9CisKKwlyZXR1cm4gcmV0OworfQorCitzdGF0aWMgaW50IGJyY21u YW5kX3JlYWQoc3RydWN0IG10ZF9pbmZvICptdGQsIHN0cnVjdCBuYW5kX2NoaXAgKmNoaXAsCisJ CQkgdTY0IGFkZHIsIHVuc2lnbmVkIGludCB0cmFucywgdTMyICpidWYsIHU4ICpvb2IpCit7CisJ c3RydWN0IGJyY21uYW5kX2hvc3QgKmhvc3QgPSBjaGlwLT5wcml2OworCXN0cnVjdCBicmNtbmFu ZF9jb250cm9sbGVyICpjdHJsID0gaG9zdC0+Y3RybDsKKwl1NjQgZXJyX2FkZHIgPSAwOworCWlu dCBlcnI7CisKKwlkZXZfZGJnKGN0cmwtPmRldiwgInJlYWQgJWxseCAtPiAlcFxuIiwgKHVuc2ln bmVkIGxvbmcgbG9uZylhZGRyLCBidWYpOworCisJYnJjbW5hbmRfd3JpdGVfcmVnKGN0cmwsIEJS Q01OQU5EX1VOQ09SUl9DT1VOVCwgMCk7CisKKwlpZiAoaGFzX2ZsYXNoX2RtYShjdHJsKSAmJiAh b29iICYmIGZsYXNoX2RtYV9idWZfb2soYnVmKSkgeworCQllcnIgPSBicmNtbmFuZF9kbWFfdHJh bnMoaG9zdCwgYWRkciwgYnVmLCB0cmFucyAqIEZDX0JZVEVTLAorCQkJCQkgICAgIENNRF9QQUdF X1JFQUQpOworCQlpZiAoZXJyKSB7CisJCQlpZiAobXRkX2lzX2JpdGZsaXBfb3JfZWNjZXJyKGVy cikpCisJCQkJZXJyX2FkZHIgPSBhZGRyOworCQkJZWxzZQorCQkJCXJldHVybiAtRUlPOworCQl9 CisJfSBlbHNlIHsKKwkJaWYgKG9vYikKKwkJCW1lbXNldChvb2IsIDB4OTksIG10ZC0+b29ic2l6 ZSk7CisKKwkJZXJyID0gYnJjbW5hbmRfcmVhZF9ieV9waW8obXRkLCBjaGlwLCBhZGRyLCB0cmFu cywgYnVmLAorCQkJCQkgICAgICAgb29iLCAmZXJyX2FkZHIpOworCX0KKworCWlmIChtdGRfaXNf ZWNjZXJyKGVycikpIHsKKwkJZGV2X2RiZyhjdHJsLT5kZXYsICJ1bmNvcnJlY3RhYmxlIGVycm9y IGF0IDB4JWxseFxuIiwKKwkJCSh1bnNpZ25lZCBsb25nIGxvbmcpZXJyX2FkZHIpOworCQltdGQt PmVjY19zdGF0cy5mYWlsZWQrKzsKKwkJLyogTkFORCBsYXllciBleHBlY3RzIHplcm8gb24gRUND IGVycm9ycyAqLworCQlyZXR1cm4gMDsKKwl9CisKKwlpZiAobXRkX2lzX2JpdGZsaXAoZXJyKSkg eworCQl1bnNpZ25lZCBpbnQgY29ycmVjdGVkID0gYnJjbW5hbmRfY291bnRfY29ycmVjdGVkKGN0 cmwpOworCisJCWRldl9kYmcoY3RybC0+ZGV2LCAiY29ycmVjdGVkIGVycm9yIGF0IDB4JWxseFxu IiwKKwkJCSh1bnNpZ25lZCBsb25nIGxvbmcpZXJyX2FkZHIpOworCQltdGQtPmVjY19zdGF0cy5j b3JyZWN0ZWQgKz0gY29ycmVjdGVkOworCQkvKiBBbHdheXMgZXhjZWVkIHRoZSBzb2Z0d2FyZS1p bXBvc2VkIHRocmVzaG9sZCAqLworCQlyZXR1cm4gbWF4KG10ZC0+Yml0ZmxpcF90aHJlc2hvbGQs IGNvcnJlY3RlZCk7CisJfQorCisJcmV0dXJuIDA7Cit9CisKK3N0YXRpYyBpbnQgYnJjbW5hbmRf cmVhZF9wYWdlKHN0cnVjdCBtdGRfaW5mbyAqbXRkLCBzdHJ1Y3QgbmFuZF9jaGlwICpjaGlwLAor CQkJICAgICAgdWludDhfdCAqYnVmLCBpbnQgb29iX3JlcXVpcmVkLCBpbnQgcGFnZSkKK3sKKwlz dHJ1Y3QgYnJjbW5hbmRfaG9zdCAqaG9zdCA9IGNoaXAtPnByaXY7CisJdTggKm9vYiA9IG9vYl9y ZXF1aXJlZCA/ICh1OCAqKWNoaXAtPm9vYl9wb2kgOiBOVUxMOworCisJcmV0dXJuIGJyY21uYW5k X3JlYWQobXRkLCBjaGlwLCBob3N0LT5sYXN0X2FkZHIsCisJCQltdGQtPndyaXRlc2l6ZSA+PiBG Q19TSElGVCwgKHUzMiAqKWJ1Ziwgb29iKTsKK30KKworc3RhdGljIGludCBicmNtbmFuZF9yZWFk X3BhZ2VfcmF3KHN0cnVjdCBtdGRfaW5mbyAqbXRkLCBzdHJ1Y3QgbmFuZF9jaGlwICpjaGlwLAor CQkJCSAgdWludDhfdCAqYnVmLCBpbnQgb29iX3JlcXVpcmVkLCBpbnQgcGFnZSkKK3sKKwlzdHJ1 Y3QgYnJjbW5hbmRfaG9zdCAqaG9zdCA9IGNoaXAtPnByaXY7CisJdTggKm9vYiA9IG9vYl9yZXF1 aXJlZCA/ICh1OCAqKWNoaXAtPm9vYl9wb2kgOiBOVUxMOworCWludCByZXQ7CisKKwlicmNtbmFu ZF9zZXRfZWNjX2VuYWJsZWQoaG9zdCwgMCk7CisJcmV0ID0gYnJjbW5hbmRfcmVhZChtdGQsIGNo aXAsIGhvc3QtPmxhc3RfYWRkciwKKwkJCW10ZC0+d3JpdGVzaXplID4+IEZDX1NISUZULCAodTMy ICopYnVmLCBvb2IpOworCWJyY21uYW5kX3NldF9lY2NfZW5hYmxlZChob3N0LCAxKTsKKwlyZXR1 cm4gcmV0OworfQorCitzdGF0aWMgaW50IGJyY21uYW5kX3JlYWRfb29iKHN0cnVjdCBtdGRfaW5m byAqbXRkLCBzdHJ1Y3QgbmFuZF9jaGlwICpjaGlwLAorCQkJICAgICBpbnQgcGFnZSkKK3sKKwly ZXR1cm4gYnJjbW5hbmRfcmVhZChtdGQsIGNoaXAsICh1NjQpcGFnZSA8PCBjaGlwLT5wYWdlX3No aWZ0LAorCQkJbXRkLT53cml0ZXNpemUgPj4gRkNfU0hJRlQsCisJCQlOVUxMLCAodTggKiljaGlw LT5vb2JfcG9pKTsKK30KKworc3RhdGljIGludCBicmNtbmFuZF9yZWFkX29vYl9yYXcoc3RydWN0 IG10ZF9pbmZvICptdGQsIHN0cnVjdCBuYW5kX2NoaXAgKmNoaXAsCisJCQkJIGludCBwYWdlKQor eworCXN0cnVjdCBicmNtbmFuZF9ob3N0ICpob3N0ID0gY2hpcC0+cHJpdjsKKworCWJyY21uYW5k X3NldF9lY2NfZW5hYmxlZChob3N0LCAwKTsKKwlicmNtbmFuZF9yZWFkKG10ZCwgY2hpcCwgKHU2 NClwYWdlIDw8IGNoaXAtPnBhZ2Vfc2hpZnQsCisJCW10ZC0+d3JpdGVzaXplID4+IEZDX1NISUZU LAorCQlOVUxMLCAodTggKiljaGlwLT5vb2JfcG9pKTsKKwlicmNtbmFuZF9zZXRfZWNjX2VuYWJs ZWQoaG9zdCwgMSk7CisJcmV0dXJuIDA7Cit9CisKK3N0YXRpYyBpbnQgYnJjbW5hbmRfcmVhZF9z dWJwYWdlKHN0cnVjdCBtdGRfaW5mbyAqbXRkLCBzdHJ1Y3QgbmFuZF9jaGlwICpjaGlwLAorCQkJ CSB1aW50MzJfdCBkYXRhX29mZnMsIHVpbnQzMl90IHJlYWRsZW4sCisJCQkJIHVpbnQ4X3QgKmJ1 ZnBvaSwgaW50IHBhZ2UpCit7CisJc3RydWN0IGJyY21uYW5kX2hvc3QgKmhvc3QgPSBjaGlwLT5w cml2OworCisJcmV0dXJuIGJyY21uYW5kX3JlYWQobXRkLCBjaGlwLCBob3N0LT5sYXN0X2FkZHIg KyBkYXRhX29mZnMsCisJCQlyZWFkbGVuID4+IEZDX1NISUZULCAodTMyICopYnVmcG9pLCBOVUxM KTsKK30KKworc3RhdGljIGludCBicmNtbmFuZF93cml0ZShzdHJ1Y3QgbXRkX2luZm8gKm10ZCwg c3RydWN0IG5hbmRfY2hpcCAqY2hpcCwKKwkJCSAgdTY0IGFkZHIsIGNvbnN0IHUzMiAqYnVmLCB1 OCAqb29iKQoreworCXN0cnVjdCBicmNtbmFuZF9ob3N0ICpob3N0ID0gY2hpcC0+cHJpdjsKKwlz dHJ1Y3QgYnJjbW5hbmRfY29udHJvbGxlciAqY3RybCA9IGhvc3QtPmN0cmw7CisJdW5zaWduZWQg aW50IGksIGosIHRyYW5zID0gbXRkLT53cml0ZXNpemUgPj4gRkNfU0hJRlQ7CisJaW50IHN0YXR1 cywgcmV0ID0gMDsKKworCWRldl9kYmcoY3RybC0+ZGV2LCAid3JpdGUgJWxseCA8LSAlcFxuIiwg KHVuc2lnbmVkIGxvbmcgbG9uZylhZGRyLCBidWYpOworCisJaWYgKHVubGlrZWx5KCh1MzIpYnVm ICYgMHgwMykpIHsKKwkJZGV2X3dhcm4oY3RybC0+ZGV2LCAidW5hbGlnbmVkIGJ1ZmZlcjogJXBc biIsIGJ1Zik7CisJCWJ1ZiA9ICh1MzIgKikoKHUzMilidWYgJiB+MHgwMyk7CisJfQorCisJYnJj bW5hbmRfd3AobXRkLCAwKTsKKworCWZvciAoaSA9IDA7IGkgPCBjdHJsLT5tYXhfb29iOyBpICs9 IDQpCisJCW9vYl9yZWdfd3JpdGUoY3RybCwgaSwgMHhmZmZmZmZmZik7CisKKwlpZiAoaGFzX2Zs YXNoX2RtYShjdHJsKSAmJiAhb29iICYmIGZsYXNoX2RtYV9idWZfb2soYnVmKSkgeworCQlpZiAo YnJjbW5hbmRfZG1hX3RyYW5zKGhvc3QsIGFkZHIsICh1MzIgKilidWYsCisJCQkJCW10ZC0+d3Jp dGVzaXplLCBDTURfUFJPR1JBTV9QQUdFKSkKKwkJCXJldCA9IC1FSU87CisJCWdvdG8gb3V0Owor CX0KKworCWJyY21uYW5kX3dyaXRlX3JlZyhjdHJsLCBCUkNNTkFORF9DTURfRVhUX0FERFJFU1Ms CisJCQkoaG9zdC0+Y3MgPDwgMTYpIHwgKChhZGRyID4+IDMyKSAmIDB4ZmZmZikpOworCSh2b2lk KWJyY21uYW5kX3JlYWRfcmVnKGN0cmwsIEJSQ01OQU5EX0NNRF9FWFRfQUREUkVTUyk7CisKKwlm b3IgKGkgPSAwOyBpIDwgdHJhbnM7IGkrKywgYWRkciArPSBGQ19CWVRFUykgeworCQkvKiBmdWxs IGFkZHJlc3MgTVVTVCBiZSBzZXQgYmVmb3JlIHBvcHVsYXRpbmcgRkMgKi8KKwkJYnJjbW5hbmRf d3JpdGVfcmVnKGN0cmwsIEJSQ01OQU5EX0NNRF9BRERSRVNTLAorCQkJCSAgIGxvd2VyXzMyX2Jp dHMoYWRkcikpOworCQkodm9pZClicmNtbmFuZF9yZWFkX3JlZyhjdHJsLCBCUkNNTkFORF9DTURf QUREUkVTUyk7CisKKwkJaWYgKGJ1ZikKKwkJCWZvciAoaiA9IDA7IGogPCBGQ19XT1JEUzsgaisr LCBidWYrKykKKwkJCQlicmNtbmFuZF93cml0ZV9mYyhjdHJsLCBqLCAqYnVmKTsKKwkJZWxzZSBp ZiAob29iKQorCQkJZm9yIChqID0gMDsgaiA8IEZDX1dPUkRTOyBqKyspCisJCQkJYnJjbW5hbmRf d3JpdGVfZmMoY3RybCwgaiwgMHhmZmZmZmZmZik7CisKKwkJaWYgKG9vYikgeworCQkJb29iICs9 IHdyaXRlX29vYl90b19yZWdzKGN0cmwsIGksIG9vYiwKKwkJCQkJbXRkLT5vb2JzaXplIC8gdHJh bnMsCisJCQkJCWhvc3QtPmh3Y2ZnLnNlY3Rvcl9zaXplXzFrKTsKKwkJfQorCisJCS8qIHdlIGNh bm5vdCB1c2UgU1BBUkVfQVJFQV9QUk9HUkFNIHdoZW4gUEFSVElBTF9QQUdFX0VOPTAgKi8KKwkJ YnJjbW5hbmRfc2VuZF9jbWQoaG9zdCwgQ01EX1BST0dSQU1fUEFHRSk7CisJCXN0YXR1cyA9IGJy Y21uYW5kX3dhaXRmdW5jKG10ZCwgY2hpcCk7CisKKwkJaWYgKHN0YXR1cyAmIE5BTkRfU1RBVFVT X0ZBSUwpIHsKKwkJCWRldl9pbmZvKGN0cmwtPmRldiwgInByb2dyYW0gZmFpbGVkIGF0ICVsbHhc biIsCisJCQkJKHVuc2lnbmVkIGxvbmcgbG9uZylhZGRyKTsKKwkJCXJldCA9IC1FSU87CisJCQln b3RvIG91dDsKKwkJfQorCX0KK291dDoKKwlicmNtbmFuZF93cChtdGQsIDEpOworCXJldHVybiBy ZXQ7Cit9CisKK3N0YXRpYyBpbnQgYnJjbW5hbmRfd3JpdGVfcGFnZShzdHJ1Y3QgbXRkX2luZm8g Km10ZCwgc3RydWN0IG5hbmRfY2hpcCAqY2hpcCwKKwkJCSAgICAgICBjb25zdCB1aW50OF90ICpi dWYsIGludCBvb2JfcmVxdWlyZWQpCit7CisJc3RydWN0IGJyY21uYW5kX2hvc3QgKmhvc3QgPSBj aGlwLT5wcml2OworCXZvaWQgKm9vYiA9IG9vYl9yZXF1aXJlZCA/IGNoaXAtPm9vYl9wb2kgOiBO VUxMOworCisJYnJjbW5hbmRfd3JpdGUobXRkLCBjaGlwLCBob3N0LT5sYXN0X2FkZHIsIChjb25z dCB1MzIgKilidWYsIG9vYik7CisJcmV0dXJuIDA7Cit9CisKK3N0YXRpYyBpbnQgYnJjbW5hbmRf d3JpdGVfcGFnZV9yYXcoc3RydWN0IG10ZF9pbmZvICptdGQsCisJCQkJICAgc3RydWN0IG5hbmRf Y2hpcCAqY2hpcCwgY29uc3QgdWludDhfdCAqYnVmLAorCQkJCSAgIGludCBvb2JfcmVxdWlyZWQp Cit7CisJc3RydWN0IGJyY21uYW5kX2hvc3QgKmhvc3QgPSBjaGlwLT5wcml2OworCXZvaWQgKm9v YiA9IG9vYl9yZXF1aXJlZCA/IGNoaXAtPm9vYl9wb2kgOiBOVUxMOworCisJYnJjbW5hbmRfc2V0 X2VjY19lbmFibGVkKGhvc3QsIDApOworCWJyY21uYW5kX3dyaXRlKG10ZCwgY2hpcCwgaG9zdC0+ bGFzdF9hZGRyLCAoY29uc3QgdTMyICopYnVmLCBvb2IpOworCWJyY21uYW5kX3NldF9lY2NfZW5h YmxlZChob3N0LCAxKTsKKwlyZXR1cm4gMDsKK30KKworc3RhdGljIGludCBicmNtbmFuZF93cml0 ZV9vb2Ioc3RydWN0IG10ZF9pbmZvICptdGQsIHN0cnVjdCBuYW5kX2NoaXAgKmNoaXAsCisJCQkJ ICBpbnQgcGFnZSkKK3sKKwlyZXR1cm4gYnJjbW5hbmRfd3JpdGUobXRkLCBjaGlwLCAodTY0KXBh Z2UgPDwgY2hpcC0+cGFnZV9zaGlmdCwKKwkJCQkgIE5VTEwsIGNoaXAtPm9vYl9wb2kpOworfQor CitzdGF0aWMgaW50IGJyY21uYW5kX3dyaXRlX29vYl9yYXcoc3RydWN0IG10ZF9pbmZvICptdGQs IHN0cnVjdCBuYW5kX2NoaXAgKmNoaXAsCisJCQkJICBpbnQgcGFnZSkKK3sKKwlzdHJ1Y3QgYnJj bW5hbmRfaG9zdCAqaG9zdCA9IGNoaXAtPnByaXY7CisJaW50IHJldDsKKworCWJyY21uYW5kX3Nl dF9lY2NfZW5hYmxlZChob3N0LCAwKTsKKwlyZXQgPSBicmNtbmFuZF93cml0ZShtdGQsIGNoaXAs ICh1NjQpcGFnZSA8PCBjaGlwLT5wYWdlX3NoaWZ0LCBOVUxMLAorCQkJCSAodTggKiljaGlwLT5v b2JfcG9pKTsKKwlicmNtbmFuZF9zZXRfZWNjX2VuYWJsZWQoaG9zdCwgMSk7CisKKwlyZXR1cm4g cmV0OworfQorCisvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioq KioqKioqKioqKioqKioqKioqKioqKioqKioKKyAqIFBlci1DUyBzZXR1cCAoMSBOQU5EIGRldmlj ZSkKKyAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioq KioqKioqKioqKioqKioqKioqKi8KKworc3RhdGljIGludCBicmNtbmFuZF9zZXRfY2ZnKHN0cnVj dCBicmNtbmFuZF9ob3N0ICpob3N0LAorCQkJICAgIHN0cnVjdCBicmNtbmFuZF9jZmcgKmNmZykK K3sKKwlzdHJ1Y3QgYnJjbW5hbmRfY29udHJvbGxlciAqY3RybCA9IGhvc3QtPmN0cmw7CisJc3Ry dWN0IG5hbmRfY2hpcCAqY2hpcCA9ICZob3N0LT5jaGlwOworCXUxNiBjZmdfb2ZmcyA9IGJyY21u YW5kX2NzX29mZnNldChjdHJsLCBob3N0LT5jcywgQlJDTU5BTkRfQ1NfQ0ZHKTsKKwl1MTYgY2Zn X2V4dF9vZmZzID0gYnJjbW5hbmRfY3Nfb2Zmc2V0KGN0cmwsIGhvc3QtPmNzLAorCQkJQlJDTU5B TkRfQ1NfQ0ZHX0VYVCk7CisJdTE2IGFjY19jb250cm9sX29mZnMgPSBicmNtbmFuZF9jc19vZmZz ZXQoY3RybCwgaG9zdC0+Y3MsCisJCQlCUkNNTkFORF9DU19BQ0NfQ09OVFJPTCk7CisJdTggYmxv Y2tfc2l6ZSA9IDAsIHBhZ2Vfc2l6ZSA9IDAsIGRldmljZV9zaXplID0gMDsKKwl1MzIgdG1wOwor CisJaWYgKGN0cmwtPmJsb2NrX3NpemVzKSB7CisJCWludCBpLCBmb3VuZDsKKworCQlmb3IgKGkg PSAwLCBmb3VuZCA9IDA7IGN0cmwtPmJsb2NrX3NpemVzW2ldOyBpKyspCisJCQlpZiAoY3RybC0+ YmxvY2tfc2l6ZXNbaV0gKiAxMDI0ID09IGNmZy0+YmxvY2tfc2l6ZSkgeworCQkJCWJsb2NrX3Np emUgPSBpOworCQkJCWZvdW5kID0gMTsKKwkJCX0KKwkJaWYgKCFmb3VuZCkgeworCQkJZGV2X3dh cm4oY3RybC0+ZGV2LCAiaW52YWxpZCBibG9jayBzaXplICV1XG4iLAorCQkJCQljZmctPmJsb2Nr X3NpemUpOworCQkJcmV0dXJuIC1FSU5WQUw7CisJCX0KKwl9IGVsc2UgeworCQlibG9ja19zaXpl ID0gZmZzKGNmZy0+YmxvY2tfc2l6ZSkgLSBmZnMoQlJDTU5BTkRfTUlOX0JMT0NLU0laRSk7CisJ fQorCisJaWYgKGNmZy0+YmxvY2tfc2l6ZSA8IEJSQ01OQU5EX01JTl9CTE9DS1NJWkUgfHwgKGN0 cmwtPm1heF9ibG9ja19zaXplICYmCisJCQkJY2ZnLT5ibG9ja19zaXplID4gY3RybC0+bWF4X2Js b2NrX3NpemUpKSB7CisJCWRldl93YXJuKGN0cmwtPmRldiwgImludmFsaWQgYmxvY2sgc2l6ZSAl dVxuIiwKKwkJCQljZmctPmJsb2NrX3NpemUpOworCQlibG9ja19zaXplID0gMDsKKwl9CisKKwlp ZiAoY3RybC0+cGFnZV9zaXplcykgeworCQlpbnQgaSwgZm91bmQ7CisKKwkJZm9yIChpID0gMCwg Zm91bmQgPSAwOyBjdHJsLT5wYWdlX3NpemVzW2ldOyBpKyspCisJCQlpZiAoY3RybC0+cGFnZV9z aXplc1tpXSA9PSBjZmctPnBhZ2Vfc2l6ZSkgeworCQkJCXBhZ2Vfc2l6ZSA9IGk7CisJCQkJZm91 bmQgPSAxOworCQkJfQorCQlpZiAoIWZvdW5kKSB7CisJCQlkZXZfd2FybihjdHJsLT5kZXYsICJp bnZhbGlkIHBhZ2Ugc2l6ZSAldVxuIiwKKwkJCQkJY2ZnLT5wYWdlX3NpemUpOworCQkJcmV0dXJu IC1FSU5WQUw7CisJCX0KKwl9IGVsc2UgeworCQlwYWdlX3NpemUgPSBmZnMoY2ZnLT5wYWdlX3Np emUpIC0gZmZzKEJSQ01OQU5EX01JTl9QQUdFU0laRSk7CisJfQorCisJaWYgKGNmZy0+cGFnZV9z aXplIDwgQlJDTU5BTkRfTUlOX1BBR0VTSVpFIHx8IChjdHJsLT5tYXhfcGFnZV9zaXplICYmCisJ CQkJY2ZnLT5wYWdlX3NpemUgPiBjdHJsLT5tYXhfcGFnZV9zaXplKSkgeworCQlkZXZfd2Fybihj dHJsLT5kZXYsICJpbnZhbGlkIHBhZ2Ugc2l6ZSAldVxuIiwgY2ZnLT5wYWdlX3NpemUpOworCQly ZXR1cm4gLUVJTlZBTDsKKwl9CisKKwlpZiAoZmxzNjQoY2ZnLT5kZXZpY2Vfc2l6ZSkgPCBmbHM2 NChCUkNNTkFORF9NSU5fREVWU0laRSkpIHsKKwkJZGV2X3dhcm4oY3RybC0+ZGV2LCAiaW52YWxp ZCBkZXZpY2Ugc2l6ZSAweCVsbHhcbiIsCisJCQkodW5zaWduZWQgbG9uZyBsb25nKWNmZy0+ZGV2 aWNlX3NpemUpOworCQlyZXR1cm4gLUVJTlZBTDsKKwl9CisJZGV2aWNlX3NpemUgPSBmbHM2NChj ZmctPmRldmljZV9zaXplKSAtIGZsczY0KEJSQ01OQU5EX01JTl9ERVZTSVpFKTsKKworCXRtcCA9 IChjZmctPmJsa19hZHJfYnl0ZXMgPDwgOCkgfAorCQkoY2ZnLT5jb2xfYWRyX2J5dGVzIDw8IDEy KSB8CisJCShjZmctPmZ1bF9hZHJfYnl0ZXMgPDwgMTYpIHwKKwkJKCEhKGNmZy0+ZGV2aWNlX3dp ZHRoID09IDE2KSA8PCAyMykgfAorCQkoZGV2aWNlX3NpemUgPDwgMjQpOworCWlmIChjZmdfb2Zm cyA9PSBjZmdfZXh0X29mZnMpIHsKKwkJdG1wIHw9IChwYWdlX3NpemUgPDwgMjApIHwgKGJsb2Nr X3NpemUgPDwgMjgpOworCQluYW5kX3dyaXRlcmVnKGN0cmwsIGNmZ19vZmZzLCB0bXApOworCX0g ZWxzZSB7CisJCW5hbmRfd3JpdGVyZWcoY3RybCwgY2ZnX29mZnMsIHRtcCk7CisJCXRtcCA9IHBh Z2Vfc2l6ZSB8IChibG9ja19zaXplIDw8IDQpOworCQluYW5kX3dyaXRlcmVnKGN0cmwsIGNmZ19l eHRfb2ZmcywgdG1wKTsKKwl9CisKKwl0bXAgPSBuYW5kX3JlYWRyZWcoY3RybCwgYWNjX2NvbnRy b2xfb2Zmcyk7CisJdG1wICY9IH5icmNtbmFuZF9lY2NfbGV2ZWxfbWFzayhjdHJsKTsKKwl0bXAg fD0gY2ZnLT5lY2NfbGV2ZWwgPDwgTkFORF9BQ0NfQ09OVFJPTF9FQ0NfU0hJRlQ7CisJdG1wICY9 IH5icmNtbmFuZF9zcGFyZV9hcmVhX21hc2soY3RybCk7CisJdG1wIHw9IGNmZy0+c3BhcmVfYXJl YV9zaXplOworCW5hbmRfd3JpdGVyZWcoY3RybCwgYWNjX2NvbnRyb2xfb2ZmcywgdG1wKTsKKwor CWJyY21uYW5kX3NldF9zZWN0b3Jfc2l6ZV8xayhob3N0LCBjZmctPnNlY3Rvcl9zaXplXzFrKTsK KworCS8qIHRocmVzaG9sZCA9IGNlaWwoQkNILWxldmVsICogMC43NSkgKi8KKwlicmNtbmFuZF93 cl9jb3JyX3RocmVzaChob3N0LCBESVZfUk9VTkRfVVAoY2hpcC0+ZWNjLnN0cmVuZ3RoICogMywg NCkpOworCisJcmV0dXJuIDA7Cit9CisKK3N0YXRpYyB2b2lkIGJyY21uYW5kX3ByaW50X2NmZyhj aGFyICpidWYsIHN0cnVjdCBicmNtbmFuZF9jZmcgKmNmZykKK3sKKwlidWYgKz0gc3ByaW50Zihi dWYsCisJCSIlbGx1TWlCIHRvdGFsLCAldUtpQiBibG9ja3MsICV1JXMgcGFnZXMsICV1QiBPT0Is ICV1LWJpdCIsCisJCSh1bnNpZ25lZCBsb25nIGxvbmcpY2ZnLT5kZXZpY2Vfc2l6ZSA+PiAyMCwK KwkJY2ZnLT5ibG9ja19zaXplID4+IDEwLAorCQljZmctPnBhZ2Vfc2l6ZSA+PSAxMDI0ID8gY2Zn LT5wYWdlX3NpemUgPj4gMTAgOiBjZmctPnBhZ2Vfc2l6ZSwKKwkJY2ZnLT5wYWdlX3NpemUgPj0g MTAyNCA/ICJLaUIiIDogIkIiLAorCQljZmctPnNwYXJlX2FyZWFfc2l6ZSwgY2ZnLT5kZXZpY2Vf d2lkdGgpOworCisJLyogQWNjb3VudCBmb3IgSGFtbWluZyBFQ0MgYW5kIGZvciBCQ0ggNTEyQiB2 cyAxS2lCIHNlY3RvcnMgKi8KKwlpZiAoaXNfaGFtbWluZ19lY2MoY2ZnKSkKKwkJc3ByaW50Zihi dWYsICIsIEhhbW1pbmcgRUNDIik7CisJZWxzZSBpZiAoY2ZnLT5zZWN0b3Jfc2l6ZV8xaykKKwkJ c3ByaW50ZihidWYsICIsIEJDSC0ldSAoMUtpQiBzZWN0b3IpIiwgY2ZnLT5lY2NfbGV2ZWwgPDwg MSk7CisJZWxzZQorCQlzcHJpbnRmKGJ1ZiwgIiwgQkNILSV1XG4iLCBjZmctPmVjY19sZXZlbCk7 Cit9CisKKy8qCisgKiBNaW5pbXVtIG51bWJlciBvZiBieXRlcyB0byBhZGRyZXNzIGEgcGFnZS4g Q2FsY3VsYXRlZCBhczoKKyAqICAgICByb3VuZHVwKGxvZzIoc2l6ZSAvIHBhZ2Utc2l6ZSkgLyA4 KQorICoKKyAqIE5COiB0aGUgZm9sbG93aW5nIGRvZXMgbm90ICJyb3VuZCB1cCIgZm9yIG5vbi1w b3dlci1vZi0yICdzaXplJzsgYnV0IHRoaXMgaXMKKyAqICAgICBPSyBiZWNhdXNlIG1hbnkgb3Ro ZXIgdGhpbmdzIHdpbGwgYnJlYWsgaWYgJ3NpemUnIGlzIGlycmVndWxhci4uLgorICovCitzdGF0 aWMgaW5saW5lIGludCBnZXRfYmxrX2Fkcl9ieXRlcyh1NjQgc2l6ZSwgdTMyIHdyaXRlc2l6ZSkK K3sKKwlyZXR1cm4gQUxJR04oaWxvZzIoc2l6ZSkgLSBpbG9nMih3cml0ZXNpemUpLCA4KSA+PiAz OworfQorCitzdGF0aWMgaW50IGJyY21uYW5kX3NldHVwX2RldihzdHJ1Y3QgYnJjbW5hbmRfaG9z dCAqaG9zdCkKK3sKKwlzdHJ1Y3QgbXRkX2luZm8gKm10ZCA9ICZob3N0LT5tdGQ7CisJc3RydWN0 IG5hbmRfY2hpcCAqY2hpcCA9ICZob3N0LT5jaGlwOworCXN0cnVjdCBicmNtbmFuZF9jb250cm9s bGVyICpjdHJsID0gaG9zdC0+Y3RybDsKKwlzdHJ1Y3QgYnJjbW5hbmRfY2ZnICpjZmcgPSAmaG9z dC0+aHdjZmc7CisJY2hhciBtc2dbMTI4XTsKKwl1MzIgb2ZmcywgdG1wLCBvb2Jfc2VjdG9yOwor CWludCByZXQ7CisKKwltZW1zZXQoY2ZnLCAwLCBzaXplb2YoKmNmZykpOworCisJcmV0ID0gb2Zf cHJvcGVydHlfcmVhZF91MzIoY2hpcC0+ZG4sICJicmNtLG5hbmQtb29iLXNlY3Rvci1zaXplIiwK KwkJCQkgICAmb29iX3NlY3Rvcik7CisJaWYgKHJldCkgeworCQkvKiBVc2UgZGV0ZWN0ZWQgc2l6 ZSAqLworCQljZmctPnNwYXJlX2FyZWFfc2l6ZSA9IG10ZC0+b29ic2l6ZSAvCisJCQkJCShtdGQt PndyaXRlc2l6ZSA+PiBGQ19TSElGVCk7CisJfSBlbHNlIHsKKwkJY2ZnLT5zcGFyZV9hcmVhX3Np emUgPSBvb2Jfc2VjdG9yOworCX0KKwlpZiAoY2ZnLT5zcGFyZV9hcmVhX3NpemUgPiBjdHJsLT5t YXhfb29iKQorCQljZmctPnNwYXJlX2FyZWFfc2l6ZSA9IGN0cmwtPm1heF9vb2I7CisJLyoKKwkg KiBTZXQgb29ic2l6ZSB0byBiZSBjb25zaXN0ZW50IHdpdGggY29udHJvbGxlcidzIHNwYXJlX2Fy ZWFfc2l6ZSwgYXMKKwkgKiB0aGUgcmVzdCBpcyBpbmFjY2Vzc2libGUuCisJICovCisJbXRkLT5v b2JzaXplID0gY2ZnLT5zcGFyZV9hcmVhX3NpemUgKiAobXRkLT53cml0ZXNpemUgPj4gRkNfU0hJ RlQpOworCisJY2ZnLT5kZXZpY2Vfc2l6ZSA9IG10ZC0+c2l6ZTsKKwljZmctPmJsb2NrX3NpemUg PSBtdGQtPmVyYXNlc2l6ZTsKKwljZmctPnBhZ2Vfc2l6ZSA9IG10ZC0+d3JpdGVzaXplOworCWNm Zy0+ZGV2aWNlX3dpZHRoID0gKGNoaXAtPm9wdGlvbnMgJiBOQU5EX0JVU1dJRFRIXzE2KSA/IDE2 IDogODsKKwljZmctPmNvbF9hZHJfYnl0ZXMgPSAyOworCWNmZy0+YmxrX2Fkcl9ieXRlcyA9IGdl dF9ibGtfYWRyX2J5dGVzKG10ZC0+c2l6ZSwgbXRkLT53cml0ZXNpemUpOworCisJc3dpdGNoIChj aGlwLT5lY2Muc2l6ZSkgeworCWNhc2UgNTEyOgorCQlpZiAoY2hpcC0+ZWNjLnN0cmVuZ3RoID09 IDEpIC8qIEhhbW1pbmcgKi8KKwkJCWNmZy0+ZWNjX2xldmVsID0gMTU7CisJCWVsc2UKKwkJCWNm Zy0+ZWNjX2xldmVsID0gY2hpcC0+ZWNjLnN0cmVuZ3RoOworCQljZmctPnNlY3Rvcl9zaXplXzFr ID0gMDsKKwkJYnJlYWs7CisJY2FzZSAxMDI0OgorCQlpZiAoIShjdHJsLT5mZWF0dXJlcyAmIEJS Q01OQU5EX0hBU18xS19TRUNUT1JTKSkgeworCQkJZGV2X2VycihjdHJsLT5kZXYsICIxS0Igc2Vj dG9ycyBub3Qgc3VwcG9ydGVkXG4iKTsKKwkJCXJldHVybiAtRUlOVkFMOworCQl9CisJCWlmIChj aGlwLT5lY2Muc3RyZW5ndGggJiAweDEpIHsKKwkJCWRldl9lcnIoY3RybC0+ZGV2LAorCQkJCSJv ZGQgRUNDIG5vdCBzdXBwb3J0ZWQgd2l0aCAxS0Igc2VjdG9yc1xuIik7CisJCQlyZXR1cm4gLUVJ TlZBTDsKKwkJfQorCisJCWNmZy0+ZWNjX2xldmVsID0gY2hpcC0+ZWNjLnN0cmVuZ3RoID4+IDE7 CisJCWNmZy0+c2VjdG9yX3NpemVfMWsgPSAxOworCQlicmVhazsKKwlkZWZhdWx0OgorCQlkZXZf ZXJyKGN0cmwtPmRldiwgInVuc3VwcG9ydGVkIEVDQyBzaXplOiAlZFxuIiwKKwkJCWNoaXAtPmVj Yy5zaXplKTsKKwkJcmV0dXJuIC1FSU5WQUw7CisJfQorCisJY2ZnLT5mdWxfYWRyX2J5dGVzID0g Y2ZnLT5ibGtfYWRyX2J5dGVzOworCWlmIChtdGQtPndyaXRlc2l6ZSA+IDUxMikKKwkJY2ZnLT5m dWxfYWRyX2J5dGVzICs9IGNmZy0+Y29sX2Fkcl9ieXRlczsKKwllbHNlCisJCWNmZy0+ZnVsX2Fk cl9ieXRlcyArPSAxOworCisJcmV0ID0gYnJjbW5hbmRfc2V0X2NmZyhob3N0LCBjZmcpOworCWlm IChyZXQpCisJCXJldHVybiByZXQ7CisKKwlicmNtbmFuZF9zZXRfZWNjX2VuYWJsZWQoaG9zdCwg MSk7CisKKwlicmNtbmFuZF9wcmludF9jZmcobXNnLCBjZmcpOworCWRldl9pbmZvKGN0cmwtPmRl diwgImRldGVjdGVkICVzXG4iLCBtc2cpOworCisJLyogQ29uZmlndXJlIEFDQ19DT05UUk9MICov CisJb2ZmcyA9IGJyY21uYW5kX2NzX29mZnNldChjdHJsLCBob3N0LT5jcywgQlJDTU5BTkRfQ1Nf QUNDX0NPTlRST0wpOworCXRtcCA9IG5hbmRfcmVhZHJlZyhjdHJsLCBvZmZzKTsKKwl0bXAgJj0g fkFDQ19DT05UUk9MX1BBUlRJQUxfUEFHRTsKKwl0bXAgJj0gfkFDQ19DT05UUk9MX1JEX0VSQVNF RDsKKwl0bXAgJj0gfkFDQ19DT05UUk9MX0ZBU1RfUEdNX1JESU47CisJaWYgKGN0cmwtPmZlYXR1 cmVzICYgQlJDTU5BTkRfSEFTX1BSRUZFVENIKSB7CisJCS8qCisJCSAqIEZJWE1FOiBGbGFzaCBE TUEgKyBwcmVmZXRjaCBtYXkgc2VlIHNwdXJpb3VzIGVyYXNlZC1wYWdlIEVDQworCQkgKiBlcnJv cnMKKwkJICovCisJCWlmIChoYXNfZmxhc2hfZG1hKGN0cmwpKQorCQkJdG1wICY9IH5BQ0NfQ09O VFJPTF9QUkVGRVRDSDsKKwkJZWxzZQorCQkJdG1wIHw9IEFDQ19DT05UUk9MX1BSRUZFVENIOwor CX0KKwluYW5kX3dyaXRlcmVnKGN0cmwsIG9mZnMsIHRtcCk7CisKKwlyZXR1cm4gMDsKK30KKwor c3RhdGljIGludCBicmNtbmFuZF9pbml0X2NzKHN0cnVjdCBicmNtbmFuZF9ob3N0ICpob3N0KQor eworCXN0cnVjdCBicmNtbmFuZF9jb250cm9sbGVyICpjdHJsID0gaG9zdC0+Y3RybDsKKwlzdHJ1 Y3QgZGV2aWNlX25vZGUgKmRuID0gaG9zdC0+b2Zfbm9kZTsKKwlzdHJ1Y3QgcGxhdGZvcm1fZGV2 aWNlICpwZGV2ID0gaG9zdC0+cGRldjsKKwlzdHJ1Y3QgbXRkX2luZm8gKm10ZDsKKwlzdHJ1Y3Qg bmFuZF9jaGlwICpjaGlwOworCWludCByZXQgPSAwOworCXN0cnVjdCBtdGRfcGFydF9wYXJzZXJf ZGF0YSBwcGRhdGEgPSB7IC5vZl9ub2RlID0gZG4gfTsKKworCXJldCA9IG9mX3Byb3BlcnR5X3Jl YWRfdTMyKGRuLCAicmVnIiwgJmhvc3QtPmNzKTsKKwlpZiAocmV0KSB7CisJCWRldl9lcnIoJnBk ZXYtPmRldiwgImNhbid0IGdldCBjaGlwLXNlbGVjdFxuIik7CisJCXJldHVybiAtRU5YSU87CisJ fQorCisJbXRkID0gJmhvc3QtPm10ZDsKKwljaGlwID0gJmhvc3QtPmNoaXA7CisKKwljaGlwLT5k biA9IGRuOworCWNoaXAtPnByaXYgPSBob3N0OworCW10ZC0+cHJpdiA9IGNoaXA7CisJbXRkLT5u YW1lID0gZGV2bV9rYXNwcmludGYoJnBkZXYtPmRldiwgR0ZQX0tFUk5FTCwgImJyY21uYW5kLiVk IiwKKwkJCQkgICBob3N0LT5jcyk7CisJbXRkLT5vd25lciA9IFRISVNfTU9EVUxFOworCW10ZC0+ ZGV2LnBhcmVudCA9ICZwZGV2LT5kZXY7CisKKwljaGlwLT5JT19BRERSX1IgPSAodm9pZCBfX2lv bWVtICopMHhkZWFkYmVlZjsKKwljaGlwLT5JT19BRERSX1cgPSAodm9pZCBfX2lvbWVtICopMHhk ZWFkYmVlZjsKKworCWNoaXAtPmNtZF9jdHJsID0gYnJjbW5hbmRfY21kX2N0cmw7CisJY2hpcC0+ Y21kZnVuYyA9IGJyY21uYW5kX2NtZGZ1bmM7CisJY2hpcC0+d2FpdGZ1bmMgPSBicmNtbmFuZF93 YWl0ZnVuYzsKKwljaGlwLT5yZWFkX2J5dGUgPSBicmNtbmFuZF9yZWFkX2J5dGU7CisJY2hpcC0+ cmVhZF9idWYgPSBicmNtbmFuZF9yZWFkX2J1ZjsKKwljaGlwLT53cml0ZV9idWYgPSBicmNtbmFu ZF93cml0ZV9idWY7CisKKwljaGlwLT5lY2MubW9kZSA9IE5BTkRfRUNDX0hXOworCWNoaXAtPmVj Yy5yZWFkX3BhZ2UgPSBicmNtbmFuZF9yZWFkX3BhZ2U7CisJY2hpcC0+ZWNjLnJlYWRfc3VicGFn ZSA9IGJyY21uYW5kX3JlYWRfc3VicGFnZTsKKwljaGlwLT5lY2Mud3JpdGVfcGFnZSA9IGJyY21u YW5kX3dyaXRlX3BhZ2U7CisJY2hpcC0+ZWNjLnJlYWRfcGFnZV9yYXcgPSBicmNtbmFuZF9yZWFk X3BhZ2VfcmF3OworCWNoaXAtPmVjYy53cml0ZV9wYWdlX3JhdyA9IGJyY21uYW5kX3dyaXRlX3Bh Z2VfcmF3OworCWNoaXAtPmVjYy53cml0ZV9vb2JfcmF3ID0gYnJjbW5hbmRfd3JpdGVfb29iX3Jh dzsKKwljaGlwLT5lY2MucmVhZF9vb2JfcmF3ID0gYnJjbW5hbmRfcmVhZF9vb2JfcmF3OworCWNo aXAtPmVjYy5yZWFkX29vYiA9IGJyY21uYW5kX3JlYWRfb29iOworCWNoaXAtPmVjYy53cml0ZV9v b2IgPSBicmNtbmFuZF93cml0ZV9vb2I7CisKKwljaGlwLT5jb250cm9sbGVyID0gJmN0cmwtPmNv bnRyb2xsZXI7CisKKwlpZiAobmFuZF9zY2FuX2lkZW50KG10ZCwgMSwgTlVMTCkpCisJCXJldHVy biAtRU5YSU87CisKKwljaGlwLT5vcHRpb25zIHw9IE5BTkRfTk9fU1VCUEFHRV9XUklURTsKKwkv KgorCSAqIEF2b2lkIChmb3IgaW5zdGFuY2UpIGttYXAoKSdkIGJ1ZmZlcnMgZnJvbSBKRkZTMiwg d2hpY2ggd2UgY2FuJ3QgRE1BCisJICogdG8vZnJvbSwgYW5kIGhhdmUgbmFuZF9iYXNlIHBhc3Mg dXMgYSBib3VuY2UgYnVmZmVyIGluc3RlYWQsIGFzCisJICogbmVlZGVkLgorCSAqLworCWNoaXAt Pm9wdGlvbnMgfD0gTkFORF9VU0VfQk9VTkNFX0JVRkZFUjsKKworCWlmIChvZl9nZXRfbmFuZF9v bl9mbGFzaF9iYnQoZG4pKQorCQljaGlwLT5iYnRfb3B0aW9ucyB8PSBOQU5EX0JCVF9VU0VfRkxB U0ggfCBOQU5EX0JCVF9OT19PT0I7CisKKwlpZiAoYnJjbW5hbmRfc2V0dXBfZGV2KGhvc3QpKQor CQlyZXR1cm4gLUVOWElPOworCisJY2hpcC0+ZWNjLnNpemUgPSBob3N0LT5od2NmZy5zZWN0b3Jf c2l6ZV8xayA/IDEwMjQgOiA1MTI7CisJLyogb25seSB1c2Ugb3VyIGludGVybmFsIEhXIHRocmVz aG9sZCAqLworCW10ZC0+Yml0ZmxpcF90aHJlc2hvbGQgPSAxOworCisJY2hpcC0+ZWNjLmxheW91 dCA9IGJyY21zdGJfY2hvb3NlX2VjY19sYXlvdXQoaG9zdCk7CisJaWYgKCFjaGlwLT5lY2MubGF5 b3V0KQorCQlyZXR1cm4gLUVOWElPOworCisJaWYgKG5hbmRfc2Nhbl90YWlsKG10ZCkpCisJCXJl dHVybiAtRU5YSU87CisKKwlyZXR1cm4gbXRkX2RldmljZV9wYXJzZV9yZWdpc3RlcihtdGQsIE5V TEwsICZwcGRhdGEsIE5VTEwsIDApOworfQorCitzdGF0aWMgdm9pZCBicmNtbmFuZF9zYXZlX3Jl c3RvcmVfY3NfY29uZmlnKHN0cnVjdCBicmNtbmFuZF9ob3N0ICpob3N0LAorCQkJCQkgICAgaW50 IHJlc3RvcmUpCit7CisJc3RydWN0IGJyY21uYW5kX2NvbnRyb2xsZXIgKmN0cmwgPSBob3N0LT5j dHJsOworCXUxNiBjZmdfb2ZmcyA9IGJyY21uYW5kX2NzX29mZnNldChjdHJsLCBob3N0LT5jcywg QlJDTU5BTkRfQ1NfQ0ZHKTsKKwl1MTYgY2ZnX2V4dF9vZmZzID0gYnJjbW5hbmRfY3Nfb2Zmc2V0 KGN0cmwsIGhvc3QtPmNzLAorCQkJQlJDTU5BTkRfQ1NfQ0ZHX0VYVCk7CisJdTE2IGFjY19jb250 cm9sX29mZnMgPSBicmNtbmFuZF9jc19vZmZzZXQoY3RybCwgaG9zdC0+Y3MsCisJCQlCUkNNTkFO RF9DU19BQ0NfQ09OVFJPTCk7CisJdTE2IHQxX29mZnMgPSBicmNtbmFuZF9jc19vZmZzZXQoY3Ry bCwgaG9zdC0+Y3MsIEJSQ01OQU5EX0NTX1RJTUlORzEpOworCXUxNiB0Ml9vZmZzID0gYnJjbW5h bmRfY3Nfb2Zmc2V0KGN0cmwsIGhvc3QtPmNzLCBCUkNNTkFORF9DU19USU1JTkcyKTsKKworCWlm IChyZXN0b3JlKSB7CisJCW5hbmRfd3JpdGVyZWcoY3RybCwgY2ZnX29mZnMsIGhvc3QtPmh3Y2Zn LmNvbmZpZyk7CisJCWlmIChjZmdfb2ZmcyAhPSBjZmdfZXh0X29mZnMpCisJCQluYW5kX3dyaXRl cmVnKGN0cmwsIGNmZ19leHRfb2ZmcywKKwkJCQkgICAgICBob3N0LT5od2NmZy5jb25maWdfZXh0 KTsKKwkJbmFuZF93cml0ZXJlZyhjdHJsLCBhY2NfY29udHJvbF9vZmZzLCBob3N0LT5od2NmZy5h Y2NfY29udHJvbCk7CisJCW5hbmRfd3JpdGVyZWcoY3RybCwgdDFfb2ZmcywgaG9zdC0+aHdjZmcu dGltaW5nXzEpOworCQluYW5kX3dyaXRlcmVnKGN0cmwsIHQyX29mZnMsIGhvc3QtPmh3Y2ZnLnRp bWluZ18yKTsKKwl9IGVsc2UgeworCQlob3N0LT5od2NmZy5jb25maWcgPSBuYW5kX3JlYWRyZWco Y3RybCwgY2ZnX29mZnMpOworCQlpZiAoY2ZnX29mZnMgIT0gY2ZnX2V4dF9vZmZzKQorCQkJaG9z dC0+aHdjZmcuY29uZmlnX2V4dCA9CisJCQkJbmFuZF9yZWFkcmVnKGN0cmwsIGNmZ19leHRfb2Zm cyk7CisJCWhvc3QtPmh3Y2ZnLmFjY19jb250cm9sID0gbmFuZF9yZWFkcmVnKGN0cmwsIGFjY19j b250cm9sX29mZnMpOworCQlob3N0LT5od2NmZy50aW1pbmdfMSA9IG5hbmRfcmVhZHJlZyhjdHJs LCB0MV9vZmZzKTsKKwkJaG9zdC0+aHdjZmcudGltaW5nXzIgPSBuYW5kX3JlYWRyZWcoY3RybCwg dDJfb2Zmcyk7CisJfQorfQorCitzdGF0aWMgaW50IGJyY21uYW5kX3N1c3BlbmQoc3RydWN0IGRl dmljZSAqZGV2KQoreworCXN0cnVjdCBicmNtbmFuZF9jb250cm9sbGVyICpjdHJsID0gZGV2X2dl dF9kcnZkYXRhKGRldik7CisJc3RydWN0IGJyY21uYW5kX2hvc3QgKmhvc3Q7CisKKwlsaXN0X2Zv cl9lYWNoX2VudHJ5KGhvc3QsICZjdHJsLT5ob3N0X2xpc3QsIG5vZGUpCisJCWJyY21uYW5kX3Nh dmVfcmVzdG9yZV9jc19jb25maWcoaG9zdCwgMCk7CisKKwljdHJsLT5uYW5kX2NzX25hbmRfc2Vs ZWN0ID0gYnJjbW5hbmRfcmVhZF9yZWcoY3RybCwgQlJDTU5BTkRfQ1NfU0VMRUNUKTsKKwljdHJs LT5uYW5kX2NzX25hbmRfeG9yID0gYnJjbW5hbmRfcmVhZF9yZWcoY3RybCwgQlJDTU5BTkRfQ1Nf WE9SKTsKKwljdHJsLT5jb3JyX3N0YXRfdGhyZXNob2xkID0KKwkJYnJjbW5hbmRfcmVhZF9yZWco Y3RybCwgQlJDTU5BTkRfQ09SUl9USFJFU0hPTEQpOworCisJaWYgKGhhc19mbGFzaF9kbWEoY3Ry bCkpCisJCWN0cmwtPmZsYXNoX2RtYV9tb2RlID0gZmxhc2hfZG1hX3JlYWRsKGN0cmwsIEZMQVNI X0RNQV9NT0RFKTsKKworCXJldHVybiAwOworfQorCitzdGF0aWMgaW50IGJyY21uYW5kX3Jlc3Vt ZShzdHJ1Y3QgZGV2aWNlICpkZXYpCit7CisJc3RydWN0IGJyY21uYW5kX2NvbnRyb2xsZXIgKmN0 cmwgPSBkZXZfZ2V0X2RydmRhdGEoZGV2KTsKKwlzdHJ1Y3QgYnJjbW5hbmRfaG9zdCAqaG9zdDsK KworCWlmIChoYXNfZmxhc2hfZG1hKGN0cmwpKSB7CisJCWZsYXNoX2RtYV93cml0ZWwoY3RybCwg RkxBU0hfRE1BX01PREUsIGN0cmwtPmZsYXNoX2RtYV9tb2RlKTsKKwkJZmxhc2hfZG1hX3dyaXRl bChjdHJsLCBGTEFTSF9ETUFfRVJST1JfU1RBVFVTLCAwKTsKKwl9CisKKwlicmNtbmFuZF93cml0 ZV9yZWcoY3RybCwgQlJDTU5BTkRfQ1NfU0VMRUNULCBjdHJsLT5uYW5kX2NzX25hbmRfc2VsZWN0 KTsKKwlicmNtbmFuZF93cml0ZV9yZWcoY3RybCwgQlJDTU5BTkRfQ1NfWE9SLCBjdHJsLT5uYW5k X2NzX25hbmRfeG9yKTsKKwlicmNtbmFuZF93cml0ZV9yZWcoY3RybCwgQlJDTU5BTkRfQ09SUl9U SFJFU0hPTEQsCisJCQljdHJsLT5jb3JyX3N0YXRfdGhyZXNob2xkKTsKKworCWxpc3RfZm9yX2Vh Y2hfZW50cnkoaG9zdCwgJmN0cmwtPmhvc3RfbGlzdCwgbm9kZSkgeworCQlzdHJ1Y3QgbXRkX2lu Zm8gKm10ZCA9ICZob3N0LT5tdGQ7CisJCXN0cnVjdCBuYW5kX2NoaXAgKmNoaXAgPSBtdGQtPnBy aXY7CisKKwkJYnJjbW5hbmRfc2F2ZV9yZXN0b3JlX2NzX2NvbmZpZyhob3N0LCAxKTsKKworCQkv KiBSZXNldCB0aGUgY2hpcCwgcmVxdWlyZWQgYnkgc29tZSBjaGlwcyBhZnRlciBwb3dlci11cCAq LworCQljaGlwLT5jbWRmdW5jKG10ZCwgTkFORF9DTURfUkVTRVQsIC0xLCAtMSk7CisJfQorCisJ cmV0dXJuIDA7Cit9CisKK3N0YXRpYyBjb25zdCBzdHJ1Y3QgZGV2X3BtX29wcyBicmNtbmFuZF9w bV9vcHMgPSB7CisJLnN1c3BlbmQJCT0gYnJjbW5hbmRfc3VzcGVuZCwKKwkucmVzdW1lCQkJPSBi cmNtbmFuZF9yZXN1bWUsCit9OworCisvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioq KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioKKyAqIFBsYXRmb3JtIGRyaXZl ciBzZXR1cCAocGVyIGNvbnRyb2xsZXIpCisgKioqKioqKioqKioqKioqKioqKioqKioqKioqKioq KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovCisKK3N0YXRpYyBpbnQg YnJjbW5hbmRfcHJvYmUoc3RydWN0IHBsYXRmb3JtX2RldmljZSAqcGRldikKK3sKKwlzdHJ1Y3Qg ZGV2aWNlICpkZXYgPSAmcGRldi0+ZGV2OworCXN0cnVjdCBkZXZpY2Vfbm9kZSAqZG4gPSBkZXYt Pm9mX25vZGUsICpjaGlsZDsKKwlzdGF0aWMgc3RydWN0IGJyY21uYW5kX2NvbnRyb2xsZXIgKmN0 cmw7CisJc3RydWN0IHJlc291cmNlICpyZXM7CisJaW50IHJldDsKKworCS8qIFdlIG9ubHkgc3Vw cG9ydCBkZXZpY2UtdHJlZSBpbnN0YW50aWF0aW9uICovCisJaWYgKCFkbikKKwkJcmV0dXJuIC1F Tk9ERVY7CisKKwljdHJsID0gZGV2bV9remFsbG9jKGRldiwgc2l6ZW9mKCpjdHJsKSwgR0ZQX0tF Uk5FTCk7CisJaWYgKCFjdHJsKQorCQlyZXR1cm4gLUVOT01FTTsKKworCWRldl9zZXRfZHJ2ZGF0 YShkZXYsIGN0cmwpOworCWN0cmwtPmRldiA9IGRldjsKKworCWluaXRfY29tcGxldGlvbigmY3Ry bC0+ZG9uZSk7CisJaW5pdF9jb21wbGV0aW9uKCZjdHJsLT5kbWFfZG9uZSk7CisJc3Bpbl9sb2Nr X2luaXQoJmN0cmwtPmNvbnRyb2xsZXIubG9jayk7CisJaW5pdF93YWl0cXVldWVfaGVhZCgmY3Ry bC0+Y29udHJvbGxlci53cSk7CisJSU5JVF9MSVNUX0hFQUQoJmN0cmwtPmhvc3RfbGlzdCk7CisK KwkvKiBOQU5EIHJlZ2lzdGVyIHJhbmdlICovCisJcmVzID0gcGxhdGZvcm1fZ2V0X3Jlc291cmNl KHBkZXYsIElPUkVTT1VSQ0VfTUVNLCAwKTsKKwljdHJsLT5uYW5kX2Jhc2UgPSBkZXZtX2lvcmVt YXBfcmVzb3VyY2UoZGV2LCByZXMpOworCWlmIChJU19FUlIoY3RybC0+bmFuZF9iYXNlKSkKKwkJ cmV0dXJuIFBUUl9FUlIoY3RybC0+bmFuZF9iYXNlKTsKKworCS8qIEluaXRpYWxpemUgTkFORCBy ZXZpc2lvbiAqLworCXJldCA9IGJyY21uYW5kX3JldmlzaW9uX2luaXQoY3RybCk7CisJaWYgKHJl dCkKKwkJcmV0dXJuIHJldDsKKworCS8qCisJICogTW9zdCBjaGlwcyBoYXZlIHRoaXMgY2FjaGUg YXQgYSBmaXhlZCBvZmZzZXQgd2l0aGluICduYW5kJyBibG9jay4KKwkgKiBTb21lIG11c3Qgc3Bl Y2lmeSB0aGlzIHJlZ2lvbiBzZXBhcmF0ZWx5LgorCSAqLworCXJlcyA9IHBsYXRmb3JtX2dldF9y ZXNvdXJjZV9ieW5hbWUocGRldiwgSU9SRVNPVVJDRV9NRU0sICJuYW5kLWNhY2hlIik7CisJaWYg KHJlcykgeworCQljdHJsLT5uYW5kX2ZjID0gZGV2bV9pb3JlbWFwX3Jlc291cmNlKGRldiwgcmVz KTsKKwkJaWYgKElTX0VSUihjdHJsLT5uYW5kX2ZjKSkKKwkJCXJldHVybiBQVFJfRVJSKGN0cmwt Pm5hbmRfZmMpOworCX0gZWxzZSB7CisJCWN0cmwtPm5hbmRfZmMgPSBjdHJsLT5uYW5kX2Jhc2Ug KworCQkJCWN0cmwtPnJlZ19vZmZzZXRzW0JSQ01OQU5EX0ZDX0JBU0VdOworCX0KKworCS8qIEZM QVNIX0RNQSAqLworCXJlcyA9IHBsYXRmb3JtX2dldF9yZXNvdXJjZV9ieW5hbWUocGRldiwgSU9S RVNPVVJDRV9NRU0sICJmbGFzaC1kbWEiKTsKKwlpZiAocmVzKSB7CisJCWN0cmwtPmZsYXNoX2Rt YV9iYXNlID0gZGV2bV9pb3JlbWFwX3Jlc291cmNlKGRldiwgcmVzKTsKKwkJaWYgKElTX0VSUihj dHJsLT5mbGFzaF9kbWFfYmFzZSkpCisJCQlyZXR1cm4gUFRSX0VSUihjdHJsLT5mbGFzaF9kbWFf YmFzZSk7CisKKwkJZmxhc2hfZG1hX3dyaXRlbChjdHJsLCBGTEFTSF9ETUFfTU9ERSwgMSk7IC8q IGxpbmtlZC1saXN0ICovCisJCWZsYXNoX2RtYV93cml0ZWwoY3RybCwgRkxBU0hfRE1BX0VSUk9S X1NUQVRVUywgMCk7CisKKwkJLyogQWxsb2NhdGUgZGVzY3JpcHRvcihzKSAqLworCQljdHJsLT5k bWFfZGVzYyA9IGRtYW1fYWxsb2NfY29oZXJlbnQoZGV2LAorCQkJCQkJICAgICBzaXplb2YoKmN0 cmwtPmRtYV9kZXNjKSwKKwkJCQkJCSAgICAgJmN0cmwtPmRtYV9wYSwgR0ZQX0tFUk5FTCk7CisJ CWlmICghY3RybC0+ZG1hX2Rlc2MpCisJCQlyZXR1cm4gLUVOT01FTTsKKworCQljdHJsLT5kbWFf aXJxID0gcGxhdGZvcm1fZ2V0X2lycShwZGV2LCAxKTsKKwkJaWYgKChpbnQpY3RybC0+ZG1hX2ly cSA8IDApIHsKKwkJCWRldl9lcnIoZGV2LCAibWlzc2luZyBGTEFTSF9ETUEgSVJRXG4iKTsKKwkJ CXJldHVybiAtRU5PREVWOworCQl9CisKKwkJcmV0ID0gZGV2bV9yZXF1ZXN0X2lycShkZXYsIGN0 cmwtPmRtYV9pcnEsCisJCQkJYnJjbW5hbmRfZG1hX2lycSwgMCwgRFJWX05BTUUsCisJCQkJY3Ry bCk7CisJCWlmIChyZXQgPCAwKSB7CisJCQlkZXZfZXJyKGRldiwgImNhbid0IGFsbG9jYXRlIElS USAlZDogZXJyb3IgJWRcbiIsCisJCQkJCWN0cmwtPmRtYV9pcnEsIHJldCk7CisJCQlyZXR1cm4g cmV0OworCQl9CisKKwkJZGV2X2luZm8oZGV2LCAiZW5hYmxpbmcgRkxBU0hfRE1BXG4iKTsKKwl9 CisKKwkvKiBEaXNhYmxlIGF1dG9tYXRpYyBkZXZpY2UgSUQgY29uZmlnLCBkaXJlY3QgYWRkcmVz c2luZyAqLworCWJyY21uYW5kX3Jtd19yZWcoY3RybCwgQlJDTU5BTkRfQ1NfU0VMRUNULAorCQkJ IENTX1NFTEVDVF9BVVRPX0RFVklDRV9JRF9DRkcgfCAweGZmLCAwLCAwKTsKKwkvKiBEaXNhYmxl IFhPUiBhZGRyZXNzaW5nICovCisJYnJjbW5hbmRfcm13X3JlZyhjdHJsLCBCUkNNTkFORF9DU19Y T1IsIDB4ZmYsIDAsIDApOworCisJaWYgKGN0cmwtPmZlYXR1cmVzICYgQlJDTU5BTkRfSEFTX1dQ KSB7CisJCS8qIFBlcm1hbmVudGx5IGRpc2FibGUgd3JpdGUgcHJvdGVjdGlvbiAqLworCQlpZiAo d3Bfb24gPT0gMikKKwkJCWJyY21uYW5kX3NldF93cChjdHJsLCBmYWxzZSk7CisJfSBlbHNlIHsK KwkJd3Bfb24gPSAwOworCX0KKworCS8qIElSUSAqLworCWN0cmwtPmlycSA9IHBsYXRmb3JtX2dl dF9pcnEocGRldiwgMCk7CisJaWYgKChpbnQpY3RybC0+aXJxIDwgMCkgeworCQlkZXZfZXJyKGRl diwgIm5vIElSUSBkZWZpbmVkXG4iKTsKKwkJcmV0dXJuIC1FTk9ERVY7CisJfQorCisJcmV0ID0g ZGV2bV9yZXF1ZXN0X2lycShkZXYsIGN0cmwtPmlycSwgYnJjbW5hbmRfY3RscmR5X2lycSwgMCwK KwkJCURSVl9OQU1FLCBjdHJsKTsKKwlpZiAocmV0IDwgMCkgeworCQlkZXZfZXJyKGRldiwgImNh bid0IGFsbG9jYXRlIElSUSAlZDogZXJyb3IgJWRcbiIsCisJCQljdHJsLT5pcnEsIHJldCk7CisJ CXJldHVybiByZXQ7CisJfQorCisJZm9yX2VhY2hfYXZhaWxhYmxlX2NoaWxkX29mX25vZGUoZG4s IGNoaWxkKSB7CisJCWlmIChvZl9kZXZpY2VfaXNfY29tcGF0aWJsZShjaGlsZCwgImJyY20sbmFu ZGNzIikpIHsKKwkJCXN0cnVjdCBicmNtbmFuZF9ob3N0ICpob3N0OworCisJCQlob3N0ID0gZGV2 bV9remFsbG9jKGRldiwgc2l6ZW9mKCpob3N0KSwgR0ZQX0tFUk5FTCk7CisJCQlpZiAoIWhvc3Qp CisJCQkJcmV0dXJuIC1FTk9NRU07CisJCQlob3N0LT5wZGV2ID0gcGRldjsKKwkJCWhvc3QtPmN0 cmwgPSBjdHJsOworCQkJaG9zdC0+b2Zfbm9kZSA9IGNoaWxkOworCisJCQlyZXQgPSBicmNtbmFu ZF9pbml0X2NzKGhvc3QpOworCQkJaWYgKHJldCkKKwkJCQljb250aW51ZTsgLyogVHJ5IGFsbCBj aGlwLXNlbGVjdHMgKi8KKworCQkJbGlzdF9hZGRfdGFpbCgmaG9zdC0+bm9kZSwgJmN0cmwtPmhv c3RfbGlzdCk7CisJCX0KKwl9CisKKwkvKiBObyBjaGlwLXNlbGVjdHMgY291bGQgaW5pdGlhbGl6 ZSBwcm9wZXJseSAqLworCWlmIChsaXN0X2VtcHR5KCZjdHJsLT5ob3N0X2xpc3QpKQorCQlyZXR1 cm4gLUVOT0RFVjsKKworCXJldHVybiAwOworfQorCitzdGF0aWMgaW50IGJyY21uYW5kX3JlbW92 ZShzdHJ1Y3QgcGxhdGZvcm1fZGV2aWNlICpwZGV2KQoreworCXN0cnVjdCBicmNtbmFuZF9jb250 cm9sbGVyICpjdHJsID0gZGV2X2dldF9kcnZkYXRhKCZwZGV2LT5kZXYpOworCXN0cnVjdCBicmNt bmFuZF9ob3N0ICpob3N0OworCisJbGlzdF9mb3JfZWFjaF9lbnRyeShob3N0LCAmY3RybC0+aG9z dF9saXN0LCBub2RlKQorCQluYW5kX3JlbGVhc2UoJmhvc3QtPm10ZCk7CisKKwlkZXZfc2V0X2Ry dmRhdGEoJnBkZXYtPmRldiwgTlVMTCk7CisKKwlyZXR1cm4gMDsKK30KKworc3RhdGljIGNvbnN0 IHN0cnVjdCBvZl9kZXZpY2VfaWQgYnJjbW5hbmRfb2ZfbWF0Y2hbXSA9IHsKKwl7IC5jb21wYXRp YmxlID0gImJyY20sYnJjbW5hbmQtdjQuMCIgfSwKKwl7IC5jb21wYXRpYmxlID0gImJyY20sYnJj bW5hbmQtdjUuMCIgfSwKKwl7IC5jb21wYXRpYmxlID0gImJyY20sYnJjbW5hbmQtdjYuMCIgfSwK Kwl7IC5jb21wYXRpYmxlID0gImJyY20sYnJjbW5hbmQtdjYuMSIgfSwKKwl7IC5jb21wYXRpYmxl ID0gImJyY20sYnJjbW5hbmQtdjcuMCIgfSwKKwl7IC5jb21wYXRpYmxlID0gImJyY20sYnJjbW5h bmQtdjcuMSIgfSwKKwl7fSwKK307CitNT0RVTEVfREVWSUNFX1RBQkxFKG9mLCBicmNtbmFuZF9v Zl9tYXRjaCk7CisKK3N0YXRpYyBzdHJ1Y3QgcGxhdGZvcm1fZHJpdmVyIGJyY21zdGJfbmFuZF9k cml2ZXIgPSB7CisJLnByb2JlCQkJPSBicmNtbmFuZF9wcm9iZSwKKwkucmVtb3ZlCQkJPSBicmNt bmFuZF9yZW1vdmUsCisJLmRyaXZlciA9IHsKKwkJLm5hbWUJCT0gRFJWX05BTUUsCisJCS5wbQkJ PSAmYnJjbW5hbmRfcG1fb3BzLAorCQkub2ZfbWF0Y2hfdGFibGUgPSBvZl9tYXRjaF9wdHIoYnJj bW5hbmRfb2ZfbWF0Y2gpLAorCX0KK307Cittb2R1bGVfcGxhdGZvcm1fZHJpdmVyKGJyY21zdGJf bmFuZF9kcml2ZXIpOworCitNT0RVTEVfTElDRU5TRSgiR1BMIHYyIik7CitNT0RVTEVfQVVUSE9S KCJLZXZpbiBDZXJuZWtlZSIpOworTU9EVUxFX0FVVEhPUigiQnJpYW4gTm9ycmlzIik7CitNT0RV TEVfREVTQ1JJUFRJT04oIk5BTkQgZHJpdmVyIGZvciBCcm9hZGNvbSBTVEIgY2hpcHMiKTsKK01P RFVMRV9BTElBUygicGxhdGZvcm06YnJjbW5hbmQiKTsKLS0gCjEuOS4xCgoKX19fX19fX19fX19f X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fCkxpbnV4IE1URCBkaXNj dXNzaW9uIG1haWxpbmcgbGlzdApodHRwOi8vbGlzdHMuaW5mcmFkZWFkLm9yZy9tYWlsbWFuL2xp c3RpbmZvL2xpbnV4LW10ZC8K From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752966AbbEFSD0 (ORCPT ); Wed, 6 May 2015 14:03:26 -0400 Received: from mail-pa0-f51.google.com ([209.85.220.51]:36127 "EHLO mail-pa0-f51.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750995AbbEFSBD (ORCPT ); Wed, 6 May 2015 14:01:03 -0400 From: Brian Norris To: Cc: Brian Norris , Dmitry Torokhov , Anatol Pomazao , Ray Jui , Corneliu Doban , Jonathan Richardson , Scott Branden , Florian Fainelli , =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= , bcm-kernel-feedback-list@broadcom.com, Dan Ehrenberg , Gregory Fong , , , Kevin Cernekee Subject: [PATCH v3 03/10] mtd: nand: add NAND driver for Broadcom STB NAND controller Date: Wed, 6 May 2015 10:59:47 -0700 Message-Id: <1430935194-7579-4-git-send-email-computersforpeace@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1430935194-7579-1-git-send-email-computersforpeace@gmail.com> References: <1430935194-7579-1-git-send-email-computersforpeace@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This core originated in Set-Top Box chips (BCM7xxx) but is used in a variety of other Broadcom chips, including some BCM63xxx, BCM33xx, and iProc/Cygnus. It's been used only on ARM and MIPS SoCs, so restrict it to those architectures. There are multiple revisions of this core throughout the years, and almost every version broke register compatibility in some small way, but with some effort, this driver is able to support v4.0, v5.0, v6.x, v7.0, and v7.1. It's been tested on v5.0, v6.0, v7.0, and v7.1 recently, so there hopefully are no more lurking inconsistencies. Signed-off-by: Brian Norris --- drivers/mtd/nand/Kconfig | 8 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/brcmstb_nand.c | 2198 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 2207 insertions(+) create mode 100644 drivers/mtd/nand/brcmstb_nand.c diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 5897d8d8fa5a..3587017b209a 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -394,6 +394,14 @@ config MTD_NAND_GPMI_NAND block, such as SD card. So pay attention to it when you enable the GPMI. +config MTD_NAND_BRCMSTB + tristate "Broadcom STB NAND controller" + depends on ARM || MIPS + help + Enables the Broadcom NAND controller driver. The controller was + originally designed for Set-Top Box but is used on various BCM7xxx, + BCM3xxx, BCM63xxx, iProc/Cygnus and more. + config MTD_NAND_BCM47XXNFLASH tristate "Support for NAND flash on BCM4706 BCMA bus" depends on BCMA_NFLASH diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 582bbd05aff7..3b1adddc83dd 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -52,5 +52,6 @@ obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/ obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o +obj-$(CONFIG_MTD_NAND_BRCMSTB) += brcmstb_nand.o nand-objs := nand_base.o nand_bbt.o nand_timings.o diff --git a/drivers/mtd/nand/brcmstb_nand.c b/drivers/mtd/nand/brcmstb_nand.c new file mode 100644 index 000000000000..ec65a48d2487 --- /dev/null +++ b/drivers/mtd/nand/brcmstb_nand.c @@ -0,0 +1,2198 @@ +/* + * Copyright © 2010-2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This flag controls if WP stays on between erase/write commands to mitigate + * flash corruption due to power glitches. Values: + * 0: NAND_WP is not used or not available + * 1: NAND_WP is set by default, cleared for erase/write operations + * 2: NAND_WP is always cleared + */ +static int wp_on = 1; +module_param(wp_on, int, 0444); + +/*********************************************************************** + * Definitions + ***********************************************************************/ + +#define DRV_NAME "brcmstb_nand" + +#define CMD_NULL 0x00 +#define CMD_PAGE_READ 0x01 +#define CMD_SPARE_AREA_READ 0x02 +#define CMD_STATUS_READ 0x03 +#define CMD_PROGRAM_PAGE 0x04 +#define CMD_PROGRAM_SPARE_AREA 0x05 +#define CMD_COPY_BACK 0x06 +#define CMD_DEVICE_ID_READ 0x07 +#define CMD_BLOCK_ERASE 0x08 +#define CMD_FLASH_RESET 0x09 +#define CMD_BLOCKS_LOCK 0x0a +#define CMD_BLOCKS_LOCK_DOWN 0x0b +#define CMD_BLOCKS_UNLOCK 0x0c +#define CMD_READ_BLOCKS_LOCK_STATUS 0x0d +#define CMD_PARAMETER_READ 0x0e +#define CMD_PARAMETER_CHANGE_COL 0x0f +#define CMD_LOW_LEVEL_OP 0x10 + +struct brcm_nand_dma_desc { + u32 next_desc; + u32 next_desc_ext; + u32 cmd_irq; + u32 dram_addr; + u32 dram_addr_ext; + u32 tfr_len; + u32 total_len; + u32 flash_addr; + u32 flash_addr_ext; + u32 cs; + u32 pad2[5]; + u32 status_valid; +} __packed; + +/* Bitfields for brcm_nand_dma_desc::status_valid */ +#define FLASH_DMA_ECC_ERROR (1 << 8) +#define FLASH_DMA_CORR_ERROR (1 << 9) + +/* 512B flash cache in the NAND controller HW */ +#define FC_SHIFT 9U +#define FC_BYTES 512U +#define FC_WORDS (FC_BYTES >> 2) + +#define BRCMNAND_MIN_PAGESIZE 512 +#define BRCMNAND_MIN_BLOCKSIZE (8 * 1024) +#define BRCMNAND_MIN_DEVSIZE (4ULL * 1024 * 1024) + +/* Controller feature flags */ +enum { + BRCMNAND_HAS_1K_SECTORS = BIT(0), + BRCMNAND_HAS_PREFETCH = BIT(1), + BRCMNAND_HAS_CACHE_MODE = BIT(2), + BRCMNAND_HAS_WP = BIT(3), +}; + +struct brcmnand_controller { + struct device *dev; + struct nand_hw_control controller; + void __iomem *nand_base; + void __iomem *nand_fc; /* flash cache */ + void __iomem *flash_dma_base; + unsigned int irq; + unsigned int dma_irq; + int nand_version; + + int cmd_pending; + bool dma_pending; + struct completion done; + struct completion dma_done; + + /* List of NAND hosts (one for each chip-select) */ + struct list_head host_list; + + struct brcm_nand_dma_desc *dma_desc; + dma_addr_t dma_pa; + + /* in-memory cache of the FLASH_CACHE, used only for some commands */ + u32 flash_cache[FC_WORDS]; + + /* Controller revision details */ + const u16 *reg_offsets; + unsigned int reg_spacing; /* between CS1, CS2, ... regs */ + const u8 *cs_offsets; /* within each chip-select */ + const u8 *cs0_offsets; /* within CS0, if different */ + unsigned int max_block_size; + const unsigned int *block_sizes; + unsigned int max_page_size; + const unsigned int *page_sizes; + unsigned int max_oob; + u32 features; + + /* for low-power standby/resume only */ + u32 nand_cs_nand_select; + u32 nand_cs_nand_xor; + u32 corr_stat_threshold; + u32 flash_dma_mode; +}; + +struct brcmnand_cfg { + u64 device_size; + unsigned int block_size; + unsigned int page_size; + unsigned int spare_area_size; + unsigned int device_width; + unsigned int col_adr_bytes; + unsigned int blk_adr_bytes; + unsigned int ful_adr_bytes; + unsigned int sector_size_1k; + unsigned int ecc_level; + /* use for low-power standby/resume only */ + u32 acc_control; + u32 config; + u32 config_ext; + u32 timing_1; + u32 timing_2; +}; + +struct brcmnand_host { + struct list_head node; + struct device_node *of_node; + + struct nand_chip chip; + struct mtd_info mtd; + struct platform_device *pdev; + int cs; + + unsigned int last_cmd; + unsigned int last_byte; + u64 last_addr; + struct brcmnand_cfg hwcfg; + struct brcmnand_controller *ctrl; +}; + +enum brcmnand_reg { + BRCMNAND_CMD_START = 0, + BRCMNAND_CMD_EXT_ADDRESS, + BRCMNAND_CMD_ADDRESS, + BRCMNAND_INTFC_STATUS, + BRCMNAND_CS_SELECT, + BRCMNAND_CS_XOR, + BRCMNAND_LL_OP, + BRCMNAND_CS0_BASE, + BRCMNAND_CS1_BASE, /* CS1 regs, if non-contiguous */ + BRCMNAND_CORR_THRESHOLD, + BRCMNAND_CORR_THRESHOLD_EXT, + BRCMNAND_UNCORR_COUNT, + BRCMNAND_CORR_COUNT, + BRCMNAND_CORR_EXT_ADDR, + BRCMNAND_CORR_ADDR, + BRCMNAND_UNCORR_EXT_ADDR, + BRCMNAND_UNCORR_ADDR, + BRCMNAND_SEMAPHORE, + BRCMNAND_ID, + BRCMNAND_ID_EXT, + BRCMNAND_LL_RDATA, + BRCMNAND_OOB_READ_BASE, + BRCMNAND_OOB_READ_10_BASE, /* offset 0x10, if non-contiguous */ + BRCMNAND_OOB_WRITE_BASE, + BRCMNAND_OOB_WRITE_10_BASE, /* offset 0x10, if non-contiguous */ + BRCMNAND_FC_BASE, +}; + +/* BRCMNAND v4.0 */ +static const u16 brcmnand_regs_v40[] = { + [BRCMNAND_CMD_START] = 0x04, + [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, + [BRCMNAND_CMD_ADDRESS] = 0x0c, + [BRCMNAND_INTFC_STATUS] = 0x6c, + [BRCMNAND_CS_SELECT] = 0x14, + [BRCMNAND_CS_XOR] = 0x18, + [BRCMNAND_LL_OP] = 0x178, + [BRCMNAND_CS0_BASE] = 0x40, + [BRCMNAND_CS1_BASE] = 0xd0, + [BRCMNAND_CORR_THRESHOLD] = 0x84, + [BRCMNAND_CORR_THRESHOLD_EXT] = 0, + [BRCMNAND_UNCORR_COUNT] = 0, + [BRCMNAND_CORR_COUNT] = 0, + [BRCMNAND_CORR_EXT_ADDR] = 0x70, + [BRCMNAND_CORR_ADDR] = 0x74, + [BRCMNAND_UNCORR_EXT_ADDR] = 0x78, + [BRCMNAND_UNCORR_ADDR] = 0x7c, + [BRCMNAND_SEMAPHORE] = 0x58, + [BRCMNAND_ID] = 0x60, + [BRCMNAND_ID_EXT] = 0x64, + [BRCMNAND_LL_RDATA] = 0x17c, + [BRCMNAND_OOB_READ_BASE] = 0x20, + [BRCMNAND_OOB_READ_10_BASE] = 0x130, + [BRCMNAND_OOB_WRITE_BASE] = 0x30, + [BRCMNAND_OOB_WRITE_10_BASE] = 0, + [BRCMNAND_FC_BASE] = 0x200, +}; + +/* BRCMNAND v5.0 */ +static const u16 brcmnand_regs_v50[] = { + [BRCMNAND_CMD_START] = 0x04, + [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, + [BRCMNAND_CMD_ADDRESS] = 0x0c, + [BRCMNAND_INTFC_STATUS] = 0x6c, + [BRCMNAND_CS_SELECT] = 0x14, + [BRCMNAND_CS_XOR] = 0x18, + [BRCMNAND_LL_OP] = 0x178, + [BRCMNAND_CS0_BASE] = 0x40, + [BRCMNAND_CS1_BASE] = 0xd0, + [BRCMNAND_CORR_THRESHOLD] = 0x84, + [BRCMNAND_CORR_THRESHOLD_EXT] = 0, + [BRCMNAND_UNCORR_COUNT] = 0, + [BRCMNAND_CORR_COUNT] = 0, + [BRCMNAND_CORR_EXT_ADDR] = 0x70, + [BRCMNAND_CORR_ADDR] = 0x74, + [BRCMNAND_UNCORR_EXT_ADDR] = 0x78, + [BRCMNAND_UNCORR_ADDR] = 0x7c, + [BRCMNAND_SEMAPHORE] = 0x58, + [BRCMNAND_ID] = 0x60, + [BRCMNAND_ID_EXT] = 0x64, + [BRCMNAND_LL_RDATA] = 0x17c, + [BRCMNAND_OOB_READ_BASE] = 0x20, + [BRCMNAND_OOB_READ_10_BASE] = 0x130, + [BRCMNAND_OOB_WRITE_BASE] = 0x30, + [BRCMNAND_OOB_WRITE_10_BASE] = 0x140, + [BRCMNAND_FC_BASE] = 0x200, +}; + +/* BRCMNAND v6.0 - v7.1 */ +static const u16 brcmnand_regs_v60[] = { + [BRCMNAND_CMD_START] = 0x04, + [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, + [BRCMNAND_CMD_ADDRESS] = 0x0c, + [BRCMNAND_INTFC_STATUS] = 0x14, + [BRCMNAND_CS_SELECT] = 0x18, + [BRCMNAND_CS_XOR] = 0x1c, + [BRCMNAND_LL_OP] = 0x20, + [BRCMNAND_CS0_BASE] = 0x50, + [BRCMNAND_CS1_BASE] = 0, + [BRCMNAND_CORR_THRESHOLD] = 0xc0, + [BRCMNAND_CORR_THRESHOLD_EXT] = 0xc4, + [BRCMNAND_UNCORR_COUNT] = 0xfc, + [BRCMNAND_CORR_COUNT] = 0x100, + [BRCMNAND_CORR_EXT_ADDR] = 0x10c, + [BRCMNAND_CORR_ADDR] = 0x110, + [BRCMNAND_UNCORR_EXT_ADDR] = 0x114, + [BRCMNAND_UNCORR_ADDR] = 0x118, + [BRCMNAND_SEMAPHORE] = 0x150, + [BRCMNAND_ID] = 0x194, + [BRCMNAND_ID_EXT] = 0x198, + [BRCMNAND_LL_RDATA] = 0x19c, + [BRCMNAND_OOB_READ_BASE] = 0x200, + [BRCMNAND_OOB_READ_10_BASE] = 0, + [BRCMNAND_OOB_WRITE_BASE] = 0x280, + [BRCMNAND_OOB_WRITE_10_BASE] = 0, + [BRCMNAND_FC_BASE] = 0x400, +}; + +enum brcmnand_cs_reg { + BRCMNAND_CS_CFG_EXT = 0, + BRCMNAND_CS_CFG, + BRCMNAND_CS_ACC_CONTROL, + BRCMNAND_CS_TIMING1, + BRCMNAND_CS_TIMING2, +}; + +/* Per chip-select offsets for v7.1 */ +static const u8 brcmnand_cs_offsets_v71[] = { + [BRCMNAND_CS_ACC_CONTROL] = 0x00, + [BRCMNAND_CS_CFG_EXT] = 0x04, + [BRCMNAND_CS_CFG] = 0x08, + [BRCMNAND_CS_TIMING1] = 0x0c, + [BRCMNAND_CS_TIMING2] = 0x10, +}; + +/* Per chip-select offsets for pre v7.1, except CS0 on <= v5.0 */ +static const u8 brcmnand_cs_offsets[] = { + [BRCMNAND_CS_ACC_CONTROL] = 0x00, + [BRCMNAND_CS_CFG_EXT] = 0x04, + [BRCMNAND_CS_CFG] = 0x04, + [BRCMNAND_CS_TIMING1] = 0x08, + [BRCMNAND_CS_TIMING2] = 0x0c, +}; + +/* Per chip-select offset for <= v5.0 on CS0 only */ +static const u8 brcmnand_cs_offsets_cs0[] = { + [BRCMNAND_CS_ACC_CONTROL] = 0x00, + [BRCMNAND_CS_CFG_EXT] = 0x08, + [BRCMNAND_CS_CFG] = 0x08, + [BRCMNAND_CS_TIMING1] = 0x10, + [BRCMNAND_CS_TIMING2] = 0x14, +}; + +/* BRCMNAND_INTFC_STATUS */ +enum { + INTFC_FLASH_STATUS = GENMASK(7, 0), + + INTFC_ERASED = BIT(27), + INTFC_OOB_VALID = BIT(28), + INTFC_CACHE_VALID = BIT(29), + INTFC_FLASH_READY = BIT(30), + INTFC_CTLR_READY = BIT(31), +}; + +static inline u32 nand_readreg(struct brcmnand_controller *ctrl, u32 offs) +{ + return __raw_readl(ctrl->nand_base + offs); +} + +static inline void nand_writereg(struct brcmnand_controller *ctrl, u32 offs, + u32 val) +{ + __raw_writel(val, ctrl->nand_base + offs); +} + +static int brcmnand_revision_init(struct brcmnand_controller *ctrl) +{ + static const unsigned int block_sizes_v6[] = { 8, 16, 128, 256, 512, 1024, 2048, 0 }; + static const unsigned int block_sizes_v4[] = { 16, 128, 8, 512, 256, 1024, 2048, 0 }; + static const unsigned int page_sizes[] = { 512, 2048, 4096, 8192, 0 }; + + ctrl->nand_version = nand_readreg(ctrl, 0) & 0xffff; + + /* Only support v4.0+? */ + if (ctrl->nand_version < 0x0400) { + dev_err(ctrl->dev, "version %#x not supported\n", + ctrl->nand_version); + return -ENODEV; + } + + /* Register offsets */ + if (ctrl->nand_version >= 0x0600) + ctrl->reg_offsets = brcmnand_regs_v60; + else if (ctrl->nand_version >= 0x0500) + ctrl->reg_offsets = brcmnand_regs_v50; + else if (ctrl->nand_version >= 0x0400) + ctrl->reg_offsets = brcmnand_regs_v40; + + /* Chip-select stride */ + if (ctrl->nand_version >= 0x0701) + ctrl->reg_spacing = 0x14; + else + ctrl->reg_spacing = 0x10; + + /* Per chip-select registers */ + if (ctrl->nand_version >= 0x0701) { + ctrl->cs_offsets = brcmnand_cs_offsets_v71; + } else { + ctrl->cs_offsets = brcmnand_cs_offsets; + + /* v5.0 and earlier has a different CS0 offset layout */ + if (ctrl->nand_version <= 0x0500) + ctrl->cs0_offsets = brcmnand_cs_offsets_cs0; + } + + /* Page / block sizes */ + if (ctrl->nand_version >= 0x0701) { + /* >= v7.1 use nice power-of-2 values! */ + ctrl->max_page_size = 16 * 1024; + ctrl->max_block_size = 2 * 1024 * 1024; + } else { + ctrl->page_sizes = page_sizes; + if (ctrl->nand_version >= 0x0600) + ctrl->block_sizes = block_sizes_v6; + else + ctrl->block_sizes = block_sizes_v4; + + if (ctrl->nand_version < 0x0400) { + ctrl->max_page_size = 4096; + ctrl->max_block_size = 512 * 1024; + } + } + + /* Maximum spare area sector size (per 512B) */ + if (ctrl->nand_version >= 0x0600) + ctrl->max_oob = 64; + else if (ctrl->nand_version >= 0x0500) + ctrl->max_oob = 32; + else + ctrl->max_oob = 16; + + /* v6.0 and newer (except v6.1) have prefetch support */ + if (ctrl->nand_version >= 0x0600 && ctrl->nand_version != 0x0601) + ctrl->features |= BRCMNAND_HAS_PREFETCH; + + /* + * v6.x has cache mode, but it's implemented differently. Ignore it for + * now. + */ + if (ctrl->nand_version >= 0x0700) + ctrl->features |= BRCMNAND_HAS_CACHE_MODE; + + if (ctrl->nand_version >= 0x0500) + ctrl->features |= BRCMNAND_HAS_1K_SECTORS; + + if (ctrl->nand_version >= 0x0700) + ctrl->features |= BRCMNAND_HAS_WP; + else if (of_property_read_bool(ctrl->dev->of_node, "brcm,nand-has-wp")) + ctrl->features |= BRCMNAND_HAS_WP; + + return 0; +} + +static inline u32 brcmnand_read_reg(struct brcmnand_controller *ctrl, + enum brcmnand_reg reg) +{ + u16 offs = ctrl->reg_offsets[reg]; + + if (offs) + return nand_readreg(ctrl, offs); + else + return 0; +} + +static inline void brcmnand_write_reg(struct brcmnand_controller *ctrl, + enum brcmnand_reg reg, u32 val) +{ + u16 offs = ctrl->reg_offsets[reg]; + + if (offs) + nand_writereg(ctrl, offs, val); +} + +static inline void brcmnand_rmw_reg(struct brcmnand_controller *ctrl, + enum brcmnand_reg reg, u32 mask, unsigned + int shift, u32 val) +{ + u32 tmp = brcmnand_read_reg(ctrl, reg); + + tmp &= ~mask; + tmp |= val << shift; + brcmnand_write_reg(ctrl, reg, tmp); +} + +static inline u32 brcmnand_read_fc(struct brcmnand_controller *ctrl, int word) +{ + return __raw_readl(ctrl->nand_fc + word * 4); +} + +static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl, + int word, u32 val) +{ + __raw_writel(val, ctrl->nand_fc + word * 4); +} + +static inline u16 brcmnand_cs_offset(struct brcmnand_controller *ctrl, int cs, + enum brcmnand_cs_reg reg) +{ + u16 offs_cs0 = ctrl->reg_offsets[BRCMNAND_CS0_BASE]; + u16 offs_cs1 = ctrl->reg_offsets[BRCMNAND_CS1_BASE]; + u8 cs_offs; + + if (cs == 0 && ctrl->cs0_offsets) + cs_offs = ctrl->cs0_offsets[reg]; + else + cs_offs = ctrl->cs_offsets[reg]; + + if (cs && offs_cs1) + return offs_cs1 + (cs - 1) * ctrl->reg_spacing + cs_offs; + + return offs_cs0 + cs * ctrl->reg_spacing + cs_offs; +} + +static inline u32 brcmnand_count_corrected(struct brcmnand_controller *ctrl) +{ + if (ctrl->nand_version < 0x0600) + return 1; + return brcmnand_read_reg(ctrl, BRCMNAND_CORR_COUNT); +} + +static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val) +{ + struct brcmnand_controller *ctrl = host->ctrl; + unsigned int shift = 0, bits; + enum brcmnand_reg reg = BRCMNAND_CORR_THRESHOLD; + int cs = host->cs; + + if (ctrl->nand_version >= 0x0600) + bits = 6; + else if (ctrl->nand_version >= 0x0500) + bits = 5; + else + bits = 4; + + if (ctrl->nand_version >= 0x0600) { + if (cs >= 5) + reg = BRCMNAND_CORR_THRESHOLD_EXT; + shift = (cs % 5) * bits; + } + brcmnand_rmw_reg(ctrl, reg, (bits - 1) << shift, shift, val); +} + +static inline int brcmnand_cmd_shift(struct brcmnand_controller *ctrl) +{ + if (ctrl->nand_version < 0x0700) + return 24; + return 0; +} + +/*********************************************************************** + * NAND ACC CONTROL bitfield + * + * Some bits have remained constant throughout hardware revision, while + * others have shifted around. + ***********************************************************************/ + +/* Constant for all versions (where supported) */ +enum { + /* See BRCMNAND_HAS_CACHE_MODE */ + ACC_CONTROL_CACHE_MODE = BIT(22), + + /* See BRCMNAND_HAS_PREFETCH */ + ACC_CONTROL_PREFETCH = BIT(23), + + ACC_CONTROL_PAGE_HIT = BIT(24), + ACC_CONTROL_WR_PREEMPT = BIT(25), + ACC_CONTROL_PARTIAL_PAGE = BIT(26), + ACC_CONTROL_RD_ERASED = BIT(27), + ACC_CONTROL_FAST_PGM_RDIN = BIT(28), + ACC_CONTROL_WR_ECC = BIT(30), + ACC_CONTROL_RD_ECC = BIT(31), +}; + +static inline u32 brcmnand_spare_area_mask(struct brcmnand_controller *ctrl) +{ + if (ctrl->nand_version >= 0x0600) + return GENMASK(6, 0); + else + return GENMASK(5, 0); +} + +#define NAND_ACC_CONTROL_ECC_SHIFT 16 + +static inline u32 brcmnand_ecc_level_mask(struct brcmnand_controller *ctrl) +{ + u32 mask = (ctrl->nand_version >= 0x0600) ? 0x1f : 0x0f; + + return mask << NAND_ACC_CONTROL_ECC_SHIFT; +} + +static void brcmnand_set_ecc_enabled(struct brcmnand_host *host, int en) +{ + struct brcmnand_controller *ctrl = host->ctrl; + u16 offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL); + u32 acc_control = nand_readreg(ctrl, offs); + u32 ecc_flags = ACC_CONTROL_WR_ECC | ACC_CONTROL_RD_ECC; + + if (en) { + acc_control |= ecc_flags; /* enable RD/WR ECC */ + acc_control |= host->hwcfg.ecc_level + << NAND_ACC_CONTROL_ECC_SHIFT; + } else { + acc_control &= ~ecc_flags; /* disable RD/WR ECC */ + acc_control &= ~brcmnand_ecc_level_mask(ctrl); + } + + nand_writereg(ctrl, offs, acc_control); +} + +static inline int brcmnand_sector_1k_shift(struct brcmnand_controller *ctrl) +{ + if (ctrl->nand_version >= 0x0600) + return 7; + else if (ctrl->nand_version >= 0x0500) + return 6; + else + return -1; +} + +static int brcmnand_get_sector_size_1k(struct brcmnand_host *host) +{ + struct brcmnand_controller *ctrl = host->ctrl; + int shift = brcmnand_sector_1k_shift(ctrl); + u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_ACC_CONTROL); + + if (shift < 0) + return 0; + + return (nand_readreg(ctrl, acc_control_offs) >> shift) & 0x1; +} + +static void brcmnand_set_sector_size_1k(struct brcmnand_host *host, int val) +{ + struct brcmnand_controller *ctrl = host->ctrl; + int shift = brcmnand_sector_1k_shift(ctrl); + u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_ACC_CONTROL); + u32 tmp; + + if (shift < 0) + return; + + tmp = nand_readreg(ctrl, acc_control_offs); + tmp &= ~(1 << shift); + tmp |= (!!val) << shift; + nand_writereg(ctrl, acc_control_offs, tmp); +} + +/*********************************************************************** + * CS_NAND_SELECT + ***********************************************************************/ + +enum { + CS_SELECT_NAND_WP = BIT(29), + CS_SELECT_AUTO_DEVICE_ID_CFG = BIT(30), +}; + +static inline void brcmnand_set_wp(struct brcmnand_controller *ctrl, bool en) +{ + u32 val = en ? CS_SELECT_NAND_WP : 0; + + brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT, CS_SELECT_NAND_WP, 0, val); +} + +/*********************************************************************** + * Flash DMA + ***********************************************************************/ + +enum flash_dma_reg { + FLASH_DMA_REVISION = 0x00, + FLASH_DMA_FIRST_DESC = 0x04, + FLASH_DMA_FIRST_DESC_EXT = 0x08, + FLASH_DMA_CTRL = 0x0c, + FLASH_DMA_MODE = 0x10, + FLASH_DMA_STATUS = 0x14, + FLASH_DMA_INTERRUPT_DESC = 0x18, + FLASH_DMA_INTERRUPT_DESC_EXT = 0x1c, + FLASH_DMA_ERROR_STATUS = 0x20, + FLASH_DMA_CURRENT_DESC = 0x24, + FLASH_DMA_CURRENT_DESC_EXT = 0x28, +}; + +static inline bool has_flash_dma(struct brcmnand_controller *ctrl) +{ + return ctrl->flash_dma_base; +} + +static inline bool flash_dma_buf_ok(const void *buf) +{ + return buf && !is_vmalloc_addr(buf) && + likely(IS_ALIGNED((uintptr_t)buf, 4)); +} + +static inline void flash_dma_writel(struct brcmnand_controller *ctrl, u8 offs, + u32 val) +{ + __raw_writel(val, ctrl->flash_dma_base + offs); +} + +static inline u32 flash_dma_readl(struct brcmnand_controller *ctrl, u8 offs) +{ + return __raw_readl(ctrl->flash_dma_base + offs); +} + +/* Low-level operation types: command, address, write, or read */ +enum brcmnand_llop_type { + LL_OP_CMD, + LL_OP_ADDR, + LL_OP_WR, + LL_OP_RD, +}; + +/*********************************************************************** + * Internal support functions + ***********************************************************************/ + +static inline bool is_hamming_ecc(struct brcmnand_cfg *cfg) +{ + return cfg->sector_size_1k == 0 && cfg->spare_area_size == 16 && + cfg->ecc_level == 15; +} + +/* + * Returns a nand_ecclayout strucutre for the given layout/configuration. + * Returns NULL on failure. + */ +static struct nand_ecclayout *brcmnand_create_layout(int ecc_level, + struct brcmnand_host *host) +{ + struct brcmnand_cfg *cfg = &host->hwcfg; + int i, j; + struct nand_ecclayout *layout; + int req; + int sectors; + int sas; + int idx1, idx2; + + layout = devm_kzalloc(&host->pdev->dev, sizeof(*layout), GFP_KERNEL); + if (!layout) + return NULL; + + sectors = cfg->page_size / (512 << cfg->sector_size_1k); + sas = cfg->spare_area_size << cfg->sector_size_1k; + + /* Hamming */ + if (is_hamming_ecc(cfg)) { + for (i = 0, idx1 = 0, idx2 = 0; i < sectors; i++) { + /* First sector of each page may have BBI */ + if (i == 0) { + layout->oobfree[idx2].offset = i * sas + 1; + /* Small-page NAND use byte 6 for BBI */ + if (cfg->page_size == 512) + layout->oobfree[idx2].offset--; + layout->oobfree[idx2].length = 5; + } else { + layout->oobfree[idx2].offset = i * sas; + layout->oobfree[idx2].length = 6; + } + idx2++; + layout->eccpos[idx1++] = i * sas + 6; + layout->eccpos[idx1++] = i * sas + 7; + layout->eccpos[idx1++] = i * sas + 8; + layout->oobfree[idx2].offset = i * sas + 9; + layout->oobfree[idx2].length = 7; + idx2++; + /* Leave zero-terminated entry for OOBFREE */ + if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE || + idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1) + break; + } + goto out; + } + + /* + * CONTROLLER_VERSION: + * < v5.0: ECC_REQ = ceil(BCH_T * 13/8) + * >= v5.0: ECC_REQ = ceil(BCH_T * 14/8) + * But we will just be conservative. + */ + req = DIV_ROUND_UP(ecc_level * 14, 8); + if (req >= sas) { + dev_err(&host->pdev->dev, + "error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n", + req, sas); + return NULL; + } + + layout->eccbytes = req * sectors; + for (i = 0, idx1 = 0, idx2 = 0; i < sectors; i++) { + for (j = sas - req; j < sas && idx1 < + MTD_MAX_ECCPOS_ENTRIES_LARGE; j++, idx1++) + layout->eccpos[idx1] = i * sas + j; + + /* First sector of each page may have BBI */ + if (i == 0) { + if (cfg->page_size == 512 && (sas - req >= 6)) { + /* Small-page NAND use byte 6 for BBI */ + layout->oobfree[idx2].offset = 0; + layout->oobfree[idx2].length = 5; + idx2++; + if (sas - req > 6) { + layout->oobfree[idx2].offset = 6; + layout->oobfree[idx2].length = + sas - req - 6; + idx2++; + } + } else if (sas > req + 1) { + layout->oobfree[idx2].offset = i * sas + 1; + layout->oobfree[idx2].length = sas - req - 1; + idx2++; + } + } else if (sas > req) { + layout->oobfree[idx2].offset = i * sas; + layout->oobfree[idx2].length = sas - req; + idx2++; + } + /* Leave zero-terminated entry for OOBFREE */ + if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE || + idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1) + break; + } +out: + /* Sum available OOB */ + for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE; i++) + layout->oobavail += layout->oobfree[i].length; + return layout; +} + +static struct nand_ecclayout *brcmstb_choose_ecc_layout( + struct brcmnand_host *host) +{ + struct nand_ecclayout *layout; + struct brcmnand_cfg *p = &host->hwcfg; + unsigned int ecc_level = p->ecc_level; + + if (p->sector_size_1k) + ecc_level <<= 1; + + layout = brcmnand_create_layout(ecc_level, host); + if (!layout) { + dev_err(&host->pdev->dev, + "no proper ecc_layout for this NAND cfg\n"); + return NULL; + } + + return layout; +} + +static void brcmnand_wp(struct mtd_info *mtd, int wp) +{ + struct nand_chip *chip = mtd->priv; + struct brcmnand_host *host = chip->priv; + struct brcmnand_controller *ctrl = host->ctrl; + + if ((ctrl->features & BRCMNAND_HAS_WP) && wp_on == 1) { + static int old_wp = -1; + + if (old_wp != wp) { + dev_dbg(ctrl->dev, "WP %s\n", wp ? "on" : "off"); + old_wp = wp; + } + brcmnand_set_wp(ctrl, wp); + } +} + +/* Helper functions for reading and writing OOB registers */ +static inline u8 oob_reg_read(struct brcmnand_controller *ctrl, u32 offs) +{ + u16 offset0, offset10, reg_offs; + + offset0 = ctrl->reg_offsets[BRCMNAND_OOB_READ_BASE]; + offset10 = ctrl->reg_offsets[BRCMNAND_OOB_READ_10_BASE]; + + if (offs >= ctrl->max_oob) + return 0x77; + + if (offs >= 16 && offset10) + reg_offs = offset10 + ((offs - 0x10) & ~0x03); + else + reg_offs = offset0 + (offs & ~0x03); + + return nand_readreg(ctrl, reg_offs) >> (24 - ((offs & 0x03) << 3)); +} + +static inline void oob_reg_write(struct brcmnand_controller *ctrl, u32 offs, + u32 data) +{ + u16 offset0, offset10, reg_offs; + + offset0 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_BASE]; + offset10 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_10_BASE]; + + if (offs >= ctrl->max_oob) + return; + + if (offs >= 16 && offset10) + reg_offs = offset10 + ((offs - 0x10) & ~0x03); + else + reg_offs = offset0 + (offs & ~0x03); + + nand_writereg(ctrl, reg_offs, data); +} + +/* + * read_oob_from_regs - read data from OOB registers + * @ctrl: NAND controller + * @i: sub-page sector index + * @oob: buffer to read to + * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE) + * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal + */ +static int read_oob_from_regs(struct brcmnand_controller *ctrl, int i, u8 *oob, + int sas, int sector_1k) +{ + int tbytes = sas << sector_1k; + int j; + + /* Adjust OOB values for 1K sector size */ + if (sector_1k && (i & 0x01)) + tbytes = max(0, tbytes - (int)ctrl->max_oob); + tbytes = min_t(int, tbytes, ctrl->max_oob); + + for (j = 0; j < tbytes; j++) + oob[j] = oob_reg_read(ctrl, j); + return tbytes; +} + +/* + * write_oob_to_regs - write data to OOB registers + * @i: sub-page sector index + * @oob: buffer to write from + * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE) + * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal + */ +static int write_oob_to_regs(struct brcmnand_controller *ctrl, int i, + const u8 *oob, int sas, int sector_1k) +{ + int tbytes = sas << sector_1k; + int j; + + /* Adjust OOB values for 1K sector size */ + if (sector_1k && (i & 0x01)) + tbytes = max(0, tbytes - (int)ctrl->max_oob); + tbytes = min_t(int, tbytes, ctrl->max_oob); + + for (j = 0; j < tbytes; j += 4) + oob_reg_write(ctrl, j, + (oob[j + 0] << 24) | + (oob[j + 1] << 16) | + (oob[j + 2] << 8) | + (oob[j + 3] << 0)); + return tbytes; +} + +static irqreturn_t brcmnand_ctlrdy_irq(int irq, void *data) +{ + struct brcmnand_controller *ctrl = data; + + /* Discard all NAND_CTLRDY interrupts during DMA */ + if (ctrl->dma_pending) + return IRQ_HANDLED; + + complete(&ctrl->done); + return IRQ_HANDLED; +} + +static irqreturn_t brcmnand_dma_irq(int irq, void *data) +{ + struct brcmnand_controller *ctrl = data; + + complete(&ctrl->dma_done); + + return IRQ_HANDLED; +} + +static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd) +{ + struct brcmnand_controller *ctrl = host->ctrl; + u32 intfc; + + dev_dbg(ctrl->dev, "send native cmd %d addr_lo 0x%x\n", cmd, + brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS)); + BUG_ON(ctrl->cmd_pending != 0); + ctrl->cmd_pending = cmd; + + intfc = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS); + BUG_ON(!(intfc & INTFC_CTLR_READY)); + + mb(); /* flush previous writes */ + brcmnand_write_reg(ctrl, BRCMNAND_CMD_START, + cmd << brcmnand_cmd_shift(ctrl)); +} + +/*********************************************************************** + * NAND MTD API: read/program/erase + ***********************************************************************/ + +static void brcmnand_cmd_ctrl(struct mtd_info *mtd, int dat, + unsigned int ctrl) +{ + /* intentionally left blank */ +} + +static int brcmnand_waitfunc(struct mtd_info *mtd, struct nand_chip *this) +{ + struct nand_chip *chip = mtd->priv; + struct brcmnand_host *host = chip->priv; + struct brcmnand_controller *ctrl = host->ctrl; + unsigned long timeo = msecs_to_jiffies(100); + + dev_dbg(ctrl->dev, "wait on native cmd %d\n", ctrl->cmd_pending); + if (ctrl->cmd_pending && + wait_for_completion_timeout(&ctrl->done, timeo) <= 0) { + u32 cmd = brcmnand_read_reg(ctrl, BRCMNAND_CMD_START) + >> brcmnand_cmd_shift(ctrl); + + dev_err_ratelimited(ctrl->dev, + "timeout waiting for command %#02x\n", cmd); + dev_err_ratelimited(ctrl->dev, "intfc status %08x\n", + brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS)); + } + ctrl->cmd_pending = 0; + return brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) & + INTFC_FLASH_STATUS; +} + +enum { + LLOP_RE = BIT(16), + LLOP_WE = BIT(17), + LLOP_ALE = BIT(18), + LLOP_CLE = BIT(19), + LLOP_RETURN_IDLE = BIT(31), + + LLOP_DATA_MASK = GENMASK(15, 0), +}; + +static int brcmnand_low_level_op(struct brcmnand_host *host, + enum brcmnand_llop_type type, u32 data, + bool last_op) +{ + struct mtd_info *mtd = &host->mtd; + struct nand_chip *chip = &host->chip; + struct brcmnand_controller *ctrl = host->ctrl; + u32 tmp; + + tmp = data & LLOP_DATA_MASK; + switch (type) { + case LL_OP_CMD: + tmp |= LLOP_WE | LLOP_CLE; + break; + case LL_OP_ADDR: + /* WE | ALE */ + tmp |= LLOP_WE | LLOP_ALE; + break; + case LL_OP_WR: + /* WE */ + tmp |= LLOP_WE; + break; + case LL_OP_RD: + /* RE */ + tmp |= LLOP_RE; + break; + } + if (last_op) + /* RETURN_IDLE */ + tmp |= LLOP_RETURN_IDLE; + + dev_dbg(ctrl->dev, "ll_op cmd %#x\n", tmp); + + brcmnand_write_reg(ctrl, BRCMNAND_LL_OP, tmp); + (void)brcmnand_read_reg(ctrl, BRCMNAND_LL_OP); + + brcmnand_send_cmd(host, CMD_LOW_LEVEL_OP); + return brcmnand_waitfunc(mtd, chip); +} + +static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd->priv; + struct brcmnand_host *host = chip->priv; + struct brcmnand_controller *ctrl = host->ctrl; + u64 addr = (u64)page_addr << chip->page_shift; + int native_cmd = 0; + + if (command == NAND_CMD_READID || command == NAND_CMD_PARAM || + command == NAND_CMD_RNDOUT) + addr = (u64)column; + /* Avoid propagating a negative, don't-care address */ + else if (page_addr < 0) + addr = 0; + + dev_dbg(ctrl->dev, "cmd 0x%x addr 0x%llx\n", command, + (unsigned long long)addr); + + host->last_cmd = command; + host->last_byte = 0; + host->last_addr = addr; + + switch (command) { + case NAND_CMD_RESET: + native_cmd = CMD_FLASH_RESET; + break; + case NAND_CMD_STATUS: + native_cmd = CMD_STATUS_READ; + break; + case NAND_CMD_READID: + native_cmd = CMD_DEVICE_ID_READ; + break; + case NAND_CMD_READOOB: + native_cmd = CMD_SPARE_AREA_READ; + break; + case NAND_CMD_ERASE1: + native_cmd = CMD_BLOCK_ERASE; + brcmnand_wp(mtd, 0); + break; + case NAND_CMD_PARAM: + native_cmd = CMD_PARAMETER_READ; + break; + case NAND_CMD_SET_FEATURES: + case NAND_CMD_GET_FEATURES: + brcmnand_low_level_op(host, LL_OP_CMD, command, false); + brcmnand_low_level_op(host, LL_OP_ADDR, column, false); + break; + case NAND_CMD_RNDOUT: + native_cmd = CMD_PARAMETER_CHANGE_COL; + addr &= ~((u64)(FC_BYTES - 1)); + /* + * HW quirk: PARAMETER_CHANGE_COL requires SECTOR_SIZE_1K=0 + * NB: hwcfg.sector_size_1k may not be initialized yet + */ + if (brcmnand_get_sector_size_1k(host)) { + host->hwcfg.sector_size_1k = + brcmnand_get_sector_size_1k(host); + brcmnand_set_sector_size_1k(host, 0); + } + break; + } + + if (!native_cmd) + return; + + brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, + (host->cs << 16) | ((addr >> 32) & 0xffff)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); + brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, lower_32_bits(addr)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); + + brcmnand_send_cmd(host, native_cmd); + brcmnand_waitfunc(mtd, chip); + + if (native_cmd == CMD_PARAMETER_READ || + native_cmd == CMD_PARAMETER_CHANGE_COL) { + int i; + /* + * Must cache the FLASH_CACHE now, since changes in + * SECTOR_SIZE_1K may invalidate it + */ + for (i = 0; i < FC_WORDS; i++) + ctrl->flash_cache[i] = brcmnand_read_fc(ctrl, i); + /* Cleanup from HW quirk: restore SECTOR_SIZE_1K */ + if (host->hwcfg.sector_size_1k) + brcmnand_set_sector_size_1k(host, + host->hwcfg.sector_size_1k); + } + + /* Re-enable protection is necessary only after erase */ + if (command == NAND_CMD_ERASE1) + brcmnand_wp(mtd, 1); +} + +static uint8_t brcmnand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct brcmnand_host *host = chip->priv; + struct brcmnand_controller *ctrl = host->ctrl; + uint8_t ret = 0; + int addr, offs; + + switch (host->last_cmd) { + case NAND_CMD_READID: + if (host->last_byte < 4) + ret = brcmnand_read_reg(ctrl, BRCMNAND_ID) >> + (24 - (host->last_byte << 3)); + else if (host->last_byte < 8) + ret = brcmnand_read_reg(ctrl, BRCMNAND_ID_EXT) >> + (56 - (host->last_byte << 3)); + break; + + case NAND_CMD_READOOB: + ret = oob_reg_read(ctrl, host->last_byte); + break; + + case NAND_CMD_STATUS: + ret = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) & + INTFC_FLASH_STATUS; + if (wp_on) /* hide WP status */ + ret |= NAND_STATUS_WP; + break; + + case NAND_CMD_PARAM: + case NAND_CMD_RNDOUT: + addr = host->last_addr + host->last_byte; + offs = addr & (FC_BYTES - 1); + + /* At FC_BYTES boundary, switch to next column */ + if (host->last_byte > 0 && offs == 0) + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, addr, -1); + + ret = ctrl->flash_cache[offs >> 2] >> + (24 - ((offs & 0x03) << 3)); + break; + case NAND_CMD_GET_FEATURES: + if (host->last_byte >= ONFI_SUBFEATURE_PARAM_LEN) { + ret = 0; + } else { + bool last = host->last_byte == + ONFI_SUBFEATURE_PARAM_LEN - 1; + brcmnand_low_level_op(host, LL_OP_RD, 0, last); + ret = brcmnand_read_reg(ctrl, BRCMNAND_LL_RDATA) & 0xff; + } + } + + dev_dbg(ctrl->dev, "read byte = 0x%02x\n", ret); + host->last_byte++; + + return ret; +} + +static void brcmnand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + + for (i = 0; i < len; i++, buf++) + *buf = brcmnand_read_byte(mtd); +} + +static void brcmnand_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + struct brcmnand_host *host = chip->priv; + + switch (host->last_cmd) { + case NAND_CMD_SET_FEATURES: + for (i = 0; i < len; i++) + brcmnand_low_level_op(host, LL_OP_WR, buf[i], + (i + 1) == len); + break; + default: + BUG(); + break; + } +} + +/** + * Construct a FLASH_DMA descriptor as part of a linked list. You must know the + * following ahead of time: + * - Is this descriptor the beginning or end of a linked list? + * - What is the (DMA) address of the next descriptor in the linked list? + */ +static int brcmnand_fill_dma_desc(struct brcmnand_host *host, + struct brcm_nand_dma_desc *desc, u64 addr, + dma_addr_t buf, u32 len, u8 dma_cmd, + bool begin, bool end, + dma_addr_t next_desc) +{ + memset(desc, 0, sizeof(*desc)); + /* Descriptors are written in native byte order (wordwise) */ + desc->next_desc = lower_32_bits(next_desc); + desc->next_desc_ext = upper_32_bits(next_desc); + desc->cmd_irq = (dma_cmd << 24) | + (end ? (0x03 << 8) : 0) | /* IRQ | STOP */ + (!!begin) | ((!!end) << 1); /* head, tail */ +#ifdef CONFIG_CPU_BIG_ENDIAN + desc->cmd_irq |= 0x01 << 12; +#endif + desc->dram_addr = lower_32_bits(buf); + desc->dram_addr_ext = upper_32_bits(buf); + desc->tfr_len = len; + desc->total_len = len; + desc->flash_addr = lower_32_bits(addr); + desc->flash_addr_ext = upper_32_bits(addr); + desc->cs = host->cs; + desc->status_valid = 0x01; + return 0; +} + +/** + * Kick the FLASH_DMA engine, with a given DMA descriptor + */ +static void brcmnand_dma_run(struct brcmnand_host *host, dma_addr_t desc) +{ + struct brcmnand_controller *ctrl = host->ctrl; + unsigned long timeo = msecs_to_jiffies(100); + + flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC, lower_32_bits(desc)); + (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC); + flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC_EXT, upper_32_bits(desc)); + (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC_EXT); + + /* Start FLASH_DMA engine */ + ctrl->dma_pending = true; + mb(); /* flush previous writes */ + flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0x03); /* wake | run */ + + if (wait_for_completion_timeout(&ctrl->dma_done, timeo) <= 0) { + dev_err(ctrl->dev, + "timeout waiting for DMA; status %#x, error status %#x\n", + flash_dma_readl(ctrl, FLASH_DMA_STATUS), + flash_dma_readl(ctrl, FLASH_DMA_ERROR_STATUS)); + } + ctrl->dma_pending = false; + flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0); /* force stop */ +} + +static int brcmnand_dma_trans(struct brcmnand_host *host, u64 addr, u32 *buf, + u32 len, u8 dma_cmd) +{ + struct brcmnand_controller *ctrl = host->ctrl; + dma_addr_t buf_pa; + int dir = dma_cmd == CMD_PAGE_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + + buf_pa = dma_map_single(ctrl->dev, buf, len, dir); + if (dma_mapping_error(ctrl->dev, buf_pa)) { + dev_err(ctrl->dev, "unable to map buffer for DMA\n"); + return -ENOMEM; + } + + brcmnand_fill_dma_desc(host, ctrl->dma_desc, addr, buf_pa, len, + dma_cmd, true, true, 0); + + brcmnand_dma_run(host, ctrl->dma_pa); + + dma_unmap_single(ctrl->dev, buf_pa, len, dir); + + if (ctrl->dma_desc->status_valid & FLASH_DMA_ECC_ERROR) + return -EBADMSG; + else if (ctrl->dma_desc->status_valid & FLASH_DMA_CORR_ERROR) + return -EUCLEAN; + + return 0; +} + +/* + * Assumes proper CS is already set + */ +static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip, + u64 addr, unsigned int trans, u32 *buf, + u8 *oob, u64 *err_addr) +{ + struct brcmnand_host *host = chip->priv; + struct brcmnand_controller *ctrl = host->ctrl; + int i, j, ret = 0; + + /* Clear error addresses */ + brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_ADDR, 0); + brcmnand_write_reg(ctrl, BRCMNAND_CORR_ADDR, 0); + + brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, + (host->cs << 16) | ((addr >> 32) & 0xffff)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); + + for (i = 0; i < trans; i++, addr += FC_BYTES) { + brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, + lower_32_bits(addr)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); + /* SPARE_AREA_READ does not use ECC, so just use PAGE_READ */ + brcmnand_send_cmd(host, CMD_PAGE_READ); + brcmnand_waitfunc(mtd, chip); + + if (likely(buf)) + for (j = 0; j < FC_WORDS; j++, buf++) + *buf = brcmnand_read_fc(ctrl, j); + + if (oob) + oob += read_oob_from_regs(ctrl, i, oob, + mtd->oobsize / trans, + host->hwcfg.sector_size_1k); + + if (!ret) { + *err_addr = brcmnand_read_reg(ctrl, + BRCMNAND_UNCORR_ADDR) | + ((u64)(brcmnand_read_reg(ctrl, + BRCMNAND_UNCORR_EXT_ADDR) + & 0xffff) << 32); + if (*err_addr) + ret = -EBADMSG; + } + + if (!ret) { + *err_addr = brcmnand_read_reg(ctrl, + BRCMNAND_CORR_ADDR) | + ((u64)(brcmnand_read_reg(ctrl, + BRCMNAND_CORR_EXT_ADDR) + & 0xffff) << 32); + if (*err_addr) + ret = -EUCLEAN; + } + } + + return ret; +} + +static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip, + u64 addr, unsigned int trans, u32 *buf, u8 *oob) +{ + struct brcmnand_host *host = chip->priv; + struct brcmnand_controller *ctrl = host->ctrl; + u64 err_addr = 0; + int err; + + dev_dbg(ctrl->dev, "read %llx -> %p\n", (unsigned long long)addr, buf); + + brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_COUNT, 0); + + if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) { + err = brcmnand_dma_trans(host, addr, buf, trans * FC_BYTES, + CMD_PAGE_READ); + if (err) { + if (mtd_is_bitflip_or_eccerr(err)) + err_addr = addr; + else + return -EIO; + } + } else { + if (oob) + memset(oob, 0x99, mtd->oobsize); + + err = brcmnand_read_by_pio(mtd, chip, addr, trans, buf, + oob, &err_addr); + } + + if (mtd_is_eccerr(err)) { + dev_dbg(ctrl->dev, "uncorrectable error at 0x%llx\n", + (unsigned long long)err_addr); + mtd->ecc_stats.failed++; + /* NAND layer expects zero on ECC errors */ + return 0; + } + + if (mtd_is_bitflip(err)) { + unsigned int corrected = brcmnand_count_corrected(ctrl); + + dev_dbg(ctrl->dev, "corrected error at 0x%llx\n", + (unsigned long long)err_addr); + mtd->ecc_stats.corrected += corrected; + /* Always exceed the software-imposed threshold */ + return max(mtd->bitflip_threshold, corrected); + } + + return 0; +} + +static int brcmnand_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct brcmnand_host *host = chip->priv; + u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL; + + return brcmnand_read(mtd, chip, host->last_addr, + mtd->writesize >> FC_SHIFT, (u32 *)buf, oob); +} + +static int brcmnand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct brcmnand_host *host = chip->priv; + u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL; + int ret; + + brcmnand_set_ecc_enabled(host, 0); + ret = brcmnand_read(mtd, chip, host->last_addr, + mtd->writesize >> FC_SHIFT, (u32 *)buf, oob); + brcmnand_set_ecc_enabled(host, 1); + return ret; +} + +static int brcmnand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + return brcmnand_read(mtd, chip, (u64)page << chip->page_shift, + mtd->writesize >> FC_SHIFT, + NULL, (u8 *)chip->oob_poi); +} + +static int brcmnand_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct brcmnand_host *host = chip->priv; + + brcmnand_set_ecc_enabled(host, 0); + brcmnand_read(mtd, chip, (u64)page << chip->page_shift, + mtd->writesize >> FC_SHIFT, + NULL, (u8 *)chip->oob_poi); + brcmnand_set_ecc_enabled(host, 1); + return 0; +} + +static int brcmnand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, + uint32_t data_offs, uint32_t readlen, + uint8_t *bufpoi, int page) +{ + struct brcmnand_host *host = chip->priv; + + return brcmnand_read(mtd, chip, host->last_addr + data_offs, + readlen >> FC_SHIFT, (u32 *)bufpoi, NULL); +} + +static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip, + u64 addr, const u32 *buf, u8 *oob) +{ + struct brcmnand_host *host = chip->priv; + struct brcmnand_controller *ctrl = host->ctrl; + unsigned int i, j, trans = mtd->writesize >> FC_SHIFT; + int status, ret = 0; + + dev_dbg(ctrl->dev, "write %llx <- %p\n", (unsigned long long)addr, buf); + + if (unlikely((u32)buf & 0x03)) { + dev_warn(ctrl->dev, "unaligned buffer: %p\n", buf); + buf = (u32 *)((u32)buf & ~0x03); + } + + brcmnand_wp(mtd, 0); + + for (i = 0; i < ctrl->max_oob; i += 4) + oob_reg_write(ctrl, i, 0xffffffff); + + if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) { + if (brcmnand_dma_trans(host, addr, (u32 *)buf, + mtd->writesize, CMD_PROGRAM_PAGE)) + ret = -EIO; + goto out; + } + + brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, + (host->cs << 16) | ((addr >> 32) & 0xffff)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); + + for (i = 0; i < trans; i++, addr += FC_BYTES) { + /* full address MUST be set before populating FC */ + brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, + lower_32_bits(addr)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); + + if (buf) + for (j = 0; j < FC_WORDS; j++, buf++) + brcmnand_write_fc(ctrl, j, *buf); + else if (oob) + for (j = 0; j < FC_WORDS; j++) + brcmnand_write_fc(ctrl, j, 0xffffffff); + + if (oob) { + oob += write_oob_to_regs(ctrl, i, oob, + mtd->oobsize / trans, + host->hwcfg.sector_size_1k); + } + + /* we cannot use SPARE_AREA_PROGRAM when PARTIAL_PAGE_EN=0 */ + brcmnand_send_cmd(host, CMD_PROGRAM_PAGE); + status = brcmnand_waitfunc(mtd, chip); + + if (status & NAND_STATUS_FAIL) { + dev_info(ctrl->dev, "program failed at %llx\n", + (unsigned long long)addr); + ret = -EIO; + goto out; + } + } +out: + brcmnand_wp(mtd, 1); + return ret; +} + +static int brcmnand_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) +{ + struct brcmnand_host *host = chip->priv; + void *oob = oob_required ? chip->oob_poi : NULL; + + brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob); + return 0; +} + +static int brcmnand_write_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf, + int oob_required) +{ + struct brcmnand_host *host = chip->priv; + void *oob = oob_required ? chip->oob_poi : NULL; + + brcmnand_set_ecc_enabled(host, 0); + brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob); + brcmnand_set_ecc_enabled(host, 1); + return 0; +} + +static int brcmnand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + return brcmnand_write(mtd, chip, (u64)page << chip->page_shift, + NULL, chip->oob_poi); +} + +static int brcmnand_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct brcmnand_host *host = chip->priv; + int ret; + + brcmnand_set_ecc_enabled(host, 0); + ret = brcmnand_write(mtd, chip, (u64)page << chip->page_shift, NULL, + (u8 *)chip->oob_poi); + brcmnand_set_ecc_enabled(host, 1); + + return ret; +} + +/*********************************************************************** + * Per-CS setup (1 NAND device) + ***********************************************************************/ + +static int brcmnand_set_cfg(struct brcmnand_host *host, + struct brcmnand_cfg *cfg) +{ + struct brcmnand_controller *ctrl = host->ctrl; + struct nand_chip *chip = &host->chip; + u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG); + u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_CFG_EXT); + u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_ACC_CONTROL); + u8 block_size = 0, page_size = 0, device_size = 0; + u32 tmp; + + if (ctrl->block_sizes) { + int i, found; + + for (i = 0, found = 0; ctrl->block_sizes[i]; i++) + if (ctrl->block_sizes[i] * 1024 == cfg->block_size) { + block_size = i; + found = 1; + } + if (!found) { + dev_warn(ctrl->dev, "invalid block size %u\n", + cfg->block_size); + return -EINVAL; + } + } else { + block_size = ffs(cfg->block_size) - ffs(BRCMNAND_MIN_BLOCKSIZE); + } + + if (cfg->block_size < BRCMNAND_MIN_BLOCKSIZE || (ctrl->max_block_size && + cfg->block_size > ctrl->max_block_size)) { + dev_warn(ctrl->dev, "invalid block size %u\n", + cfg->block_size); + block_size = 0; + } + + if (ctrl->page_sizes) { + int i, found; + + for (i = 0, found = 0; ctrl->page_sizes[i]; i++) + if (ctrl->page_sizes[i] == cfg->page_size) { + page_size = i; + found = 1; + } + if (!found) { + dev_warn(ctrl->dev, "invalid page size %u\n", + cfg->page_size); + return -EINVAL; + } + } else { + page_size = ffs(cfg->page_size) - ffs(BRCMNAND_MIN_PAGESIZE); + } + + if (cfg->page_size < BRCMNAND_MIN_PAGESIZE || (ctrl->max_page_size && + cfg->page_size > ctrl->max_page_size)) { + dev_warn(ctrl->dev, "invalid page size %u\n", cfg->page_size); + return -EINVAL; + } + + if (fls64(cfg->device_size) < fls64(BRCMNAND_MIN_DEVSIZE)) { + dev_warn(ctrl->dev, "invalid device size 0x%llx\n", + (unsigned long long)cfg->device_size); + return -EINVAL; + } + device_size = fls64(cfg->device_size) - fls64(BRCMNAND_MIN_DEVSIZE); + + tmp = (cfg->blk_adr_bytes << 8) | + (cfg->col_adr_bytes << 12) | + (cfg->ful_adr_bytes << 16) | + (!!(cfg->device_width == 16) << 23) | + (device_size << 24); + if (cfg_offs == cfg_ext_offs) { + tmp |= (page_size << 20) | (block_size << 28); + nand_writereg(ctrl, cfg_offs, tmp); + } else { + nand_writereg(ctrl, cfg_offs, tmp); + tmp = page_size | (block_size << 4); + nand_writereg(ctrl, cfg_ext_offs, tmp); + } + + tmp = nand_readreg(ctrl, acc_control_offs); + tmp &= ~brcmnand_ecc_level_mask(ctrl); + tmp |= cfg->ecc_level << NAND_ACC_CONTROL_ECC_SHIFT; + tmp &= ~brcmnand_spare_area_mask(ctrl); + tmp |= cfg->spare_area_size; + nand_writereg(ctrl, acc_control_offs, tmp); + + brcmnand_set_sector_size_1k(host, cfg->sector_size_1k); + + /* threshold = ceil(BCH-level * 0.75) */ + brcmnand_wr_corr_thresh(host, DIV_ROUND_UP(chip->ecc.strength * 3, 4)); + + return 0; +} + +static void brcmnand_print_cfg(char *buf, struct brcmnand_cfg *cfg) +{ + buf += sprintf(buf, + "%lluMiB total, %uKiB blocks, %u%s pages, %uB OOB, %u-bit", + (unsigned long long)cfg->device_size >> 20, + cfg->block_size >> 10, + cfg->page_size >= 1024 ? cfg->page_size >> 10 : cfg->page_size, + cfg->page_size >= 1024 ? "KiB" : "B", + cfg->spare_area_size, cfg->device_width); + + /* Account for Hamming ECC and for BCH 512B vs 1KiB sectors */ + if (is_hamming_ecc(cfg)) + sprintf(buf, ", Hamming ECC"); + else if (cfg->sector_size_1k) + sprintf(buf, ", BCH-%u (1KiB sector)", cfg->ecc_level << 1); + else + sprintf(buf, ", BCH-%u\n", cfg->ecc_level); +} + +/* + * Minimum number of bytes to address a page. Calculated as: + * roundup(log2(size / page-size) / 8) + * + * NB: the following does not "round up" for non-power-of-2 'size'; but this is + * OK because many other things will break if 'size' is irregular... + */ +static inline int get_blk_adr_bytes(u64 size, u32 writesize) +{ + return ALIGN(ilog2(size) - ilog2(writesize), 8) >> 3; +} + +static int brcmnand_setup_dev(struct brcmnand_host *host) +{ + struct mtd_info *mtd = &host->mtd; + struct nand_chip *chip = &host->chip; + struct brcmnand_controller *ctrl = host->ctrl; + struct brcmnand_cfg *cfg = &host->hwcfg; + char msg[128]; + u32 offs, tmp, oob_sector; + int ret; + + memset(cfg, 0, sizeof(*cfg)); + + ret = of_property_read_u32(chip->dn, "brcm,nand-oob-sector-size", + &oob_sector); + if (ret) { + /* Use detected size */ + cfg->spare_area_size = mtd->oobsize / + (mtd->writesize >> FC_SHIFT); + } else { + cfg->spare_area_size = oob_sector; + } + if (cfg->spare_area_size > ctrl->max_oob) + cfg->spare_area_size = ctrl->max_oob; + /* + * Set oobsize to be consistent with controller's spare_area_size, as + * the rest is inaccessible. + */ + mtd->oobsize = cfg->spare_area_size * (mtd->writesize >> FC_SHIFT); + + cfg->device_size = mtd->size; + cfg->block_size = mtd->erasesize; + cfg->page_size = mtd->writesize; + cfg->device_width = (chip->options & NAND_BUSWIDTH_16) ? 16 : 8; + cfg->col_adr_bytes = 2; + cfg->blk_adr_bytes = get_blk_adr_bytes(mtd->size, mtd->writesize); + + switch (chip->ecc.size) { + case 512: + if (chip->ecc.strength == 1) /* Hamming */ + cfg->ecc_level = 15; + else + cfg->ecc_level = chip->ecc.strength; + cfg->sector_size_1k = 0; + break; + case 1024: + if (!(ctrl->features & BRCMNAND_HAS_1K_SECTORS)) { + dev_err(ctrl->dev, "1KB sectors not supported\n"); + return -EINVAL; + } + if (chip->ecc.strength & 0x1) { + dev_err(ctrl->dev, + "odd ECC not supported with 1KB sectors\n"); + return -EINVAL; + } + + cfg->ecc_level = chip->ecc.strength >> 1; + cfg->sector_size_1k = 1; + break; + default: + dev_err(ctrl->dev, "unsupported ECC size: %d\n", + chip->ecc.size); + return -EINVAL; + } + + cfg->ful_adr_bytes = cfg->blk_adr_bytes; + if (mtd->writesize > 512) + cfg->ful_adr_bytes += cfg->col_adr_bytes; + else + cfg->ful_adr_bytes += 1; + + ret = brcmnand_set_cfg(host, cfg); + if (ret) + return ret; + + brcmnand_set_ecc_enabled(host, 1); + + brcmnand_print_cfg(msg, cfg); + dev_info(ctrl->dev, "detected %s\n", msg); + + /* Configure ACC_CONTROL */ + offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL); + tmp = nand_readreg(ctrl, offs); + tmp &= ~ACC_CONTROL_PARTIAL_PAGE; + tmp &= ~ACC_CONTROL_RD_ERASED; + tmp &= ~ACC_CONTROL_FAST_PGM_RDIN; + if (ctrl->features & BRCMNAND_HAS_PREFETCH) { + /* + * FIXME: Flash DMA + prefetch may see spurious erased-page ECC + * errors + */ + if (has_flash_dma(ctrl)) + tmp &= ~ACC_CONTROL_PREFETCH; + else + tmp |= ACC_CONTROL_PREFETCH; + } + nand_writereg(ctrl, offs, tmp); + + return 0; +} + +static int brcmnand_init_cs(struct brcmnand_host *host) +{ + struct brcmnand_controller *ctrl = host->ctrl; + struct device_node *dn = host->of_node; + struct platform_device *pdev = host->pdev; + struct mtd_info *mtd; + struct nand_chip *chip; + int ret = 0; + struct mtd_part_parser_data ppdata = { .of_node = dn }; + + ret = of_property_read_u32(dn, "reg", &host->cs); + if (ret) { + dev_err(&pdev->dev, "can't get chip-select\n"); + return -ENXIO; + } + + mtd = &host->mtd; + chip = &host->chip; + + chip->dn = dn; + chip->priv = host; + mtd->priv = chip; + mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "brcmnand.%d", + host->cs); + mtd->owner = THIS_MODULE; + mtd->dev.parent = &pdev->dev; + + chip->IO_ADDR_R = (void __iomem *)0xdeadbeef; + chip->IO_ADDR_W = (void __iomem *)0xdeadbeef; + + chip->cmd_ctrl = brcmnand_cmd_ctrl; + chip->cmdfunc = brcmnand_cmdfunc; + chip->waitfunc = brcmnand_waitfunc; + chip->read_byte = brcmnand_read_byte; + chip->read_buf = brcmnand_read_buf; + chip->write_buf = brcmnand_write_buf; + + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.read_page = brcmnand_read_page; + chip->ecc.read_subpage = brcmnand_read_subpage; + chip->ecc.write_page = brcmnand_write_page; + chip->ecc.read_page_raw = brcmnand_read_page_raw; + chip->ecc.write_page_raw = brcmnand_write_page_raw; + chip->ecc.write_oob_raw = brcmnand_write_oob_raw; + chip->ecc.read_oob_raw = brcmnand_read_oob_raw; + chip->ecc.read_oob = brcmnand_read_oob; + chip->ecc.write_oob = brcmnand_write_oob; + + chip->controller = &ctrl->controller; + + if (nand_scan_ident(mtd, 1, NULL)) + return -ENXIO; + + chip->options |= NAND_NO_SUBPAGE_WRITE; + /* + * Avoid (for instance) kmap()'d buffers from JFFS2, which we can't DMA + * to/from, and have nand_base pass us a bounce buffer instead, as + * needed. + */ + chip->options |= NAND_USE_BOUNCE_BUFFER; + + if (of_get_nand_on_flash_bbt(dn)) + chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; + + if (brcmnand_setup_dev(host)) + return -ENXIO; + + chip->ecc.size = host->hwcfg.sector_size_1k ? 1024 : 512; + /* only use our internal HW threshold */ + mtd->bitflip_threshold = 1; + + chip->ecc.layout = brcmstb_choose_ecc_layout(host); + if (!chip->ecc.layout) + return -ENXIO; + + if (nand_scan_tail(mtd)) + return -ENXIO; + + return mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); +} + +static void brcmnand_save_restore_cs_config(struct brcmnand_host *host, + int restore) +{ + struct brcmnand_controller *ctrl = host->ctrl; + u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG); + u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_CFG_EXT); + u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_ACC_CONTROL); + u16 t1_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING1); + u16 t2_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING2); + + if (restore) { + nand_writereg(ctrl, cfg_offs, host->hwcfg.config); + if (cfg_offs != cfg_ext_offs) + nand_writereg(ctrl, cfg_ext_offs, + host->hwcfg.config_ext); + nand_writereg(ctrl, acc_control_offs, host->hwcfg.acc_control); + nand_writereg(ctrl, t1_offs, host->hwcfg.timing_1); + nand_writereg(ctrl, t2_offs, host->hwcfg.timing_2); + } else { + host->hwcfg.config = nand_readreg(ctrl, cfg_offs); + if (cfg_offs != cfg_ext_offs) + host->hwcfg.config_ext = + nand_readreg(ctrl, cfg_ext_offs); + host->hwcfg.acc_control = nand_readreg(ctrl, acc_control_offs); + host->hwcfg.timing_1 = nand_readreg(ctrl, t1_offs); + host->hwcfg.timing_2 = nand_readreg(ctrl, t2_offs); + } +} + +static int brcmnand_suspend(struct device *dev) +{ + struct brcmnand_controller *ctrl = dev_get_drvdata(dev); + struct brcmnand_host *host; + + list_for_each_entry(host, &ctrl->host_list, node) + brcmnand_save_restore_cs_config(host, 0); + + ctrl->nand_cs_nand_select = brcmnand_read_reg(ctrl, BRCMNAND_CS_SELECT); + ctrl->nand_cs_nand_xor = brcmnand_read_reg(ctrl, BRCMNAND_CS_XOR); + ctrl->corr_stat_threshold = + brcmnand_read_reg(ctrl, BRCMNAND_CORR_THRESHOLD); + + if (has_flash_dma(ctrl)) + ctrl->flash_dma_mode = flash_dma_readl(ctrl, FLASH_DMA_MODE); + + return 0; +} + +static int brcmnand_resume(struct device *dev) +{ + struct brcmnand_controller *ctrl = dev_get_drvdata(dev); + struct brcmnand_host *host; + + if (has_flash_dma(ctrl)) { + flash_dma_writel(ctrl, FLASH_DMA_MODE, ctrl->flash_dma_mode); + flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0); + } + + brcmnand_write_reg(ctrl, BRCMNAND_CS_SELECT, ctrl->nand_cs_nand_select); + brcmnand_write_reg(ctrl, BRCMNAND_CS_XOR, ctrl->nand_cs_nand_xor); + brcmnand_write_reg(ctrl, BRCMNAND_CORR_THRESHOLD, + ctrl->corr_stat_threshold); + + list_for_each_entry(host, &ctrl->host_list, node) { + struct mtd_info *mtd = &host->mtd; + struct nand_chip *chip = mtd->priv; + + brcmnand_save_restore_cs_config(host, 1); + + /* Reset the chip, required by some chips after power-up */ + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + } + + return 0; +} + +static const struct dev_pm_ops brcmnand_pm_ops = { + .suspend = brcmnand_suspend, + .resume = brcmnand_resume, +}; + +/*********************************************************************** + * Platform driver setup (per controller) + ***********************************************************************/ + +static int brcmnand_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *dn = dev->of_node, *child; + static struct brcmnand_controller *ctrl; + struct resource *res; + int ret; + + /* We only support device-tree instantiation */ + if (!dn) + return -ENODEV; + + ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + dev_set_drvdata(dev, ctrl); + ctrl->dev = dev; + + init_completion(&ctrl->done); + init_completion(&ctrl->dma_done); + spin_lock_init(&ctrl->controller.lock); + init_waitqueue_head(&ctrl->controller.wq); + INIT_LIST_HEAD(&ctrl->host_list); + + /* NAND register range */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ctrl->nand_base = devm_ioremap_resource(dev, res); + if (IS_ERR(ctrl->nand_base)) + return PTR_ERR(ctrl->nand_base); + + /* Initialize NAND revision */ + ret = brcmnand_revision_init(ctrl); + if (ret) + return ret; + + /* + * Most chips have this cache at a fixed offset within 'nand' block. + * Some must specify this region separately. + */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-cache"); + if (res) { + ctrl->nand_fc = devm_ioremap_resource(dev, res); + if (IS_ERR(ctrl->nand_fc)) + return PTR_ERR(ctrl->nand_fc); + } else { + ctrl->nand_fc = ctrl->nand_base + + ctrl->reg_offsets[BRCMNAND_FC_BASE]; + } + + /* FLASH_DMA */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "flash-dma"); + if (res) { + ctrl->flash_dma_base = devm_ioremap_resource(dev, res); + if (IS_ERR(ctrl->flash_dma_base)) + return PTR_ERR(ctrl->flash_dma_base); + + flash_dma_writel(ctrl, FLASH_DMA_MODE, 1); /* linked-list */ + flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0); + + /* Allocate descriptor(s) */ + ctrl->dma_desc = dmam_alloc_coherent(dev, + sizeof(*ctrl->dma_desc), + &ctrl->dma_pa, GFP_KERNEL); + if (!ctrl->dma_desc) + return -ENOMEM; + + ctrl->dma_irq = platform_get_irq(pdev, 1); + if ((int)ctrl->dma_irq < 0) { + dev_err(dev, "missing FLASH_DMA IRQ\n"); + return -ENODEV; + } + + ret = devm_request_irq(dev, ctrl->dma_irq, + brcmnand_dma_irq, 0, DRV_NAME, + ctrl); + if (ret < 0) { + dev_err(dev, "can't allocate IRQ %d: error %d\n", + ctrl->dma_irq, ret); + return ret; + } + + dev_info(dev, "enabling FLASH_DMA\n"); + } + + /* Disable automatic device ID config, direct addressing */ + brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT, + CS_SELECT_AUTO_DEVICE_ID_CFG | 0xff, 0, 0); + /* Disable XOR addressing */ + brcmnand_rmw_reg(ctrl, BRCMNAND_CS_XOR, 0xff, 0, 0); + + if (ctrl->features & BRCMNAND_HAS_WP) { + /* Permanently disable write protection */ + if (wp_on == 2) + brcmnand_set_wp(ctrl, false); + } else { + wp_on = 0; + } + + /* IRQ */ + ctrl->irq = platform_get_irq(pdev, 0); + if ((int)ctrl->irq < 0) { + dev_err(dev, "no IRQ defined\n"); + return -ENODEV; + } + + ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0, + DRV_NAME, ctrl); + if (ret < 0) { + dev_err(dev, "can't allocate IRQ %d: error %d\n", + ctrl->irq, ret); + return ret; + } + + for_each_available_child_of_node(dn, child) { + if (of_device_is_compatible(child, "brcm,nandcs")) { + struct brcmnand_host *host; + + host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); + if (!host) + return -ENOMEM; + host->pdev = pdev; + host->ctrl = ctrl; + host->of_node = child; + + ret = brcmnand_init_cs(host); + if (ret) + continue; /* Try all chip-selects */ + + list_add_tail(&host->node, &ctrl->host_list); + } + } + + /* No chip-selects could initialize properly */ + if (list_empty(&ctrl->host_list)) + return -ENODEV; + + return 0; +} + +static int brcmnand_remove(struct platform_device *pdev) +{ + struct brcmnand_controller *ctrl = dev_get_drvdata(&pdev->dev); + struct brcmnand_host *host; + + list_for_each_entry(host, &ctrl->host_list, node) + nand_release(&host->mtd); + + dev_set_drvdata(&pdev->dev, NULL); + + return 0; +} + +static const struct of_device_id brcmnand_of_match[] = { + { .compatible = "brcm,brcmnand-v4.0" }, + { .compatible = "brcm,brcmnand-v5.0" }, + { .compatible = "brcm,brcmnand-v6.0" }, + { .compatible = "brcm,brcmnand-v6.1" }, + { .compatible = "brcm,brcmnand-v7.0" }, + { .compatible = "brcm,brcmnand-v7.1" }, + {}, +}; +MODULE_DEVICE_TABLE(of, brcmnand_of_match); + +static struct platform_driver brcmstb_nand_driver = { + .probe = brcmnand_probe, + .remove = brcmnand_remove, + .driver = { + .name = DRV_NAME, + .pm = &brcmnand_pm_ops, + .of_match_table = of_match_ptr(brcmnand_of_match), + } +}; +module_platform_driver(brcmstb_nand_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Kevin Cernekee"); +MODULE_AUTHOR("Brian Norris"); +MODULE_DESCRIPTION("NAND driver for Broadcom STB chips"); +MODULE_ALIAS("platform:brcmnand"); -- 1.9.1