From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id AF68FC54EBE for ; Fri, 13 Jan 2023 10:40:55 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id BD1FC94B1; Fri, 13 Jan 2023 11:40:03 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz BD1FC94B1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1673606453; bh=gAbgUKd4nIA6xrisijjj+XEvBGVSjARmn0RXUjCHFXo=; h=From:To:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: Cc:From; b=uCbcPsBvZA8tPciAJjNUZlFKkVafxV7HafoLitFBCONdQLKN63LRQLdb5Cj4ThPQ1 T7NOOvBIinpdXZJBtMwkB53sM26/NXZpP+Mshq0AsimHQYn1UKwc0Ozw5WsvgZvS45 34Fj7FEUE2SohKGdIokRJ5S4iY1oMqmjXFcG7GfQ= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 70779F8057D; Fri, 13 Jan 2023 11:38:39 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id A8833F80564; Fri, 13 Jan 2023 11:38:37 +0100 (CET) Received: from relay9-d.mail.gandi.net (relay9-d.mail.gandi.net [IPv6:2001:4b98:dc4:8::229]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 963D9F80564 for ; Fri, 13 Jan 2023 11:38:29 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 963D9F80564 Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key, unprotected) header.d=bootlin.com header.i=@bootlin.com header.a=rsa-sha256 header.s=gm1 header.b=ZgoI1XZM Received: (Authenticated sender: herve.codina@bootlin.com) by mail.gandi.net (Postfix) with ESMTPA id D53B5FF818; Fri, 13 Jan 2023 10:38:24 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1673606308; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=H7KIwJYPQrAhWdDUPPixMuaTGE+4u6gAL2K6MQ/lFPI=; b=ZgoI1XZMLKS5Iby0R/H2lYWFdbeiC+jf5zM4HEifY2DHdwpmV655+CdeqzgrBQzoO9EUUJ SGKbLVckraiFG7Qg6nR3e2Yt4lmgFgKb0cV6vdFhF2Wuq2N18JlkkvafMImq5mme03cSve zMRscQ4L4qcQ15P9L7v+wawo2FpGfgTkfpwFFRvGuudFuDsNMpEuvxhXinWJHsCGmfWV9U wJ3V9Nvmvh+4GqHly1lbO1LbZsqgAHHQPamEzOPMCNXVT1kyTVo2CFRel2qkBvwtqivQsY HqpWo1i4090sTzowRaD3/vkzzcT+DSe34Ok1so89wL0E8W6SDHTwJgjdJP1vIA== From: Herve Codina To: Herve Codina , Li Yang , Rob Herring , Krzysztof Kozlowski , Liam Girdwood , Mark Brown , Christophe Leroy , Michael Ellerman , Nicholas Piggin , Qiang Zhao , Jaroslav Kysela , Takashi Iwai , Shengjiu Wang , Xiubo Li , Fabio Estevam , Nicolin Chen Subject: [PATCH v3 06/10] soc: fsl: cmp1: Add support for QMC Date: Fri, 13 Jan 2023 11:37:55 +0100 Message-Id: <20230113103759.327698-7-herve.codina@bootlin.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20230113103759.327698-1-herve.codina@bootlin.com> References: <20230113103759.327698-1-herve.codina@bootlin.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org, linux-kernel@vger.kernel.org, Thomas Petazzoni , linuxppc-dev@lists.ozlabs.org, linux-arm-kernel@lists.infradead.org Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" The QMC (QUICC Multichannel Controller) emulates up to 64 channels within one serial controller using the same TDM physical interface routed from the TSA. It is available in some PowerQUICC SoC such as the MPC885 or MPC866. It is also available on some Quicc Engine SoCs. This current version support CPM1 SoCs only and some enhancement are needed to support Quicc Engine SoCs. Signed-off-by: Herve Codina --- drivers/soc/fsl/qe/Kconfig | 12 + drivers/soc/fsl/qe/Makefile | 1 + drivers/soc/fsl/qe/qmc.c | 1531 +++++++++++++++++++++++++++++++++++ include/soc/fsl/qe/qmc.h | 71 ++ 4 files changed, 1615 insertions(+) create mode 100644 drivers/soc/fsl/qe/qmc.c create mode 100644 include/soc/fsl/qe/qmc.h diff --git a/drivers/soc/fsl/qe/Kconfig b/drivers/soc/fsl/qe/Kconfig index 60ec11c9f4d9..25b218351ae3 100644 --- a/drivers/soc/fsl/qe/Kconfig +++ b/drivers/soc/fsl/qe/Kconfig @@ -44,6 +44,18 @@ config CPM_TSA This option enables support for this controller +config CPM_QMC + tristate "CPM QMC support" + depends on OF && HAS_IOMEM + depends on CPM1 || (PPC && COMPILE_TEST) + depends on CPM_TSA + help + Freescale CPM QUICC Multichannel Controller + (QMC) + + This option enables support for this + controller + config QE_TDM bool default y if FSL_UCC_HDLC diff --git a/drivers/soc/fsl/qe/Makefile b/drivers/soc/fsl/qe/Makefile index 45c961acc81b..ec8506e13113 100644 --- a/drivers/soc/fsl/qe/Makefile +++ b/drivers/soc/fsl/qe/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_QUICC_ENGINE)+= qe.o qe_common.o qe_ic.o qe_io.o obj-$(CONFIG_CPM) += qe_common.o obj-$(CONFIG_CPM_TSA) += tsa.o +obj-$(CONFIG_CPM_QMC) += qmc.o obj-$(CONFIG_UCC) += ucc.o obj-$(CONFIG_UCC_SLOW) += ucc_slow.o obj-$(CONFIG_UCC_FAST) += ucc_fast.o diff --git a/drivers/soc/fsl/qe/qmc.c b/drivers/soc/fsl/qe/qmc.c new file mode 100644 index 000000000000..85a99538bde7 --- /dev/null +++ b/drivers/soc/fsl/qe/qmc.c @@ -0,0 +1,1531 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * QMC driver + * + * Copyright 2022 CS GROUP France + * + * Author: Herve Codina + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tsa.h" + +/* SCC general mode register high (32 bits) */ +#define SCC_GSMRL 0x00 +#define SCC_GSMRL_ENR (1 << 5) +#define SCC_GSMRL_ENT (1 << 4) +#define SCC_GSMRL_MODE_QMC (0x0A << 0) + +/* SCC general mode register low (32 bits) */ +#define SCC_GSMRH 0x04 +#define SCC_GSMRH_CTSS (1 << 7) +#define SCC_GSMRH_CDS (1 << 8) +#define SCC_GSMRH_CTSP (1 << 9) +#define SCC_GSMRH_CDP (1 << 10) + +/* SCC event register (16 bits) */ +#define SCC_SCCE 0x10 +#define SCC_SCCE_IQOV (1 << 3) +#define SCC_SCCE_GINT (1 << 2) +#define SCC_SCCE_GUN (1 << 1) +#define SCC_SCCE_GOV (1 << 0) + +/* SCC mask register (16 bits) */ +#define SCC_SCCM 0x14 +/* Multichannel base pointer (32 bits) */ +#define QMC_GBL_MCBASE 0x00 +/* Multichannel controller state (16 bits) */ +#define QMC_GBL_QMCSTATE 0x04 +/* Maximum receive buffer length (16 bits) */ +#define QMC_GBL_MRBLR 0x06 +/* Tx time-slot assignment table pointer (16 bits) */ +#define QMC_GBL_TX_S_PTR 0x08 +/* Rx pointer (16 bits) */ +#define QMC_GBL_RXPTR 0x0A +/* Global receive frame threshold (16 bits) */ +#define QMC_GBL_GRFTHR 0x0C +/* Global receive frame count (16 bits) */ +#define QMC_GBL_GRFCNT 0x0E +/* Multichannel interrupt base address (32 bits) */ +#define QMC_GBL_INTBASE 0x10 +/* Multichannel interrupt pointer (32 bits) */ +#define QMC_GBL_INTPTR 0x14 +/* Rx time-slot assignment table pointer (16 bits) */ +#define QMC_GBL_RX_S_PTR 0x18 +/* Tx pointer (16 bits) */ +#define QMC_GBL_TXPTR 0x1A +/* CRC constant (32 bits) */ +#define QMC_GBL_C_MASK32 0x1C +/* Time slot assignment table Rx (32 x 16 bits) */ +#define QMC_GBL_TSATRX 0x20 +/* Time slot assignment table Tx (32 x 16 bits) */ +#define QMC_GBL_TSATTX 0x60 +/* CRC constant (16 bits) */ +#define QMC_GBL_C_MASK16 0xA0 + +/* TSA entry (16bit entry in TSATRX and TSATTX) */ +#define QMC_TSA_VALID (1 << 15) +#define QMC_TSA_WRAP (1 << 14) +#define QMC_TSA_MASK (0x303F) +#define QMC_TSA_CHANNEL(x) ((x) << 6) + +/* Tx buffer descriptor base address (16 bits, offset from MCBASE) */ +#define QMC_SPE_TBASE 0x00 + +/* Channel mode register (16 bits) */ +#define QMC_SPE_CHAMR 0x02 +#define QMC_SPE_CHAMR_MODE_HDLC (1 << 15) +#define QMC_SPE_CHAMR_MODE_TRANSP ((0 << 15) | (1 << 13)) +#define QMC_SPE_CHAMR_ENT (1 << 12) +#define QMC_SPE_CHAMR_POL (1 << 8) +#define QMC_SPE_CHAMR_HDLC_IDLM (1 << 13) +#define QMC_SPE_CHAMR_HDLC_CRC (1 << 7) +#define QMC_SPE_CHAMR_HDLC_NOF (0x0f << 0) +#define QMC_SPE_CHAMR_TRANSP_RD (1 << 14) +#define QMC_SPE_CHAMR_TRANSP_SYNC (1 << 10) + +/* Tx internal state (32 bits) */ +#define QMC_SPE_TSTATE 0x04 +/* Tx buffer descriptor pointer (16 bits) */ +#define QMC_SPE_TBPTR 0x0C +/* Zero-insertion state (32 bits) */ +#define QMC_SPE_ZISTATE 0x14 +/* Channel’s interrupt mask flags (16 bits) */ +#define QMC_SPE_INTMSK 0x1C +/* Rx buffer descriptor base address (16 bits, offset from MCBASE) */ +#define QMC_SPE_RBASE 0x20 +/* HDLC: Maximum frame length register (16 bits) */ +#define QMC_SPE_MFLR 0x22 +/* TRANSPARENT: Transparent maximum receive length (16 bits) */ +#define QMC_SPE_TMRBLR 0x22 +/* Rx internal state (32 bits) */ +#define QMC_SPE_RSTATE 0x24 +/* Rx buffer descriptor pointer (16 bits) */ +#define QMC_SPE_RBPTR 0x2C +/* Packs 4 bytes to 1 long word before writing to buffer (32 bits) */ +#define QMC_SPE_RPACK 0x30 +/* Zero deletion state (32 bits) */ +#define QMC_SPE_ZDSTATE 0x34 + +/* Transparent synchronization (16 bits) */ +#define QMC_SPE_TRNSYNC 0x3C +#define QMC_SPE_TRNSYNC_RX(x) ((x) << 8) +#define QMC_SPE_TRNSYNC_TX(x) ((x) << 0) + +/* Interrupt related registers bits */ +#define QMC_INT_V (1 << 15) +#define QMC_INT_W (1 << 14) +#define QMC_INT_NID (1 << 13) +#define QMC_INT_IDL (1 << 12) +#define QMC_INT_GET_CHANNEL(x) (((x) & 0x0FC0) >> 6) +#define QMC_INT_MRF (1 << 5) +#define QMC_INT_UN (1 << 4) +#define QMC_INT_RXF (1 << 3) +#define QMC_INT_BSY (1 << 2) +#define QMC_INT_TXB (1 << 1) +#define QMC_INT_RXB (1 << 0) + +/* BD related registers bits */ +#define QMC_BD_RX_E (1 << 15) +#define QMC_BD_RX_W (1 << 13) +#define QMC_BD_RX_I (1 << 12) +#define QMC_BD_RX_L (1 << 11) +#define QMC_BD_RX_F (1 << 10) +#define QMC_BD_RX_CM (1 << 9) +#define QMC_BD_RX_UB (1 << 7) +#define QMC_BD_RX_LG (1 << 5) +#define QMC_BD_RX_NO (1 << 4) +#define QMC_BD_RX_AB (1 << 3) +#define QMC_BD_RX_CR (1 << 2) + +#define QMC_BD_TX_R (1 << 15) +#define QMC_BD_TX_W (1 << 13) +#define QMC_BD_TX_I (1 << 12) +#define QMC_BD_TX_L (1 << 11) +#define QMC_BD_TX_TC (1 << 10) +#define QMC_BD_TX_CM (1 << 9) +#define QMC_BD_TX_UB (1 << 7) +#define QMC_BD_TX_PAD (0x0f << 0) + +/* Numbers of BDs and interrupt items */ +#define QMC_NB_TXBDS 8 +#define QMC_NB_RXBDS 8 +#define QMC_NB_INTS 128 + +struct qmc_xfer_desc { + union { + void (*tx_complete)(void *context); + void (*rx_complete)(void *context, size_t length); + }; + void *context; +}; + +struct qmc_chan { + struct list_head list; + unsigned int id; + struct qmc *qmc; + void *__iomem s_param; + enum qmc_mode mode; + u64 tx_ts_mask; + u64 rx_ts_mask; + bool is_reverse_data; + + spinlock_t tx_lock; + cbd_t __iomem *txbds; + cbd_t __iomem *txbd_free; + cbd_t __iomem *txbd_done; + struct qmc_xfer_desc tx_desc[QMC_NB_TXBDS]; + u64 nb_tx_underrun; + bool is_tx_stopped; + + spinlock_t rx_lock; + cbd_t __iomem *rxbds; + cbd_t __iomem *rxbd_free; + cbd_t __iomem *rxbd_done; + struct qmc_xfer_desc rx_desc[QMC_NB_RXBDS]; + u64 nb_rx_busy; + int rx_pending; + bool is_rx_halted; + bool is_rx_stopped; +}; + +struct qmc { + struct device *dev; + struct tsa *tsa; + void *__iomem scc_regs; + void *__iomem scc_pram; + void *__iomem dpram; + u16 scc_pram_offset; + unsigned int tsa_cell_id; + cbd_t __iomem *bd_table; + dma_addr_t bd_dma_addr; + size_t bd_size; + u16 __iomem *int_table; + u16 __iomem *int_curr; + dma_addr_t int_dma_addr; + size_t int_size; + struct list_head chan_head; + struct qmc_chan *chans[64]; +}; + +static inline void qmc_write16(void *__iomem addr, u16 val) +{ + iowrite16be(val, addr); +} + +static inline u16 qmc_read16(void *__iomem addr) +{ + return ioread16be(addr); +} + +static inline void qmc_setbits16(void *__iomem addr, u16 set) +{ + qmc_write16(addr, qmc_read16(addr) | set); +} + +static inline void qmc_clrbits16(void *__iomem addr, u16 clr) +{ + qmc_write16(addr, qmc_read16(addr) & ~clr); +} + +static inline void qmc_write32(void *__iomem addr, u32 val) +{ + iowrite32be(val, addr); +} + +static inline u32 qmc_read32(void *__iomem addr) +{ + return ioread32be(addr); +} + +static inline void qmc_setbits32(void *__iomem addr, u32 set) +{ + qmc_write32(addr, qmc_read32(addr) | set); +} + + +int qmc_chan_get_info(struct qmc_chan *chan, struct qmc_chan_info *info) +{ + struct tsa_cell_info tsa_info; + int ret; + + /* Retrieve info from TSA related cell */ + ret = tsa_get_info(chan->qmc->tsa, chan->qmc->tsa_cell_id, &tsa_info); + if (ret) + return ret; + + info->mode = chan->mode; + info->rx_fs_rate = tsa_info.rx_fs_rate; + info->rx_bit_rate = tsa_info.rx_bit_rate; + info->nb_tx_ts = hweight64(chan->tx_ts_mask); + info->tx_fs_rate = tsa_info.tx_fs_rate; + info->tx_bit_rate = tsa_info.tx_bit_rate; + info->nb_rx_ts = hweight64(chan->rx_ts_mask); + + return 0; +} +EXPORT_SYMBOL(qmc_chan_get_info); + +int qmc_chan_set_param(struct qmc_chan *chan, const struct qmc_chan_param *param) +{ + if (param->mode != chan->mode) + return -EINVAL; + + switch (param->mode) { + case QMC_HDLC: + if ((param->hdlc.max_rx_buf_size % 4) || + (param->hdlc.max_rx_buf_size < 8)) + return -EINVAL; + + qmc_write16(chan->qmc->scc_pram + QMC_GBL_MRBLR, + param->hdlc.max_rx_buf_size - 8); + qmc_write16(chan->s_param + QMC_SPE_MFLR, + param->hdlc.max_rx_frame_size); + if (param->hdlc.is_crc32) { + qmc_setbits16(chan->s_param + QMC_SPE_CHAMR, + QMC_SPE_CHAMR_HDLC_CRC); + } else { + qmc_clrbits16(chan->s_param + QMC_SPE_CHAMR, + QMC_SPE_CHAMR_HDLC_CRC); + } + break; + + case QMC_TRANSPARENT: + qmc_write16(chan->s_param + QMC_SPE_TMRBLR, + param->transp.max_rx_buf_size); + break; + + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(qmc_chan_set_param); + +int qmc_chan_write_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length, + void (*complete)(void *context), void *context) +{ + struct qmc_xfer_desc *xfer_desc; + unsigned long flags; + cbd_t *__iomem bd; + u16 ctrl; + int ret; + + /* R bit UB bit + * 0 0 : The BD is free + * 1 1 : The BD is in used, waiting for transfer + * 0 1 : The BD is in used, waiting for completion + * 1 0 : Should not append + */ + + spin_lock_irqsave(&chan->tx_lock, flags); + bd = chan->txbd_free; + + ctrl = qmc_read16(&bd->cbd_sc); + if (ctrl & (QMC_BD_TX_R | QMC_BD_TX_UB)) { + /* We are full ... */ + ret = -EBUSY; + goto end; + } + + qmc_write16(&bd->cbd_datlen, length); + qmc_write32(&bd->cbd_bufaddr, addr); + + xfer_desc = &chan->tx_desc[bd - chan->txbds]; + xfer_desc->tx_complete = complete; + xfer_desc->context = context; + + /* Activate the descriptor */ + ctrl |= (QMC_BD_TX_R | QMC_BD_TX_UB); + wmb(); /* Be sure to flush the descriptor before control update */ + qmc_write16(&bd->cbd_sc, ctrl); + + if (!chan->is_tx_stopped) + qmc_setbits16(chan->s_param + QMC_SPE_CHAMR, QMC_SPE_CHAMR_POL); + + if (ctrl & QMC_BD_TX_W) + chan->txbd_free = chan->txbds; + else + chan->txbd_free++; + + ret = 0; + +end: + spin_unlock_irqrestore(&chan->tx_lock, flags); + return ret; +} +EXPORT_SYMBOL(qmc_chan_write_submit); + +static void qmc_chan_write_done(struct qmc_chan *chan) +{ + struct qmc_xfer_desc *xfer_desc; + void (*complete)(void *context); + unsigned long flags; + void *context; + cbd_t *__iomem bd; + u16 ctrl; + + /* R bit UB bit + * 0 0 : The BD is free + * 1 1 : The BD is in used, waiting for transfer + * 0 1 : The BD is in used, waiting for completion + * 1 0 : Should not append + */ + + spin_lock_irqsave(&chan->tx_lock, flags); + bd = chan->txbd_done; + + ctrl = qmc_read16(&bd->cbd_sc); + while (!(ctrl & QMC_BD_TX_R)) { + if (!(ctrl & QMC_BD_TX_UB)) + goto end; + + xfer_desc = &chan->tx_desc[bd - chan->txbds]; + complete = xfer_desc->tx_complete; + context = xfer_desc->context; + xfer_desc->tx_complete = NULL; + xfer_desc->context = NULL; + + qmc_write16(&bd->cbd_sc, ctrl & ~QMC_BD_TX_UB); + + if (ctrl & QMC_BD_TX_W) + chan->txbd_done = chan->txbds; + else + chan->txbd_done++; + + if (complete) { + spin_unlock_irqrestore(&chan->tx_lock, flags); + complete(context); + spin_lock_irqsave(&chan->tx_lock, flags); + } + + bd = chan->txbd_done; + ctrl = qmc_read16(&bd->cbd_sc); + } + +end: + spin_unlock_irqrestore(&chan->tx_lock, flags); +} + +int qmc_chan_read_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length, + void (*complete)(void *context, size_t length), void *context) +{ + struct qmc_xfer_desc *xfer_desc; + unsigned long flags; + cbd_t *__iomem bd; + u16 ctrl; + int ret; + + /* E bit UB bit + * 0 0 : The BD is free + * 1 1 : The BD is in used, waiting for transfer + * 0 1 : The BD is in used, waiting for completion + * 1 0 : Should not append + */ + + spin_lock_irqsave(&chan->rx_lock, flags); + bd = chan->rxbd_free; + + ctrl = qmc_read16(&bd->cbd_sc); + if (ctrl & (QMC_BD_RX_E | QMC_BD_RX_UB)) { + /* We are full ... */ + ret = -EBUSY; + goto end; + } + + qmc_write16(&bd->cbd_datlen, 0); /* data length is updated by the QMC */ + qmc_write32(&bd->cbd_bufaddr, addr); + + xfer_desc = &chan->rx_desc[bd - chan->rxbds]; + xfer_desc->rx_complete = complete; + xfer_desc->context = context; + + /* Activate the descriptor */ + ctrl |= (QMC_BD_RX_E | QMC_BD_RX_UB); + wmb(); /* Be sure to flush data before descriptor activation */ + qmc_write16(&bd->cbd_sc, ctrl); + + /* Restart receiver if needed */ + if (chan->is_rx_halted && !chan->is_rx_stopped) { + /* Restart receiver */ + if (chan->mode == QMC_TRANSPARENT) + qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x18000080); + else + qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x00000080); + qmc_write32(chan->s_param + QMC_SPE_RSTATE, 0x31000000); + chan->is_rx_halted = false; + } + chan->rx_pending++; + + if (ctrl & QMC_BD_RX_W) + chan->rxbd_free = chan->rxbds; + else + chan->rxbd_free++; + + ret = 0; +end: + spin_unlock_irqrestore(&chan->rx_lock, flags); + return ret; +} +EXPORT_SYMBOL(qmc_chan_read_submit); + +static void qmc_chan_read_done(struct qmc_chan *chan) +{ + void (*complete)(void *context, size_t size); + struct qmc_xfer_desc *xfer_desc; + unsigned long flags; + cbd_t *__iomem bd; + void *context; + u16 datalen; + u16 ctrl; + + /* E bit UB bit + * 0 0 : The BD is free + * 1 1 : The BD is in used, waiting for transfer + * 0 1 : The BD is in used, waiting for completion + * 1 0 : Should not append + */ + + spin_lock_irqsave(&chan->rx_lock, flags); + bd = chan->rxbd_done; + + ctrl = qmc_read16(&bd->cbd_sc); + while (!(ctrl & QMC_BD_RX_E)) { + if (!(ctrl & QMC_BD_RX_UB)) + goto end; + + xfer_desc = &chan->rx_desc[bd - chan->rxbds]; + complete = xfer_desc->rx_complete; + context = xfer_desc->context; + xfer_desc->rx_complete = NULL; + xfer_desc->context = NULL; + + datalen = qmc_read16(&bd->cbd_datlen); + qmc_write16(&bd->cbd_sc, ctrl & ~QMC_BD_RX_UB); + + if (ctrl & QMC_BD_RX_W) + chan->rxbd_done = chan->rxbds; + else + chan->rxbd_done++; + + chan->rx_pending--; + + if (complete) { + spin_unlock_irqrestore(&chan->rx_lock, flags); + complete(context, datalen); + spin_lock_irqsave(&chan->rx_lock, flags); + } + + bd = chan->rxbd_done; + ctrl = qmc_read16(&bd->cbd_sc); + } + +end: + spin_unlock_irqrestore(&chan->rx_lock, flags); +} + +static int qmc_chan_command(struct qmc_chan *chan, u8 qmc_opcode) +{ + return cpm_command(chan->id << 2, (qmc_opcode << 4) | 0x0E); +} + +static int qmc_chan_stop_rx(struct qmc_chan *chan) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&chan->rx_lock, flags); + + /* Send STOP RECEIVE command */ + ret = qmc_chan_command(chan, 0x0); + if (ret) { + dev_err(chan->qmc->dev, "chan %u: Send STOP RECEIVE failed (%d)\n", + chan->id, ret); + goto end; + } + + chan->is_rx_stopped = true; + +end: + spin_unlock_irqrestore(&chan->rx_lock, flags); + return ret; +} + +static int qmc_chan_stop_tx(struct qmc_chan *chan) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&chan->tx_lock, flags); + + /* Send STOP TRANSMIT command */ + ret = qmc_chan_command(chan, 0x1); + if (ret) { + dev_err(chan->qmc->dev, "chan %u: Send STOP TRANSMIT failed (%d)\n", + chan->id, ret); + goto end; + } + + chan->is_tx_stopped = true; + +end: + spin_unlock_irqrestore(&chan->tx_lock, flags); + return ret; +} + +int qmc_chan_stop(struct qmc_chan *chan, int direction) +{ + int ret; + + if (direction & QMC_CHAN_READ) { + ret = qmc_chan_stop_rx(chan); + if (ret) + return ret; + } + + if (direction & QMC_CHAN_WRITE) { + ret = qmc_chan_stop_tx(chan); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL(qmc_chan_stop); + +static void qmc_chan_start_rx(struct qmc_chan *chan) +{ + unsigned long flags; + + spin_lock_irqsave(&chan->rx_lock, flags); + + /* Restart the receiver */ + if (chan->mode == QMC_TRANSPARENT) + qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x18000080); + else + qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x00000080); + qmc_write32(chan->s_param + QMC_SPE_RSTATE, 0x31000000); + chan->is_rx_halted = false; + + chan->is_rx_stopped = false; + + spin_unlock_irqrestore(&chan->rx_lock, flags); +} + +static void qmc_chan_start_tx(struct qmc_chan *chan) +{ + unsigned long flags; + + spin_lock_irqsave(&chan->tx_lock, flags); + + /* Enable channel transmitter as it could be disabled if + * qmc_chan_reset() was called + */ + qmc_setbits16(chan->s_param + QMC_SPE_CHAMR, QMC_SPE_CHAMR_ENT); + + /* Set the POL bit in the channel mode register */ + qmc_setbits16(chan->s_param + QMC_SPE_CHAMR, QMC_SPE_CHAMR_POL); + + chan->is_tx_stopped = false; + + spin_unlock_irqrestore(&chan->tx_lock, flags); +} + +int qmc_chan_start(struct qmc_chan *chan, int direction) +{ + if (direction & QMC_CHAN_READ) + qmc_chan_start_rx(chan); + + if (direction & QMC_CHAN_WRITE) + qmc_chan_start_tx(chan); + + return 0; +} +EXPORT_SYMBOL(qmc_chan_start); + +static void qmc_chan_reset_rx(struct qmc_chan *chan) +{ + struct qmc_xfer_desc *xfer_desc; + unsigned long flags; + cbd_t *__iomem bd; + u16 ctrl; + + spin_lock_irqsave(&chan->rx_lock, flags); + bd = chan->rxbds; + do { + ctrl = qmc_read16(&bd->cbd_sc); + qmc_write16(&bd->cbd_sc, ctrl & ~(QMC_BD_RX_UB | QMC_BD_RX_E)); + + xfer_desc = &chan->rx_desc[bd - chan->rxbds]; + xfer_desc->rx_complete = NULL; + xfer_desc->context = NULL; + + bd++; + } while (!(ctrl & QMC_BD_RX_W)); + + chan->rxbd_free = chan->rxbds; + chan->rxbd_done = chan->rxbds; + qmc_write16(chan->s_param + QMC_SPE_RBPTR, + qmc_read16(chan->s_param + QMC_SPE_RBASE)); + + chan->rx_pending = 0; + chan->is_rx_stopped = false; + + spin_unlock_irqrestore(&chan->rx_lock, flags); +} + +static void qmc_chan_reset_tx(struct qmc_chan *chan) +{ + struct qmc_xfer_desc *xfer_desc; + unsigned long flags; + cbd_t *__iomem bd; + u16 ctrl; + + spin_lock_irqsave(&chan->tx_lock, flags); + + /* Disable transmitter. It will be re-enable on qmc_chan_start() */ + qmc_clrbits16(chan->s_param + QMC_SPE_CHAMR, QMC_SPE_CHAMR_ENT); + + bd = chan->txbds; + do { + ctrl = qmc_read16(&bd->cbd_sc); + qmc_write16(&bd->cbd_sc, ctrl & ~(QMC_BD_TX_UB | QMC_BD_TX_R)); + + xfer_desc = &chan->tx_desc[bd - chan->txbds]; + xfer_desc->tx_complete = NULL; + xfer_desc->context = NULL; + + bd++; + } while (!(ctrl & QMC_BD_TX_W)); + + chan->txbd_free = chan->txbds; + chan->txbd_done = chan->txbds; + qmc_write16(chan->s_param + QMC_SPE_TBPTR, + qmc_read16(chan->s_param + QMC_SPE_TBASE)); + + /* Reset TSTATE and ZISTATE to their initial value */ + qmc_write32(chan->s_param + QMC_SPE_TSTATE, 0x30000000); + qmc_write32(chan->s_param + QMC_SPE_ZISTATE, 0x00000100); + + spin_unlock_irqrestore(&chan->tx_lock, flags); +} + +int qmc_chan_reset(struct qmc_chan *chan, int direction) +{ + if (direction & QMC_CHAN_READ) + qmc_chan_reset_rx(chan); + + if (direction & QMC_CHAN_WRITE) + qmc_chan_reset_tx(chan); + + return 0; +} +EXPORT_SYMBOL(qmc_chan_reset); + +static int qmc_check_chans(struct qmc *qmc) +{ + struct tsa_cell_info info; + bool is_one_table = false; + struct qmc_chan *chan; + u64 tx_ts_mask = 0; + u64 rx_ts_mask = 0; + u64 tx_ts_assigned_mask; + u64 rx_ts_assigned_mask; + int ret; + + /* Retrieve info from TSA related cell */ + ret = tsa_get_info(qmc->tsa, qmc->tsa_cell_id, &info); + if (ret) + return ret; + + /* If more than 32 TS are assigned to this cell, one common table + * is used for Tx and Rx and so masks must be equal for all channels. + */ + if ((info.nb_tx_ts > 32) || (info.nb_rx_ts > 32)) { + if (info.nb_tx_ts != info.nb_rx_ts) { + dev_err(qmc->dev, "Number of TSA Tx/Rx TS assigned are not equal\n"); + return -EINVAL; + } + is_one_table = true; + } + + + tx_ts_assigned_mask = (((u64)1) << info.nb_tx_ts) - 1; + rx_ts_assigned_mask = (((u64)1) << info.nb_rx_ts) - 1; + + list_for_each_entry(chan, &qmc->chan_head, list) { + if (chan->tx_ts_mask > tx_ts_assigned_mask) { + dev_err(qmc->dev, "chan %u uses TSA unassigned Tx TS\n", chan->id); + return -EINVAL; + } + if (tx_ts_mask & chan->tx_ts_mask) { + dev_err(qmc->dev, "chan %u uses an already used Tx TS\n", chan->id); + return -EINVAL; + } + + if (chan->rx_ts_mask > rx_ts_assigned_mask) { + dev_err(qmc->dev, "chan %u uses TSA unassigned Rx TS\n", chan->id); + return -EINVAL; + } + if (rx_ts_mask & chan->rx_ts_mask) { + dev_err(qmc->dev, "chan %u uses an already used Rx TS\n", chan->id); + return -EINVAL; + } + + if (is_one_table && (chan->tx_ts_mask != chan->rx_ts_mask)) { + dev_err(qmc->dev, "chan %u uses different Rx and Tx TS\n", chan->id); + return -EINVAL; + } + + tx_ts_mask |= chan->tx_ts_mask; + rx_ts_mask |= chan->rx_ts_mask; + } + + return 0; +} + +static unsigned int qmc_nb_chans(struct qmc *qmc) +{ + unsigned int count = 0; + struct qmc_chan *chan; + + list_for_each_entry(chan, &qmc->chan_head, list) + count++; + + return count; +} + +static int qmc_of_parse_chans(struct qmc *qmc, struct device_node *np) +{ + struct device_node *chan_np; + struct qmc_chan *chan; + const char *mode; + u32 chan_id; + u64 ts_mask; + int ret; + + for_each_available_child_of_node(np, chan_np) { + ret = of_property_read_u32(chan_np, "reg", &chan_id); + if (ret) { + dev_err(qmc->dev, "%pOF: failed to read reg\n", chan_np); + of_node_put(chan_np); + return ret; + } + if (chan_id > 63) { + dev_err(qmc->dev, "%pOF: Invalid chan_id\n", chan_np); + of_node_put(chan_np); + return -EINVAL; + } + + chan = devm_kzalloc(qmc->dev, sizeof(*chan), GFP_KERNEL); + if (!chan) { + of_node_put(chan_np); + return -ENOMEM; + } + + chan->id = chan_id; + spin_lock_init(&chan->rx_lock); + spin_lock_init(&chan->tx_lock); + + ret = of_property_read_u64(chan_np, "fsl,tx-ts-mask", &ts_mask); + if (ret) { + dev_err(qmc->dev, "%pOF: failed to read fsl,tx-ts-mask\n", + chan_np); + of_node_put(chan_np); + return ret; + } + chan->tx_ts_mask = ts_mask; + + ret = of_property_read_u64(chan_np, "fsl,rx-ts-mask", &ts_mask); + if (ret) { + dev_err(qmc->dev, "%pOF: failed to read fsl,rx-ts-mask\n", + chan_np); + of_node_put(chan_np); + return ret; + } + chan->rx_ts_mask = ts_mask; + + mode = "transparent"; + ret = of_property_read_string(chan_np, "fsl,mode", &mode); + if (ret && ret != -EINVAL) { + dev_err(qmc->dev, "%pOF: failed to read fsl,mode\n", chan_np); + of_node_put(chan_np); + return ret; + } + if (!strcmp(mode, "transparent")) { + chan->mode = QMC_TRANSPARENT; + } else if (!strcmp(mode, "hdlc")) { + chan->mode = QMC_HDLC; + } else { + dev_err(qmc->dev, "%pOF: Invalid fsl,mode (%s)\n", chan_np, + mode); + of_node_put(chan_np); + return -EINVAL; + } + + chan->is_reverse_data = of_property_read_bool(chan_np, + "fsl,reverse-data"); + + list_add_tail(&chan->list, &qmc->chan_head); + qmc->chans[chan->id] = chan; + } + + return qmc_check_chans(qmc); +} + +static int qmc_setup_tsa_64rxtx(struct qmc *qmc, const struct tsa_cell_info *info) +{ + struct qmc_chan *chan; + unsigned int i; + u16 val; + + /* Use a common Tx/Rx 64 entries table + * Everything was previously checked, Tx and Rx related stuffs are + * identical -> Used Rx related stuff to build the table + */ + + /* Invalidate all entries */ + for (i = 0; i < 64; i++) + qmc_write16(qmc->scc_pram + QMC_GBL_TSATRX + (i * 2), 0x0000); + + /* Set entries based on Rx stuff*/ + list_for_each_entry(chan, &qmc->chan_head, list) { + for (i = 0; i < info->nb_rx_ts; i++) { + if (!(chan->rx_ts_mask & (((u64)1) << i))) + continue; + + val = QMC_TSA_VALID | QMC_TSA_MASK | + QMC_TSA_CHANNEL(chan->id); + qmc_write16(qmc->scc_pram + QMC_GBL_TSATRX + (i * 2), val); + } + } + + /* Set Wrap bit on last entry */ + qmc_setbits16(qmc->scc_pram + QMC_GBL_TSATRX + ((info->nb_rx_ts - 1) * 2), + QMC_TSA_WRAP); + + /* Init pointers to the table */ + val = qmc->scc_pram_offset + QMC_GBL_TSATRX; + qmc_write16(qmc->scc_pram + QMC_GBL_RX_S_PTR, val); + qmc_write16(qmc->scc_pram + QMC_GBL_RXPTR, val); + qmc_write16(qmc->scc_pram + QMC_GBL_TX_S_PTR, val); + qmc_write16(qmc->scc_pram + QMC_GBL_TXPTR, val); + + return 0; +} + +static int qmc_setup_tsa_32rx_32tx(struct qmc *qmc, const struct tsa_cell_info *info) +{ + struct qmc_chan *chan; + unsigned int i; + u16 val; + + /* Use a Tx 32 entries table and a Rx 32 entries table. + * Everything was previously checked. + */ + + /* Invalidate all entries */ + for (i = 0; i < 32; i++) { + qmc_write16(qmc->scc_pram + QMC_GBL_TSATRX + (i * 2), 0x0000); + qmc_write16(qmc->scc_pram + QMC_GBL_TSATTX + (i * 2), 0x0000); + } + + /* Set entries based on Rx and Tx stuff*/ + list_for_each_entry(chan, &qmc->chan_head, list) { + /* Rx part */ + for (i = 0; i < info->nb_rx_ts; i++) { + if (!(chan->rx_ts_mask & (((u64)1) << i))) + continue; + + val = QMC_TSA_VALID | QMC_TSA_MASK | + QMC_TSA_CHANNEL(chan->id); + qmc_write16(qmc->scc_pram + QMC_GBL_TSATRX + (i * 2), val); + } + /* Tx part */ + for (i = 0; i < info->nb_tx_ts; i++) { + if (!(chan->tx_ts_mask & (((u64)1) << i))) + continue; + + val = QMC_TSA_VALID | QMC_TSA_MASK | + QMC_TSA_CHANNEL(chan->id); + qmc_write16(qmc->scc_pram + QMC_GBL_TSATTX + (i * 2), val); + } + } + + /* Set Wrap bit on last entries */ + qmc_setbits16(qmc->scc_pram + QMC_GBL_TSATRX + ((info->nb_rx_ts - 1) * 2), + QMC_TSA_WRAP); + qmc_setbits16(qmc->scc_pram + QMC_GBL_TSATTX + ((info->nb_tx_ts - 1) * 2), + QMC_TSA_WRAP); + + /* Init Rx pointers ...*/ + val = qmc->scc_pram_offset + QMC_GBL_TSATRX; + qmc_write16(qmc->scc_pram + QMC_GBL_RX_S_PTR, val); + qmc_write16(qmc->scc_pram + QMC_GBL_RXPTR, val); + + /* ... and Tx pointers */ + val = qmc->scc_pram_offset + QMC_GBL_TSATTX; + qmc_write16(qmc->scc_pram + QMC_GBL_TX_S_PTR, val); + qmc_write16(qmc->scc_pram + QMC_GBL_TXPTR, val); + + return 0; +} + +static int qmc_setup_tsa(struct qmc *qmc) +{ + struct tsa_cell_info info; + int ret; + + /* Retrieve info from TSA related cell */ + ret = tsa_get_info(qmc->tsa, qmc->tsa_cell_id, &info); + if (ret) + return ret; + + /* Setup one common 64 entries table or two 32 entries (one for Tx and + * one for Tx) according to assigned TS numbers. + */ + return ((info.nb_tx_ts > 32) || (info.nb_rx_ts > 32)) ? + qmc_setup_tsa_64rxtx(qmc, &info) : + qmc_setup_tsa_32rx_32tx(qmc, &info); +} + +static int qmc_setup_chan_trnsync(struct qmc *qmc, struct qmc_chan *chan) +{ + struct tsa_cell_info info; + u16 first_rx, last_tx; + u16 trnsync; + int ret; + + /* Retrieve info from TSA related cell */ + ret = tsa_get_info(qmc->tsa, qmc->tsa_cell_id, &info); + if (ret) + return ret; + + /* Find the first Rx TS allocated to the channel */ + first_rx = chan->rx_ts_mask ? __ffs64(chan->rx_ts_mask) + 1 : 0; + + /* Find the last Tx TS allocated to the channel */ + last_tx = fls64(chan->tx_ts_mask); + + trnsync = 0; + if (info.nb_rx_ts) + trnsync |= QMC_SPE_TRNSYNC_RX((first_rx % info.nb_rx_ts) * 2); + if (info.nb_tx_ts) + trnsync |= QMC_SPE_TRNSYNC_TX((last_tx % info.nb_tx_ts) * 2); + + qmc_write16(chan->s_param + QMC_SPE_TRNSYNC, trnsync); + + dev_dbg(qmc->dev, "chan %u: trnsync=0x%04x, rx %u/%u 0x%llx, tx %u/%u 0x%llx\n", + chan->id, trnsync, + first_rx, info.nb_rx_ts, chan->rx_ts_mask, + last_tx, info.nb_tx_ts, chan->tx_ts_mask); + + return 0; +} + +static int qmc_setup_chan(struct qmc *qmc, struct qmc_chan *chan) +{ + unsigned int i; + cbd_t __iomem *bd; + int ret; + u16 val; + + chan->qmc = qmc; + + /* Set channel specific parameter base address */ + chan->s_param = qmc->dpram + (chan->id * 64); + /* 16 bd per channel (8 rx and 8 tx) */ + chan->txbds = qmc->bd_table + (chan->id * (QMC_NB_TXBDS + QMC_NB_RXBDS)); + chan->rxbds = qmc->bd_table + (chan->id * (QMC_NB_TXBDS + QMC_NB_RXBDS)) + QMC_NB_TXBDS; + + chan->txbd_free = chan->txbds; + chan->txbd_done = chan->txbds; + chan->rxbd_free = chan->rxbds; + chan->rxbd_done = chan->rxbds; + + /* TBASE and TBPTR*/ + val = chan->id * (QMC_NB_TXBDS + QMC_NB_RXBDS) * sizeof(cbd_t); + qmc_write16(chan->s_param + QMC_SPE_TBASE, val); + qmc_write16(chan->s_param + QMC_SPE_TBPTR, val); + + /* RBASE and RBPTR*/ + val = ((chan->id * (QMC_NB_TXBDS + QMC_NB_RXBDS)) + QMC_NB_TXBDS) * sizeof(cbd_t); + qmc_write16(chan->s_param + QMC_SPE_RBASE, val); + qmc_write16(chan->s_param + QMC_SPE_RBPTR, val); + qmc_write32(chan->s_param + QMC_SPE_TSTATE, 0x30000000); + qmc_write32(chan->s_param + QMC_SPE_RSTATE, 0x31000000); + qmc_write32(chan->s_param + QMC_SPE_ZISTATE, 0x00000100); + if (chan->mode == QMC_TRANSPARENT) { + qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x18000080); + qmc_write16(chan->s_param + QMC_SPE_TMRBLR, 60); + val = QMC_SPE_CHAMR_MODE_TRANSP | QMC_SPE_CHAMR_TRANSP_SYNC; + if (chan->is_reverse_data) + val |= QMC_SPE_CHAMR_TRANSP_RD; + qmc_write16(chan->s_param + QMC_SPE_CHAMR, val); + ret = qmc_setup_chan_trnsync(qmc, chan); + if (ret) + return ret; + } else { + qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x00000080); + qmc_write16(chan->s_param + QMC_SPE_MFLR, 60); + qmc_write16(chan->s_param + QMC_SPE_CHAMR, + QMC_SPE_CHAMR_MODE_HDLC | QMC_SPE_CHAMR_HDLC_IDLM); + } + + /* Do not enable interrupts now. They will be enabled later */ + qmc_write16(chan->s_param + QMC_SPE_INTMSK, 0x0000); + + /* Init Rx BDs and set Wrap bit on last descriptor */ + BUILD_BUG_ON(QMC_NB_RXBDS == 0); + val = QMC_BD_RX_I; + for (i = 0; i < QMC_NB_RXBDS; i++) { + bd = chan->rxbds + i; + qmc_write16(&bd->cbd_sc, val); + } + bd = chan->rxbds + QMC_NB_RXBDS - 1; + qmc_write16(&bd->cbd_sc, val | QMC_BD_RX_W); + + /* Init Tx BDs and set Wrap bit on last descriptor */ + BUILD_BUG_ON(QMC_NB_TXBDS == 0); + val = QMC_BD_TX_I; + if (chan->mode == QMC_HDLC) + val |= QMC_BD_TX_L | QMC_BD_TX_TC; + for (i = 0; i < QMC_NB_TXBDS; i++) { + bd = chan->txbds + i; + qmc_write16(&bd->cbd_sc, val); + } + bd = chan->txbds + QMC_NB_TXBDS - 1; + qmc_write16(&bd->cbd_sc, val | QMC_BD_TX_W); + + return 0; +} + +static int qmc_setup_chans(struct qmc *qmc) +{ + struct qmc_chan *chan; + int ret; + + list_for_each_entry(chan, &qmc->chan_head, list) { + ret = qmc_setup_chan(qmc, chan); + if (ret) + return ret; + } + + return 0; +} + +static int qmc_finalize_chans(struct qmc *qmc) +{ + struct qmc_chan *chan; + int ret; + + list_for_each_entry(chan, &qmc->chan_head, list) { + /* Unmask channel interrupts */ + if (chan->mode == QMC_HDLC) { + qmc_write16(chan->s_param + QMC_SPE_INTMSK, + QMC_INT_NID | QMC_INT_IDL | QMC_INT_MRF | + QMC_INT_UN | QMC_INT_RXF | QMC_INT_BSY | + QMC_INT_TXB | QMC_INT_RXB); + } else { + qmc_write16(chan->s_param + QMC_SPE_INTMSK, + QMC_INT_UN | QMC_INT_BSY | + QMC_INT_TXB | QMC_INT_RXB); + } + + /* Forced stop the channel */ + ret = qmc_chan_stop(chan, QMC_CHAN_ALL); + if (ret) + return ret; + } + + return 0; +} + +static int qmc_setup_ints(struct qmc *qmc) +{ + unsigned int i; + u16 __iomem *last; + + /* Raz all entries */ + for (i = 0; i < (qmc->int_size / sizeof(u16)); i++) + qmc_write16(qmc->int_table + i, 0x0000); + + /* Set Wrap bit on last entry */ + if (qmc->int_size >= sizeof(u16)) { + last = qmc->int_table + (qmc->int_size / sizeof(u16)) - 1; + qmc_write16(last, QMC_INT_W); + } + + return 0; +} + +static void qmc_irq_gint(struct qmc *qmc) +{ + struct qmc_chan *chan; + unsigned int chan_id; + unsigned long flags; + u16 int_entry; + + int_entry = qmc_read16(qmc->int_curr); + while (int_entry & QMC_INT_V) { + /* Clear all but the Wrap bit */ + qmc_write16(qmc->int_curr, int_entry & QMC_INT_W); + + chan_id = QMC_INT_GET_CHANNEL(int_entry); + chan = qmc->chans[chan_id]; + if (!chan) { + dev_err(qmc->dev, "interrupt on invalid chan %u\n", chan_id); + goto int_next; + } + + if (int_entry & QMC_INT_TXB) + qmc_chan_write_done(chan); + + if (int_entry & QMC_INT_UN) { + dev_info(qmc->dev, "intr chan %u, 0x%04x (UN)\n", chan_id, + int_entry); + chan->nb_tx_underrun++; + } + + if (int_entry & QMC_INT_BSY) { + dev_info(qmc->dev, "intr chan %u, 0x%04x (BSY)\n", chan_id, + int_entry); + chan->nb_rx_busy++; + /* Restart the receiver if needed */ + spin_lock_irqsave(&chan->rx_lock, flags); + if (chan->rx_pending && !chan->is_rx_stopped) { + if (chan->mode == QMC_TRANSPARENT) + qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x18000080); + else + qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x00000080); + qmc_write32(chan->s_param + QMC_SPE_RSTATE, 0x31000000); + chan->is_rx_halted = false; + } else { + chan->is_rx_halted = true; + } + spin_unlock_irqrestore(&chan->rx_lock, flags); + } + + if (int_entry & QMC_INT_RXB) + qmc_chan_read_done(chan); + +int_next: + if (int_entry & QMC_INT_W) + qmc->int_curr = qmc->int_table; + else + qmc->int_curr++; + int_entry = qmc_read16(qmc->int_curr); + } +} + +static irqreturn_t qmc_irq_handler(int irq, void *priv) +{ + struct qmc *qmc = (struct qmc *)priv; + u16 scce; + + scce = qmc_read16(qmc->scc_regs + SCC_SCCE); + qmc_write16(qmc->scc_regs + SCC_SCCE, scce); + + if (unlikely(scce & SCC_SCCE_IQOV)) + dev_info(qmc->dev, "IRQ queue overflow\n"); + + if (unlikely(scce & SCC_SCCE_GUN)) + dev_err(qmc->dev, "Global transmitter underrun\n"); + + if (unlikely(scce & SCC_SCCE_GOV)) + dev_err(qmc->dev, "Global receiver overrun\n"); + + /* normal interrupt */ + if (likely(scce & SCC_SCCE_GINT)) + qmc_irq_gint(qmc); + + return IRQ_HANDLED; +} + +static int qmc_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + unsigned int nb_chans; + struct resource *res; + struct qmc *qmc; + u32 val; + int irq; + int ret; + + qmc = devm_kzalloc(&pdev->dev, sizeof(*qmc), GFP_KERNEL); + if (!qmc) + return -ENOMEM; + + qmc->dev = &pdev->dev; + INIT_LIST_HEAD(&qmc->chan_head); + + qmc->scc_regs = devm_platform_ioremap_resource_byname(pdev, "scc_regs"); + if (IS_ERR(qmc->scc_regs)) + return PTR_ERR(qmc->scc_regs); + + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "scc_pram"); + if (!res) + return -EINVAL; + qmc->scc_pram_offset = res->start - get_immrbase(); + qmc->scc_pram = devm_ioremap_resource(qmc->dev, res); + if (IS_ERR(qmc->scc_pram)) + return PTR_ERR(qmc->scc_pram); + + qmc->dpram = devm_platform_ioremap_resource_byname(pdev, "dpram"); + if (IS_ERR(qmc->dpram)) + return PTR_ERR(qmc->dpram); + + ret = of_property_read_u32(np, "fsl,tsa-cell-id", &val); + if (ret) { + dev_err(qmc->dev, "%pOF: failed to read fsl,tsa-cell-id\n", np); + return ret; + } + qmc->tsa_cell_id = val; + + qmc->tsa = devm_tsa_get_byphandle(qmc->dev, np, "fsl,tsa"); + if (IS_ERR(qmc->tsa)) { + return dev_err_probe(qmc->dev, PTR_ERR(qmc->tsa), + "Failed to get TSA\n"); + } + + /* Connect the SCC to TSA */ + ret = tsa_connect(qmc->tsa, qmc->tsa_cell_id); + if (ret) { + dev_err(qmc->dev, "Failed to connect TSA\n"); + return ret; + } + + /* Parse channels informationss */ + ret = qmc_of_parse_chans(qmc, np); + if (ret) + goto err_tsa_disconnect; + + nb_chans = qmc_nb_chans(qmc); + + /* Init GMSR_H and GMSR_L registers */ + qmc_write32(qmc->scc_regs + SCC_GSMRH, + SCC_GSMRH_CDS | SCC_GSMRH_CTSS | SCC_GSMRH_CDP | SCC_GSMRH_CTSP); + + /* enable QMC mode */ + qmc_write32(qmc->scc_regs + SCC_GSMRL, SCC_GSMRL_MODE_QMC); + + /* Allocate the buffer descriptor table + * 8 rx and 8 tx descriptors per channel + */ + qmc->bd_size = (nb_chans * (QMC_NB_TXBDS + QMC_NB_RXBDS)) * sizeof(cbd_t); + qmc->bd_table = dmam_alloc_coherent(qmc->dev, qmc->bd_size, + &qmc->bd_dma_addr, GFP_KERNEL); + if (!qmc->bd_table) { + dev_err(qmc->dev, "Failed to allocate bd table\n"); + ret = -ENOMEM; + goto err_tsa_disconnect; + } + memset(qmc->bd_table, 0, qmc->bd_size); + + qmc_write32(qmc->scc_pram + QMC_GBL_MCBASE, qmc->bd_dma_addr); + + /* Allocate the interrupt table */ + qmc->int_size = QMC_NB_INTS * sizeof(u16); + qmc->int_table = dmam_alloc_coherent(qmc->dev, qmc->int_size, + &qmc->int_dma_addr, GFP_KERNEL); + if (!qmc->int_table) { + dev_err(qmc->dev, "Failed to allocate interrupt table\n"); + ret = -ENOMEM; + goto err_tsa_disconnect; + } + memset(qmc->int_table, 0, qmc->int_size); + + qmc->int_curr = qmc->int_table; + qmc_write32(qmc->scc_pram + QMC_GBL_INTBASE, qmc->int_dma_addr); + qmc_write32(qmc->scc_pram + QMC_GBL_INTPTR, qmc->int_dma_addr); + + /* Set MRBLR (valid for HDLC only) max MRU + max CRC */ + qmc_write16(qmc->scc_pram + QMC_GBL_MRBLR, HDLC_MAX_MRU + 4); + + qmc_write16(qmc->scc_pram + QMC_GBL_GRFTHR, 1); + qmc_write16(qmc->scc_pram + QMC_GBL_GRFCNT, 1); + + qmc_write32(qmc->scc_pram + QMC_GBL_C_MASK32, 0xDEBB20E3); + qmc_write16(qmc->scc_pram + QMC_GBL_C_MASK16, 0xF0B8); + + ret = qmc_setup_tsa(qmc); + if (ret) + goto err_tsa_disconnect; + + qmc_write16(qmc->scc_pram + QMC_GBL_QMCSTATE, 0x8000); + + ret = qmc_setup_chans(qmc); + if (ret) + goto err_tsa_disconnect; + + /* Init interrupts table */ + ret = qmc_setup_ints(qmc); + if (ret) + goto err_tsa_disconnect; + + /* Disable and clear interrupts, set the irq handler */ + qmc_write16(qmc->scc_regs + SCC_SCCM, 0x0000); + qmc_write16(qmc->scc_regs + SCC_SCCE, 0x000F); + irq = platform_get_irq(pdev, 0); + if (irq < 0) + goto err_tsa_disconnect; + ret = devm_request_irq(qmc->dev, irq, qmc_irq_handler, 0, "qmc", qmc); + if (ret < 0) + goto err_tsa_disconnect; + + /* Enable interrupts */ + qmc_write16(qmc->scc_regs + SCC_SCCM, + SCC_SCCE_IQOV | SCC_SCCE_GINT | SCC_SCCE_GUN | SCC_SCCE_GOV); + + ret = qmc_finalize_chans(qmc); + if (ret < 0) + goto err_disable_intr; + + /* Enable transmiter and receiver */ + qmc_setbits32(qmc->scc_regs + SCC_GSMRL, SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + platform_set_drvdata(pdev, qmc); + + return 0; + +err_disable_intr: + qmc_write16(qmc->scc_regs + SCC_SCCM, 0); + +err_tsa_disconnect: + tsa_disconnect(qmc->tsa, qmc->tsa_cell_id); + return ret; +} + +static int qmc_remove(struct platform_device *pdev) +{ + struct qmc *qmc = platform_get_drvdata(pdev); + + /* Disable transmiter and receiver */ + qmc_setbits32(qmc->scc_regs + SCC_GSMRL, 0); + + /* Disable interrupts */ + qmc_write16(qmc->scc_regs + SCC_SCCM, 0); + + /* Disconnect from tsa */ + tsa_disconnect(qmc->tsa, qmc->tsa_cell_id); + + return 0; +} + +static const struct of_device_id qmc_id_table[] = { + { .compatible = "fsl,cpm1-scc-qmc" }, + {} /* sentinel */ +}; +MODULE_DEVICE_TABLE(of, qmc_id_table); + +static struct platform_driver qmc_driver = { + .driver = { + .name = "fsl-qmc", + .of_match_table = of_match_ptr(qmc_id_table), + }, + .probe = qmc_probe, + .remove = qmc_remove, +}; +module_platform_driver(qmc_driver); + +struct qmc_chan *qmc_chan_get_byphandle(struct device_node *np, const char *phandle_name) +{ + struct of_phandle_args out_args; + struct platform_device *pdev; + struct qmc_chan *qmc_chan; + struct qmc *qmc; + int ret; + + ret = of_parse_phandle_with_fixed_args(np, phandle_name, 1, 0, + &out_args); + if (ret < 0) + return ERR_PTR(ret); + + if (!of_match_node(qmc_driver.driver.of_match_table, out_args.np)) { + of_node_put(out_args.np); + return ERR_PTR(-EINVAL); + } + + pdev = of_find_device_by_node(out_args.np); + of_node_put(out_args.np); + if (!pdev) + return ERR_PTR(-ENODEV); + + qmc = platform_get_drvdata(pdev); + if (!qmc) { + platform_device_put(pdev); + return ERR_PTR(-EPROBE_DEFER); + } + + if (out_args.args_count != 1) { + platform_device_put(pdev); + return ERR_PTR(-EINVAL); + } + + if (out_args.args[0] >= ARRAY_SIZE(qmc->chans)) { + platform_device_put(pdev); + return ERR_PTR(-EINVAL); + } + + qmc_chan = qmc->chans[out_args.args[0]]; + if (!qmc_chan) { + platform_device_put(pdev); + return ERR_PTR(-ENOENT); + } + + return qmc_chan; +} +EXPORT_SYMBOL(qmc_chan_get_byphandle); + +void qmc_chan_put(struct qmc_chan *chan) +{ + put_device(chan->qmc->dev); +} +EXPORT_SYMBOL(qmc_chan_put); + +static void devm_qmc_chan_release(struct device *dev, void *res) +{ + struct qmc_chan **qmc_chan = res; + + qmc_chan_put(*qmc_chan); +} + +struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev, + struct device_node *np, + const char *phandle_name) +{ + struct qmc_chan *qmc_chan; + struct qmc_chan **dr; + + dr = devres_alloc(devm_qmc_chan_release, sizeof(*dr), GFP_KERNEL); + if (!dr) + return ERR_PTR(-ENOMEM); + + qmc_chan = qmc_chan_get_byphandle(np, phandle_name); + if (!IS_ERR(qmc_chan)) { + *dr = qmc_chan; + devres_add(dev, dr); + } else { + devres_free(dr); + } + + return qmc_chan; +} +EXPORT_SYMBOL(devm_qmc_chan_get_byphandle); + +MODULE_AUTHOR("Herve Codina "); +MODULE_DESCRIPTION("CPM QMC driver"); +MODULE_LICENSE("GPL"); diff --git a/include/soc/fsl/qe/qmc.h b/include/soc/fsl/qe/qmc.h new file mode 100644 index 000000000000..3c61a50d2ae2 --- /dev/null +++ b/include/soc/fsl/qe/qmc.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QMC management + * + * Copyright 2022 CS GROUP France + * + * Author: Herve Codina + */ +#ifndef __SOC_FSL_QMC_H__ +#define __SOC_FSL_QMC_H__ + +#include + +struct device_node; +struct device; +struct qmc_chan; + +struct qmc_chan *qmc_chan_get_byphandle(struct device_node *np, const char *phandle_name); +void qmc_chan_put(struct qmc_chan *chan); +struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev, struct device_node *np, + const char *phandle_name); + +enum qmc_mode { + QMC_TRANSPARENT, + QMC_HDLC, +}; + +struct qmc_chan_info { + enum qmc_mode mode; + unsigned long rx_fs_rate; + unsigned long rx_bit_rate; + u8 nb_rx_ts; + unsigned long tx_fs_rate; + unsigned long tx_bit_rate; + u8 nb_tx_ts; +}; + +int qmc_chan_get_info(struct qmc_chan *chan, struct qmc_chan_info *info); + +struct qmc_chan_param { + enum qmc_mode mode; + union { + struct { + u16 max_rx_buf_size; + u16 max_rx_frame_size; + bool is_crc32; + } hdlc; + struct { + u16 max_rx_buf_size; + } transp; + }; +}; + +int qmc_chan_set_param(struct qmc_chan *chan, const struct qmc_chan_param *param); + +int qmc_chan_write_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length, + void (*complete)(void *context), void *context); + +int qmc_chan_read_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length, + void (*complete)(void *context, size_t length), + void *context); + +#define QMC_CHAN_READ (1<<0) +#define QMC_CHAN_WRITE (1<<1) +#define QMC_CHAN_ALL (QMC_CHAN_READ | QMC_CHAN_WRITE) + +int qmc_chan_start(struct qmc_chan *chan, int direction); +int qmc_chan_stop(struct qmc_chan *chan, int direction); +int qmc_chan_reset(struct qmc_chan *chan, int direction); + +#endif /* __SOC_FSL_QMC_H__ */ -- 2.38.1 From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 1F503C54EBD for ; Fri, 13 Jan 2023 10:41:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=jPkrAWssaZDuqx1yFm+9W76rsD+PCwmbCzunT4dfYIg=; b=aH9hhvJrOUNY0e Coles27o/ghydE0dcGnfsvnrQyGrSYE6l9tTer6+QhXcZfvgBnoWrDG6wIIOP3xx0CgU1sZUFq0SO bIEqOlj5AEUz6ruSnBhDW0gEnl9JmoX6jY2S0cN2jbFDrgLOpB8Tche738BfmkH/0z6VwMa2wfIP+ d54K+tP0QZBiF8VpmJooY7b07kCGE5anI6ZpMHcXFwkVHI6CKfa7maLeDFzzGY4U9noPk9VJJltgn g2mXPl87n8ffY8Rpp1wCiFGWIxozqyActopM5i7RZoWPwtdlg8ZreX/ZrPxRVwE0F4V6Qg6E06YFs Zthozjxu7wkFXkwhBhgQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1pGHTn-001oRl-NE; Fri, 13 Jan 2023 10:40:11 +0000 Received: from relay9-d.mail.gandi.net ([217.70.183.199]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1pGHSA-001naD-3E for linux-arm-kernel@lists.infradead.org; Fri, 13 Jan 2023 10:38:36 +0000 Received: (Authenticated sender: herve.codina@bootlin.com) by mail.gandi.net (Postfix) with ESMTPA id D53B5FF818; Fri, 13 Jan 2023 10:38:24 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1673606308; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=H7KIwJYPQrAhWdDUPPixMuaTGE+4u6gAL2K6MQ/lFPI=; b=ZgoI1XZMLKS5Iby0R/H2lYWFdbeiC+jf5zM4HEifY2DHdwpmV655+CdeqzgrBQzoO9EUUJ SGKbLVckraiFG7Qg6nR3e2Yt4lmgFgKb0cV6vdFhF2Wuq2N18JlkkvafMImq5mme03cSve zMRscQ4L4qcQ15P9L7v+wawo2FpGfgTkfpwFFRvGuudFuDsNMpEuvxhXinWJHsCGmfWV9U wJ3V9Nvmvh+4GqHly1lbO1LbZsqgAHHQPamEzOPMCNXVT1kyTVo2CFRel2qkBvwtqivQsY HqpWo1i4090sTzowRaD3/vkzzcT+DSe34Ok1so89wL0E8W6SDHTwJgjdJP1vIA== From: Herve Codina To: Herve Codina , Li Yang , Rob Herring , Krzysztof Kozlowski , Liam Girdwood , Mark Brown , Christophe Leroy , Michael Ellerman , Nicholas Piggin , Qiang Zhao , Jaroslav Kysela , Takashi Iwai , Shengjiu Wang , Xiubo Li , Fabio Estevam , Nicolin Chen Cc: linuxppc-dev@lists.ozlabs.org, linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, alsa-devel@alsa-project.org, Thomas Petazzoni Subject: [PATCH v3 06/10] soc: fsl: cmp1: Add support for QMC Date: Fri, 13 Jan 2023 11:37:55 +0100 Message-Id: <20230113103759.327698-7-herve.codina@bootlin.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20230113103759.327698-1-herve.codina@bootlin.com> References: <20230113103759.327698-1-herve.codina@bootlin.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20230113_023831_501346_C7450925 X-CRM114-Status: GOOD ( 26.55 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: base64 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org VGhlIFFNQyAoUVVJQ0MgTXVsdGljaGFubmVsIENvbnRyb2xsZXIpIGVtdWxhdGVzIHVwIHRvIDY0 CmNoYW5uZWxzIHdpdGhpbiBvbmUgc2VyaWFsIGNvbnRyb2xsZXIgdXNpbmcgdGhlIHNhbWUgVERN CnBoeXNpY2FsIGludGVyZmFjZSByb3V0ZWQgZnJvbSB0aGUgVFNBLgoKSXQgaXMgYXZhaWxhYmxl IGluIHNvbWUJUG93ZXJRVUlDQyBTb0Mgc3VjaCBhcyB0aGUKTVBDODg1IG9yIE1QQzg2Ni4KCkl0 IGlzIGFsc28gYXZhaWxhYmxlIG9uIHNvbWUgUXVpY2MgRW5naW5lIFNvQ3MuClRoaXMgY3VycmVu dCB2ZXJzaW9uIHN1cHBvcnQgQ1BNMSBTb0NzIG9ubHkgYW5kIHNvbWUKZW5oYW5jZW1lbnQgYXJl IG5lZWRlZCB0byBzdXBwb3J0IFF1aWNjIEVuZ2luZSBTb0NzLgoKU2lnbmVkLW9mZi1ieTogSGVy dmUgQ29kaW5hIDxoZXJ2ZS5jb2RpbmFAYm9vdGxpbi5jb20+Ci0tLQogZHJpdmVycy9zb2MvZnNs L3FlL0tjb25maWcgIHwgICAxMiArCiBkcml2ZXJzL3NvYy9mc2wvcWUvTWFrZWZpbGUgfCAgICAx ICsKIGRyaXZlcnMvc29jL2ZzbC9xZS9xbWMuYyAgICB8IDE1MzEgKysrKysrKysrKysrKysrKysr KysrKysrKysrKysrKysrKysKIGluY2x1ZGUvc29jL2ZzbC9xZS9xbWMuaCAgICB8ICAgNzEgKysK IDQgZmlsZXMgY2hhbmdlZCwgMTYxNSBpbnNlcnRpb25zKCspCiBjcmVhdGUgbW9kZSAxMDA2NDQg ZHJpdmVycy9zb2MvZnNsL3FlL3FtYy5jCiBjcmVhdGUgbW9kZSAxMDA2NDQgaW5jbHVkZS9zb2Mv ZnNsL3FlL3FtYy5oCgpkaWZmIC0tZ2l0IGEvZHJpdmVycy9zb2MvZnNsL3FlL0tjb25maWcgYi9k cml2ZXJzL3NvYy9mc2wvcWUvS2NvbmZpZwppbmRleCA2MGVjMTFjOWY0ZDkuLjI1YjIxODM1MWFl MyAxMDA2NDQKLS0tIGEvZHJpdmVycy9zb2MvZnNsL3FlL0tjb25maWcKKysrIGIvZHJpdmVycy9z b2MvZnNsL3FlL0tjb25maWcKQEAgLTQ0LDYgKzQ0LDE4IEBAIGNvbmZpZyBDUE1fVFNBCiAJICBU aGlzIG9wdGlvbiBlbmFibGVzIHN1cHBvcnQgZm9yIHRoaXMKIAkgIGNvbnRyb2xsZXIKIAorY29u ZmlnIENQTV9RTUMKKwl0cmlzdGF0ZSAiQ1BNIFFNQyBzdXBwb3J0IgorCWRlcGVuZHMgb24gT0Yg JiYgSEFTX0lPTUVNCisJZGVwZW5kcyBvbiBDUE0xIHx8IChQUEMgJiYgQ09NUElMRV9URVNUKQor CWRlcGVuZHMgb24gQ1BNX1RTQQorCWhlbHAKKwkgIEZyZWVzY2FsZSBDUE0gUVVJQ0MgTXVsdGlj aGFubmVsIENvbnRyb2xsZXIKKwkgIChRTUMpCisKKwkgIFRoaXMgb3B0aW9uIGVuYWJsZXMgc3Vw cG9ydCBmb3IgdGhpcworCSAgY29udHJvbGxlcgorCiBjb25maWcgUUVfVERNCiAJYm9vbAogCWRl ZmF1bHQgeSBpZiBGU0xfVUNDX0hETEMKZGlmZiAtLWdpdCBhL2RyaXZlcnMvc29jL2ZzbC9xZS9N YWtlZmlsZSBiL2RyaXZlcnMvc29jL2ZzbC9xZS9NYWtlZmlsZQppbmRleCA0NWM5NjFhY2M4MWIu LmVjODUwNmUxMzExMyAxMDA2NDQKLS0tIGEvZHJpdmVycy9zb2MvZnNsL3FlL01ha2VmaWxlCisr KyBiL2RyaXZlcnMvc29jL2ZzbC9xZS9NYWtlZmlsZQpAQCAtNSw2ICs1LDcgQEAKIG9iai0kKENP TkZJR19RVUlDQ19FTkdJTkUpKz0gcWUubyBxZV9jb21tb24ubyBxZV9pYy5vIHFlX2lvLm8KIG9i ai0kKENPTkZJR19DUE0pCSs9IHFlX2NvbW1vbi5vCiBvYmotJChDT05GSUdfQ1BNX1RTQSkJKz0g dHNhLm8KK29iai0kKENPTkZJR19DUE1fUU1DKQkrPSBxbWMubwogb2JqLSQoQ09ORklHX1VDQykJ Kz0gdWNjLm8KIG9iai0kKENPTkZJR19VQ0NfU0xPVykJKz0gdWNjX3Nsb3cubwogb2JqLSQoQ09O RklHX1VDQ19GQVNUKQkrPSB1Y2NfZmFzdC5vCmRpZmYgLS1naXQgYS9kcml2ZXJzL3NvYy9mc2wv cWUvcW1jLmMgYi9kcml2ZXJzL3NvYy9mc2wvcWUvcW1jLmMKbmV3IGZpbGUgbW9kZSAxMDA2NDQK aW5kZXggMDAwMDAwMDAwMDAwLi44NWE5OTUzOGJkZTcKLS0tIC9kZXYvbnVsbAorKysgYi9kcml2 ZXJzL3NvYy9mc2wvcWUvcW1jLmMKQEAgLTAsMCArMSwxNTMxIEBACisvLyBTUERYLUxpY2Vuc2Ut SWRlbnRpZmllcjogR1BMLTIuMAorLyoKKyAqIFFNQyBkcml2ZXIKKyAqCisgKiBDb3B5cmlnaHQg MjAyMiBDUyBHUk9VUCBGcmFuY2UKKyAqCisgKiBBdXRob3I6IEhlcnZlIENvZGluYSA8aGVydmUu Y29kaW5hQGJvb3RsaW4uY29tPgorICovCisKKyNpbmNsdWRlIDxzb2MvZnNsL3FlL3FtYy5oPgor I2luY2x1ZGUgPGxpbnV4L2RtYS1tYXBwaW5nLmg+CisjaW5jbHVkZSA8bGludXgvaGRsYy5oPgor I2luY2x1ZGUgPGxpbnV4L2ludGVycnVwdC5oPgorI2luY2x1ZGUgPGxpbnV4L2lvLmg+CisjaW5j bHVkZSA8bGludXgvbW9kdWxlLmg+CisjaW5jbHVkZSA8bGludXgvb2YuaD4KKyNpbmNsdWRlIDxs aW51eC9vZl9wbGF0Zm9ybS5oPgorI2luY2x1ZGUgPGxpbnV4L3BsYXRmb3JtX2RldmljZS5oPgor I2luY2x1ZGUgPGxpbnV4L3NsYWIuaD4KKyNpbmNsdWRlIDxzb2MvZnNsL2NwbS5oPgorI2luY2x1 ZGUgPHN5c2Rldi9mc2xfc29jLmg+CisjaW5jbHVkZSAidHNhLmgiCisKKy8qIFNDQyBnZW5lcmFs IG1vZGUgcmVnaXN0ZXIgaGlnaCAoMzIgYml0cykgKi8KKyNkZWZpbmUgU0NDX0dTTVJMCTB4MDAK KyNkZWZpbmUgU0NDX0dTTVJMX0VOUgkJKDEgPDwgNSkKKyNkZWZpbmUgU0NDX0dTTVJMX0VOVAkJ KDEgPDwgNCkKKyNkZWZpbmUgU0NDX0dTTVJMX01PREVfUU1DCSgweDBBIDw8IDApCisKKy8qIFND QyBnZW5lcmFsIG1vZGUgcmVnaXN0ZXIgbG93ICgzMiBiaXRzKSAqLworI2RlZmluZSBTQ0NfR1NN UkgJMHgwNAorI2RlZmluZSAgIFNDQ19HU01SSF9DVFNTCSgxIDw8IDcpCisjZGVmaW5lICAgU0ND X0dTTVJIX0NEUwkJKDEgPDwgOCkKKyNkZWZpbmUgICBTQ0NfR1NNUkhfQ1RTUAkoMSA8PCA5KQor I2RlZmluZSAgIFNDQ19HU01SSF9DRFAJCSgxIDw8IDEwKQorCisvKiBTQ0MgZXZlbnQgcmVnaXN0 ZXIgKDE2IGJpdHMpICovCisjZGVmaW5lIFNDQ19TQ0NFCTB4MTAKKyNkZWZpbmUgICBTQ0NfU0ND RV9JUU9WCQkoMSA8PCAzKQorI2RlZmluZSAgIFNDQ19TQ0NFX0dJTlQJCSgxIDw8IDIpCisjZGVm aW5lICAgU0NDX1NDQ0VfR1VOCQkoMSA8PCAxKQorI2RlZmluZSAgIFNDQ19TQ0NFX0dPVgkJKDEg PDwgMCkKKworLyogU0NDIG1hc2sgcmVnaXN0ZXIgKDE2IGJpdHMpICovCisjZGVmaW5lIFNDQ19T Q0NNCTB4MTQKKy8qIE11bHRpY2hhbm5lbCBiYXNlIHBvaW50ZXIgKDMyIGJpdHMpICovCisjZGVm aW5lIFFNQ19HQkxfTUNCQVNFCQkweDAwCisvKiBNdWx0aWNoYW5uZWwgY29udHJvbGxlciBzdGF0 ZSAoMTYgYml0cykgKi8KKyNkZWZpbmUgUU1DX0dCTF9RTUNTVEFURQkweDA0CisvKiBNYXhpbXVt IHJlY2VpdmUgYnVmZmVyIGxlbmd0aCAoMTYgYml0cykgKi8KKyNkZWZpbmUgUU1DX0dCTF9NUkJM UgkJMHgwNgorLyogVHggdGltZS1zbG90IGFzc2lnbm1lbnQgdGFibGUgcG9pbnRlciAoMTYgYml0 cykgKi8KKyNkZWZpbmUgUU1DX0dCTF9UWF9TX1BUUgkweDA4CisvKiBSeCBwb2ludGVyICgxNiBi aXRzKSAqLworI2RlZmluZSBRTUNfR0JMX1JYUFRSCQkweDBBCisvKiBHbG9iYWwgcmVjZWl2ZSBm cmFtZSB0aHJlc2hvbGQgKDE2IGJpdHMpICovCisjZGVmaW5lIFFNQ19HQkxfR1JGVEhSCQkweDBD CisvKiBHbG9iYWwgcmVjZWl2ZSBmcmFtZSBjb3VudCAoMTYgYml0cykgKi8KKyNkZWZpbmUgUU1D X0dCTF9HUkZDTlQJCTB4MEUKKy8qIE11bHRpY2hhbm5lbCBpbnRlcnJ1cHQgYmFzZSBhZGRyZXNz ICgzMiBiaXRzKSAqLworI2RlZmluZSBRTUNfR0JMX0lOVEJBU0UJCTB4MTAKKy8qIE11bHRpY2hh bm5lbCBpbnRlcnJ1cHQgcG9pbnRlciAoMzIgYml0cykgKi8KKyNkZWZpbmUgUU1DX0dCTF9JTlRQ VFIJCTB4MTQKKy8qIFJ4IHRpbWUtc2xvdCBhc3NpZ25tZW50IHRhYmxlIHBvaW50ZXIgKDE2IGJp dHMpICovCisjZGVmaW5lIFFNQ19HQkxfUlhfU19QVFIJMHgxOAorLyogVHggcG9pbnRlciAoMTYg Yml0cykgKi8KKyNkZWZpbmUgUU1DX0dCTF9UWFBUUgkJMHgxQQorLyogQ1JDIGNvbnN0YW50ICgz MiBiaXRzKSAqLworI2RlZmluZSBRTUNfR0JMX0NfTUFTSzMyCTB4MUMKKy8qIFRpbWUgc2xvdCBh c3NpZ25tZW50IHRhYmxlIFJ4ICgzMiB4IDE2IGJpdHMpICovCisjZGVmaW5lIFFNQ19HQkxfVFNB VFJYCQkweDIwCisvKiBUaW1lIHNsb3QgYXNzaWdubWVudCB0YWJsZSBUeCAoMzIgeCAxNiBiaXRz KSAqLworI2RlZmluZSBRTUNfR0JMX1RTQVRUWAkJMHg2MAorLyogQ1JDIGNvbnN0YW50ICgxNiBi aXRzKSAqLworI2RlZmluZSBRTUNfR0JMX0NfTUFTSzE2CTB4QTAKKworLyogVFNBIGVudHJ5ICgx NmJpdCBlbnRyeSBpbiBUU0FUUlggYW5kIFRTQVRUWCkgKi8KKyNkZWZpbmUgUU1DX1RTQV9WQUxJ RAkJKDEgPDwgMTUpCisjZGVmaW5lIFFNQ19UU0FfV1JBUAkJKDEgPDwgMTQpCisjZGVmaW5lIFFN Q19UU0FfTUFTSwkJKDB4MzAzRikKKyNkZWZpbmUgUU1DX1RTQV9DSEFOTkVMKHgpCSgoeCkgPDwg NikKKworLyogVHggYnVmZmVyIGRlc2NyaXB0b3IgYmFzZSBhZGRyZXNzICgxNiBiaXRzLCBvZmZz ZXQgZnJvbSBNQ0JBU0UpICovCisjZGVmaW5lIFFNQ19TUEVfVEJBU0UJMHgwMAorCisvKiBDaGFu bmVsIG1vZGUgcmVnaXN0ZXIgKDE2IGJpdHMpICovCisjZGVmaW5lIFFNQ19TUEVfQ0hBTVIJMHgw MgorI2RlZmluZSAgIFFNQ19TUEVfQ0hBTVJfTU9ERV9IRExDCSgxIDw8IDE1KQorI2RlZmluZSAg IFFNQ19TUEVfQ0hBTVJfTU9ERV9UUkFOU1AJKCgwIDw8IDE1KSB8ICgxIDw8IDEzKSkKKyNkZWZp bmUgICBRTUNfU1BFX0NIQU1SX0VOVAkJKDEgPDwgMTIpCisjZGVmaW5lICAgUU1DX1NQRV9DSEFN Ul9QT0wJCSgxIDw8IDgpCisjZGVmaW5lICAgUU1DX1NQRV9DSEFNUl9IRExDX0lETE0JKDEgPDwg MTMpCisjZGVmaW5lICAgUU1DX1NQRV9DSEFNUl9IRExDX0NSQwkoMSA8PCA3KQorI2RlZmluZSAg IFFNQ19TUEVfQ0hBTVJfSERMQ19OT0YJKDB4MGYgPDwgMCkKKyNkZWZpbmUgICBRTUNfU1BFX0NI QU1SX1RSQU5TUF9SRAkoMSA8PCAxNCkKKyNkZWZpbmUgICBRTUNfU1BFX0NIQU1SX1RSQU5TUF9T WU5DCSgxIDw8IDEwKQorCisvKiBUeCBpbnRlcm5hbCBzdGF0ZSAoMzIgYml0cykgKi8KKyNkZWZp bmUgUU1DX1NQRV9UU1RBVEUJMHgwNAorLyogVHggYnVmZmVyIGRlc2NyaXB0b3IgcG9pbnRlciAo MTYgYml0cykgKi8KKyNkZWZpbmUgUU1DX1NQRV9UQlBUUgkweDBDCisvKiBaZXJvLWluc2VydGlv biBzdGF0ZSAoMzIgYml0cykgKi8KKyNkZWZpbmUgUU1DX1NQRV9aSVNUQVRFCTB4MTQKKy8qIENo YW5uZWzigJlzIGludGVycnVwdCBtYXNrIGZsYWdzICgxNiBiaXRzKSAqLworI2RlZmluZSBRTUNf U1BFX0lOVE1TSwkweDFDCisvKiBSeCBidWZmZXIgZGVzY3JpcHRvciBiYXNlIGFkZHJlc3MgKDE2 IGJpdHMsIG9mZnNldCBmcm9tIE1DQkFTRSkgKi8KKyNkZWZpbmUgUU1DX1NQRV9SQkFTRQkweDIw CisvKiBIRExDOiBNYXhpbXVtIGZyYW1lIGxlbmd0aCByZWdpc3RlciAoMTYgYml0cykgKi8KKyNk ZWZpbmUgUU1DX1NQRV9NRkxSCTB4MjIKKy8qIFRSQU5TUEFSRU5UOiBUcmFuc3BhcmVudCBtYXhp bXVtIHJlY2VpdmUgbGVuZ3RoICgxNiBiaXRzKSAqLworI2RlZmluZSBRTUNfU1BFX1RNUkJMUgkw eDIyCisvKiBSeCBpbnRlcm5hbCBzdGF0ZSAoMzIgYml0cykgKi8KKyNkZWZpbmUgUU1DX1NQRV9S U1RBVEUJMHgyNAorLyogUnggYnVmZmVyIGRlc2NyaXB0b3IgcG9pbnRlciAoMTYgYml0cykgKi8K KyNkZWZpbmUgUU1DX1NQRV9SQlBUUgkweDJDCisvKiBQYWNrcyA0IGJ5dGVzIHRvIDEgbG9uZyB3 b3JkIGJlZm9yZSB3cml0aW5nIHRvIGJ1ZmZlciAoMzIgYml0cykgKi8KKyNkZWZpbmUgUU1DX1NQ RV9SUEFDSwkweDMwCisvKiBaZXJvIGRlbGV0aW9uIHN0YXRlICgzMiBiaXRzKSAqLworI2RlZmlu ZSBRTUNfU1BFX1pEU1RBVEUJMHgzNAorCisvKiBUcmFuc3BhcmVudCBzeW5jaHJvbml6YXRpb24g KDE2IGJpdHMpICovCisjZGVmaW5lIFFNQ19TUEVfVFJOU1lOQyAweDNDCisjZGVmaW5lICAgUU1D X1NQRV9UUk5TWU5DX1JYKHgpCSgoeCkgPDwgOCkKKyNkZWZpbmUgICBRTUNfU1BFX1RSTlNZTkNf VFgoeCkJKCh4KSA8PCAwKQorCisvKiBJbnRlcnJ1cHQgcmVsYXRlZCByZWdpc3RlcnMgYml0cyAq LworI2RlZmluZSBRTUNfSU5UX1YJCSgxIDw8IDE1KQorI2RlZmluZSBRTUNfSU5UX1cJCSgxIDw8 IDE0KQorI2RlZmluZSBRTUNfSU5UX05JRAkJKDEgPDwgMTMpCisjZGVmaW5lIFFNQ19JTlRfSURM CQkoMSA8PCAxMikKKyNkZWZpbmUgUU1DX0lOVF9HRVRfQ0hBTk5FTCh4KQkoKCh4KSAmIDB4MEZD MCkgPj4gNikKKyNkZWZpbmUgUU1DX0lOVF9NUkYJCSgxIDw8IDUpCisjZGVmaW5lIFFNQ19JTlRf VU4JCSgxIDw8IDQpCisjZGVmaW5lIFFNQ19JTlRfUlhGCQkoMSA8PCAzKQorI2RlZmluZSBRTUNf SU5UX0JTWQkJKDEgPDwgMikKKyNkZWZpbmUgUU1DX0lOVF9UWEIJCSgxIDw8IDEpCisjZGVmaW5l IFFNQ19JTlRfUlhCCQkoMSA8PCAwKQorCisvKiBCRCByZWxhdGVkIHJlZ2lzdGVycyBiaXRzICov CisjZGVmaW5lIFFNQ19CRF9SWF9FCSgxIDw8IDE1KQorI2RlZmluZSBRTUNfQkRfUlhfVwkoMSA8 PCAxMykKKyNkZWZpbmUgUU1DX0JEX1JYX0kJKDEgPDwgMTIpCisjZGVmaW5lIFFNQ19CRF9SWF9M CSgxIDw8IDExKQorI2RlZmluZSBRTUNfQkRfUlhfRgkoMSA8PCAxMCkKKyNkZWZpbmUgUU1DX0JE X1JYX0NNCSgxIDw8IDkpCisjZGVmaW5lIFFNQ19CRF9SWF9VQgkoMSA8PCA3KQorI2RlZmluZSBR TUNfQkRfUlhfTEcJKDEgPDwgNSkKKyNkZWZpbmUgUU1DX0JEX1JYX05PCSgxIDw8IDQpCisjZGVm aW5lIFFNQ19CRF9SWF9BQgkoMSA8PCAzKQorI2RlZmluZSBRTUNfQkRfUlhfQ1IJKDEgPDwgMikK KworI2RlZmluZSBRTUNfQkRfVFhfUgkoMSA8PCAxNSkKKyNkZWZpbmUgUU1DX0JEX1RYX1cJKDEg PDwgMTMpCisjZGVmaW5lIFFNQ19CRF9UWF9JCSgxIDw8IDEyKQorI2RlZmluZSBRTUNfQkRfVFhf TAkoMSA8PCAxMSkKKyNkZWZpbmUgUU1DX0JEX1RYX1RDCSgxIDw8IDEwKQorI2RlZmluZSBRTUNf QkRfVFhfQ00JKDEgPDwgOSkKKyNkZWZpbmUgUU1DX0JEX1RYX1VCCSgxIDw8IDcpCisjZGVmaW5l IFFNQ19CRF9UWF9QQUQJKDB4MGYgPDwgMCkKKworLyogTnVtYmVycyBvZiBCRHMgYW5kIGludGVy cnVwdCBpdGVtcyAqLworI2RlZmluZSBRTUNfTkJfVFhCRFMJOAorI2RlZmluZSBRTUNfTkJfUlhC RFMJOAorI2RlZmluZSBRTUNfTkJfSU5UUwkxMjgKKworc3RydWN0IHFtY194ZmVyX2Rlc2Mgewor CXVuaW9uIHsKKwkJdm9pZCAoKnR4X2NvbXBsZXRlKSh2b2lkICpjb250ZXh0KTsKKwkJdm9pZCAo KnJ4X2NvbXBsZXRlKSh2b2lkICpjb250ZXh0LCBzaXplX3QgbGVuZ3RoKTsKKwl9OworCXZvaWQg KmNvbnRleHQ7Cit9OworCitzdHJ1Y3QgcW1jX2NoYW4geworCXN0cnVjdCBsaXN0X2hlYWQgbGlz dDsKKwl1bnNpZ25lZCBpbnQgaWQ7CisJc3RydWN0IHFtYyAqcW1jOworCXZvaWQgKl9faW9tZW0g c19wYXJhbTsKKwllbnVtIHFtY19tb2RlIG1vZGU7CisJdTY0CXR4X3RzX21hc2s7CisJdTY0CXJ4 X3RzX21hc2s7CisJYm9vbCBpc19yZXZlcnNlX2RhdGE7CisKKwlzcGlubG9ja190CXR4X2xvY2s7 CisJY2JkX3QgX19pb21lbSAqdHhiZHM7CisJY2JkX3QgX19pb21lbSAqdHhiZF9mcmVlOworCWNi ZF90IF9faW9tZW0gKnR4YmRfZG9uZTsKKwlzdHJ1Y3QgcW1jX3hmZXJfZGVzYyB0eF9kZXNjW1FN Q19OQl9UWEJEU107CisJdTY0CW5iX3R4X3VuZGVycnVuOworCWJvb2wJaXNfdHhfc3RvcHBlZDsK KworCXNwaW5sb2NrX3QJcnhfbG9jazsKKwljYmRfdCBfX2lvbWVtICpyeGJkczsKKwljYmRfdCBf X2lvbWVtICpyeGJkX2ZyZWU7CisJY2JkX3QgX19pb21lbSAqcnhiZF9kb25lOworCXN0cnVjdCBx bWNfeGZlcl9kZXNjIHJ4X2Rlc2NbUU1DX05CX1JYQkRTXTsKKwl1NjQJbmJfcnhfYnVzeTsKKwlp bnQJcnhfcGVuZGluZzsKKwlib29sCWlzX3J4X2hhbHRlZDsKKwlib29sCWlzX3J4X3N0b3BwZWQ7 Cit9OworCitzdHJ1Y3QgcW1jIHsKKwlzdHJ1Y3QgZGV2aWNlICpkZXY7CisJc3RydWN0IHRzYSAq dHNhOworCXZvaWQgKl9faW9tZW0gc2NjX3JlZ3M7CisJdm9pZCAqX19pb21lbSBzY2NfcHJhbTsK Kwl2b2lkICpfX2lvbWVtIGRwcmFtOworCXUxNiBzY2NfcHJhbV9vZmZzZXQ7CisJdW5zaWduZWQg aW50IHRzYV9jZWxsX2lkOworCWNiZF90IF9faW9tZW0gKmJkX3RhYmxlOworCWRtYV9hZGRyX3Qg YmRfZG1hX2FkZHI7CisJc2l6ZV90IGJkX3NpemU7CisJdTE2IF9faW9tZW0gKmludF90YWJsZTsK Kwl1MTYgX19pb21lbSAqaW50X2N1cnI7CisJZG1hX2FkZHJfdCBpbnRfZG1hX2FkZHI7CisJc2l6 ZV90IGludF9zaXplOworCXN0cnVjdCBsaXN0X2hlYWQgY2hhbl9oZWFkOworCXN0cnVjdCBxbWNf Y2hhbiAqY2hhbnNbNjRdOworfTsKKworc3RhdGljIGlubGluZSB2b2lkIHFtY193cml0ZTE2KHZv aWQgKl9faW9tZW0gYWRkciwgdTE2IHZhbCkKK3sKKwlpb3dyaXRlMTZiZSh2YWwsIGFkZHIpOwor fQorCitzdGF0aWMgaW5saW5lIHUxNiBxbWNfcmVhZDE2KHZvaWQgKl9faW9tZW0gYWRkcikKK3sK KwlyZXR1cm4gaW9yZWFkMTZiZShhZGRyKTsKK30KKworc3RhdGljIGlubGluZSB2b2lkIHFtY19z ZXRiaXRzMTYodm9pZCAqX19pb21lbSBhZGRyLCB1MTYgc2V0KQoreworCXFtY193cml0ZTE2KGFk ZHIsIHFtY19yZWFkMTYoYWRkcikgfCBzZXQpOworfQorCitzdGF0aWMgaW5saW5lIHZvaWQgcW1j X2NscmJpdHMxNih2b2lkICpfX2lvbWVtIGFkZHIsIHUxNiBjbHIpCit7CisJcW1jX3dyaXRlMTYo YWRkciwgcW1jX3JlYWQxNihhZGRyKSAmIH5jbHIpOworfQorCitzdGF0aWMgaW5saW5lIHZvaWQg cW1jX3dyaXRlMzIodm9pZCAqX19pb21lbSBhZGRyLCB1MzIgdmFsKQoreworCWlvd3JpdGUzMmJl KHZhbCwgYWRkcik7Cit9CisKK3N0YXRpYyBpbmxpbmUgdTMyIHFtY19yZWFkMzIodm9pZCAqX19p b21lbSBhZGRyKQoreworCXJldHVybiBpb3JlYWQzMmJlKGFkZHIpOworfQorCitzdGF0aWMgaW5s aW5lIHZvaWQgcW1jX3NldGJpdHMzMih2b2lkICpfX2lvbWVtIGFkZHIsIHUzMiBzZXQpCit7CisJ cW1jX3dyaXRlMzIoYWRkciwgcW1jX3JlYWQzMihhZGRyKSB8IHNldCk7Cit9CisKKworaW50IHFt Y19jaGFuX2dldF9pbmZvKHN0cnVjdCBxbWNfY2hhbiAqY2hhbiwgc3RydWN0IHFtY19jaGFuX2lu Zm8gKmluZm8pCit7CisJc3RydWN0IHRzYV9jZWxsX2luZm8gdHNhX2luZm87CisJaW50IHJldDsK KworCS8qIFJldHJpZXZlIGluZm8gZnJvbSBUU0EgcmVsYXRlZCBjZWxsICovCisJcmV0ID0gdHNh X2dldF9pbmZvKGNoYW4tPnFtYy0+dHNhLCBjaGFuLT5xbWMtPnRzYV9jZWxsX2lkLCAmdHNhX2lu Zm8pOworCWlmIChyZXQpCisJCXJldHVybiByZXQ7CisKKwlpbmZvLT5tb2RlID0gY2hhbi0+bW9k ZTsKKwlpbmZvLT5yeF9mc19yYXRlID0gdHNhX2luZm8ucnhfZnNfcmF0ZTsKKwlpbmZvLT5yeF9i aXRfcmF0ZSA9IHRzYV9pbmZvLnJ4X2JpdF9yYXRlOworCWluZm8tPm5iX3R4X3RzID0gaHdlaWdo dDY0KGNoYW4tPnR4X3RzX21hc2spOworCWluZm8tPnR4X2ZzX3JhdGUgPSB0c2FfaW5mby50eF9m c19yYXRlOworCWluZm8tPnR4X2JpdF9yYXRlID0gdHNhX2luZm8udHhfYml0X3JhdGU7CisJaW5m by0+bmJfcnhfdHMgPSBod2VpZ2h0NjQoY2hhbi0+cnhfdHNfbWFzayk7CisKKwlyZXR1cm4gMDsK K30KK0VYUE9SVF9TWU1CT0wocW1jX2NoYW5fZ2V0X2luZm8pOworCitpbnQgcW1jX2NoYW5fc2V0 X3BhcmFtKHN0cnVjdCBxbWNfY2hhbiAqY2hhbiwgY29uc3Qgc3RydWN0IHFtY19jaGFuX3BhcmFt ICpwYXJhbSkKK3sKKwlpZiAocGFyYW0tPm1vZGUgIT0gY2hhbi0+bW9kZSkKKwkJcmV0dXJuIC1F SU5WQUw7CisKKwlzd2l0Y2ggKHBhcmFtLT5tb2RlKSB7CisJY2FzZSBRTUNfSERMQzoKKwkJaWYg KChwYXJhbS0+aGRsYy5tYXhfcnhfYnVmX3NpemUgJSA0KSB8fAorCQkgICAgKHBhcmFtLT5oZGxj Lm1heF9yeF9idWZfc2l6ZSA8IDgpKQorCQkJcmV0dXJuIC1FSU5WQUw7CisKKwkJcW1jX3dyaXRl MTYoY2hhbi0+cW1jLT5zY2NfcHJhbSArIFFNQ19HQkxfTVJCTFIsCisJCQkgICAgcGFyYW0tPmhk bGMubWF4X3J4X2J1Zl9zaXplIC0gOCk7CisJCXFtY193cml0ZTE2KGNoYW4tPnNfcGFyYW0gKyBR TUNfU1BFX01GTFIsCisJCQkgICAgcGFyYW0tPmhkbGMubWF4X3J4X2ZyYW1lX3NpemUpOworCQlp ZiAocGFyYW0tPmhkbGMuaXNfY3JjMzIpIHsKKwkJCXFtY19zZXRiaXRzMTYoY2hhbi0+c19wYXJh bSArIFFNQ19TUEVfQ0hBTVIsCisJCQkJICAgICAgUU1DX1NQRV9DSEFNUl9IRExDX0NSQyk7CisJ CX0gZWxzZSB7CisJCQlxbWNfY2xyYml0czE2KGNoYW4tPnNfcGFyYW0gKyBRTUNfU1BFX0NIQU1S LAorCQkJCSAgICAgIFFNQ19TUEVfQ0hBTVJfSERMQ19DUkMpOworCQl9CisJCWJyZWFrOworCisJ Y2FzZSBRTUNfVFJBTlNQQVJFTlQ6CisJCXFtY193cml0ZTE2KGNoYW4tPnNfcGFyYW0gKyBRTUNf U1BFX1RNUkJMUiwKKwkJCSAgICBwYXJhbS0+dHJhbnNwLm1heF9yeF9idWZfc2l6ZSk7CisJCWJy ZWFrOworCisJZGVmYXVsdDoKKwkJcmV0dXJuIC1FSU5WQUw7CisJfQorCisJcmV0dXJuIDA7Cit9 CitFWFBPUlRfU1lNQk9MKHFtY19jaGFuX3NldF9wYXJhbSk7CisKK2ludCBxbWNfY2hhbl93cml0 ZV9zdWJtaXQoc3RydWN0IHFtY19jaGFuICpjaGFuLCBkbWFfYWRkcl90IGFkZHIsIHNpemVfdCBs ZW5ndGgsCisJCQkgIHZvaWQgKCpjb21wbGV0ZSkodm9pZCAqY29udGV4dCksIHZvaWQgKmNvbnRl eHQpCit7CisJc3RydWN0IHFtY194ZmVyX2Rlc2MgKnhmZXJfZGVzYzsKKwl1bnNpZ25lZCBsb25n IGZsYWdzOworCWNiZF90ICpfX2lvbWVtIGJkOworCXUxNiBjdHJsOworCWludCByZXQ7CisKKwkv KiAgUiBiaXQgIFVCIGJpdAorCSAqICAgICAwICAgICAgIDAgIDogVGhlIEJEIGlzIGZyZWUKKwkg KiAgICAgMSAgICAgICAxICA6IFRoZSBCRCBpcyBpbiB1c2VkLCB3YWl0aW5nIGZvciB0cmFuc2Zl cgorCSAqICAgICAwICAgICAgIDEgIDogVGhlIEJEIGlzIGluIHVzZWQsIHdhaXRpbmcgZm9yIGNv bXBsZXRpb24KKwkgKiAgICAgMSAgICAgICAwICA6IFNob3VsZCBub3QgYXBwZW5kCisJICovCisK KwlzcGluX2xvY2tfaXJxc2F2ZSgmY2hhbi0+dHhfbG9jaywgZmxhZ3MpOworCWJkID0gY2hhbi0+ dHhiZF9mcmVlOworCisJY3RybCA9IHFtY19yZWFkMTYoJmJkLT5jYmRfc2MpOworCWlmIChjdHJs ICYgKFFNQ19CRF9UWF9SIHwgUU1DX0JEX1RYX1VCKSkgeworCQkvKiBXZSBhcmUgZnVsbCAuLi4g Ki8KKwkJcmV0ID0gLUVCVVNZOworCQlnb3RvIGVuZDsKKwl9CisKKwlxbWNfd3JpdGUxNigmYmQt PmNiZF9kYXRsZW4sIGxlbmd0aCk7CisJcW1jX3dyaXRlMzIoJmJkLT5jYmRfYnVmYWRkciwgYWRk cik7CisKKwl4ZmVyX2Rlc2MgPSAmY2hhbi0+dHhfZGVzY1tiZCAtIGNoYW4tPnR4YmRzXTsKKwl4 ZmVyX2Rlc2MtPnR4X2NvbXBsZXRlID0gY29tcGxldGU7CisJeGZlcl9kZXNjLT5jb250ZXh0ID0g Y29udGV4dDsKKworCS8qIEFjdGl2YXRlIHRoZSBkZXNjcmlwdG9yICovCisJY3RybCB8PSAoUU1D X0JEX1RYX1IgfCBRTUNfQkRfVFhfVUIpOworCXdtYigpOyAvKiBCZSBzdXJlIHRvIGZsdXNoIHRo ZSBkZXNjcmlwdG9yIGJlZm9yZSBjb250cm9sIHVwZGF0ZSAqLworCXFtY193cml0ZTE2KCZiZC0+ Y2JkX3NjLCBjdHJsKTsKKworCWlmICghY2hhbi0+aXNfdHhfc3RvcHBlZCkKKwkJcW1jX3NldGJp dHMxNihjaGFuLT5zX3BhcmFtICsgUU1DX1NQRV9DSEFNUiwgUU1DX1NQRV9DSEFNUl9QT0wpOwor CisJaWYgKGN0cmwgJiBRTUNfQkRfVFhfVykKKwkJY2hhbi0+dHhiZF9mcmVlID0gY2hhbi0+dHhi ZHM7CisJZWxzZQorCQljaGFuLT50eGJkX2ZyZWUrKzsKKworCXJldCA9IDA7CisKK2VuZDoKKwlz cGluX3VubG9ja19pcnFyZXN0b3JlKCZjaGFuLT50eF9sb2NrLCBmbGFncyk7CisJcmV0dXJuIHJl dDsKK30KK0VYUE9SVF9TWU1CT0wocW1jX2NoYW5fd3JpdGVfc3VibWl0KTsKKworc3RhdGljIHZv aWQgcW1jX2NoYW5fd3JpdGVfZG9uZShzdHJ1Y3QgcW1jX2NoYW4gKmNoYW4pCit7CisJc3RydWN0 IHFtY194ZmVyX2Rlc2MgKnhmZXJfZGVzYzsKKwl2b2lkICgqY29tcGxldGUpKHZvaWQgKmNvbnRl eHQpOworCXVuc2lnbmVkIGxvbmcgZmxhZ3M7CisJdm9pZCAqY29udGV4dDsKKwljYmRfdCAqX19p b21lbSBiZDsKKwl1MTYgY3RybDsKKworCS8qICBSIGJpdCAgVUIgYml0CisJICogICAgIDAgICAg ICAgMCAgOiBUaGUgQkQgaXMgZnJlZQorCSAqICAgICAxICAgICAgIDEgIDogVGhlIEJEIGlzIGlu IHVzZWQsIHdhaXRpbmcgZm9yIHRyYW5zZmVyCisJICogICAgIDAgICAgICAgMSAgOiBUaGUgQkQg aXMgaW4gdXNlZCwgd2FpdGluZyBmb3IgY29tcGxldGlvbgorCSAqICAgICAxICAgICAgIDAgIDog U2hvdWxkIG5vdCBhcHBlbmQKKwkgKi8KKworCXNwaW5fbG9ja19pcnFzYXZlKCZjaGFuLT50eF9s b2NrLCBmbGFncyk7CisJYmQgPSBjaGFuLT50eGJkX2RvbmU7CisKKwljdHJsID0gcW1jX3JlYWQx NigmYmQtPmNiZF9zYyk7CisJd2hpbGUgKCEoY3RybCAmIFFNQ19CRF9UWF9SKSkgeworCQlpZiAo IShjdHJsICYgUU1DX0JEX1RYX1VCKSkKKwkJCWdvdG8gZW5kOworCisJCXhmZXJfZGVzYyA9ICZj aGFuLT50eF9kZXNjW2JkIC0gY2hhbi0+dHhiZHNdOworCQljb21wbGV0ZSA9IHhmZXJfZGVzYy0+ dHhfY29tcGxldGU7CisJCWNvbnRleHQgPSB4ZmVyX2Rlc2MtPmNvbnRleHQ7CisJCXhmZXJfZGVz Yy0+dHhfY29tcGxldGUgPSBOVUxMOworCQl4ZmVyX2Rlc2MtPmNvbnRleHQgPSBOVUxMOworCisJ CXFtY193cml0ZTE2KCZiZC0+Y2JkX3NjLCBjdHJsICYgflFNQ19CRF9UWF9VQik7CisKKwkJaWYg KGN0cmwgJiBRTUNfQkRfVFhfVykKKwkJCWNoYW4tPnR4YmRfZG9uZSA9IGNoYW4tPnR4YmRzOwor CQllbHNlCisJCQljaGFuLT50eGJkX2RvbmUrKzsKKworCQlpZiAoY29tcGxldGUpIHsKKwkJCXNw aW5fdW5sb2NrX2lycXJlc3RvcmUoJmNoYW4tPnR4X2xvY2ssIGZsYWdzKTsKKwkJCWNvbXBsZXRl KGNvbnRleHQpOworCQkJc3Bpbl9sb2NrX2lycXNhdmUoJmNoYW4tPnR4X2xvY2ssIGZsYWdzKTsK KwkJfQorCisJCWJkID0gY2hhbi0+dHhiZF9kb25lOworCQljdHJsID0gcW1jX3JlYWQxNigmYmQt PmNiZF9zYyk7CisJfQorCitlbmQ6CisJc3Bpbl91bmxvY2tfaXJxcmVzdG9yZSgmY2hhbi0+dHhf bG9jaywgZmxhZ3MpOworfQorCitpbnQgcW1jX2NoYW5fcmVhZF9zdWJtaXQoc3RydWN0IHFtY19j aGFuICpjaGFuLCBkbWFfYWRkcl90IGFkZHIsIHNpemVfdCBsZW5ndGgsCisJCQkgdm9pZCAoKmNv bXBsZXRlKSh2b2lkICpjb250ZXh0LCBzaXplX3QgbGVuZ3RoKSwgdm9pZCAqY29udGV4dCkKK3sK KwlzdHJ1Y3QgcW1jX3hmZXJfZGVzYyAqeGZlcl9kZXNjOworCXVuc2lnbmVkIGxvbmcgZmxhZ3M7 CisJY2JkX3QgKl9faW9tZW0gYmQ7CisJdTE2IGN0cmw7CisJaW50IHJldDsKKworCS8qICBFIGJp dCAgVUIgYml0CisJICogICAgIDAgICAgICAgMCAgOiBUaGUgQkQgaXMgZnJlZQorCSAqICAgICAx ICAgICAgIDEgIDogVGhlIEJEIGlzIGluIHVzZWQsIHdhaXRpbmcgZm9yIHRyYW5zZmVyCisJICog ICAgIDAgICAgICAgMSAgOiBUaGUgQkQgaXMgaW4gdXNlZCwgd2FpdGluZyBmb3IgY29tcGxldGlv bgorCSAqICAgICAxICAgICAgIDAgIDogU2hvdWxkIG5vdCBhcHBlbmQKKwkgKi8KKworCXNwaW5f bG9ja19pcnFzYXZlKCZjaGFuLT5yeF9sb2NrLCBmbGFncyk7CisJYmQgPSBjaGFuLT5yeGJkX2Zy ZWU7CisKKwljdHJsID0gcW1jX3JlYWQxNigmYmQtPmNiZF9zYyk7CisJaWYgKGN0cmwgJiAoUU1D X0JEX1JYX0UgfCBRTUNfQkRfUlhfVUIpKSB7CisJCS8qIFdlIGFyZSBmdWxsIC4uLiAqLworCQly ZXQgPSAtRUJVU1k7CisJCWdvdG8gZW5kOworCX0KKworCXFtY193cml0ZTE2KCZiZC0+Y2JkX2Rh dGxlbiwgMCk7IC8qIGRhdGEgbGVuZ3RoIGlzIHVwZGF0ZWQgYnkgdGhlIFFNQyAqLworCXFtY193 cml0ZTMyKCZiZC0+Y2JkX2J1ZmFkZHIsIGFkZHIpOworCisJeGZlcl9kZXNjID0gJmNoYW4tPnJ4 X2Rlc2NbYmQgLSBjaGFuLT5yeGJkc107CisJeGZlcl9kZXNjLT5yeF9jb21wbGV0ZSA9IGNvbXBs ZXRlOworCXhmZXJfZGVzYy0+Y29udGV4dCA9IGNvbnRleHQ7CisKKwkvKiBBY3RpdmF0ZSB0aGUg ZGVzY3JpcHRvciAqLworCWN0cmwgfD0gKFFNQ19CRF9SWF9FIHwgUU1DX0JEX1JYX1VCKTsKKwl3 bWIoKTsgLyogQmUgc3VyZSB0byBmbHVzaCBkYXRhIGJlZm9yZSBkZXNjcmlwdG9yIGFjdGl2YXRp b24gKi8KKwlxbWNfd3JpdGUxNigmYmQtPmNiZF9zYywgY3RybCk7CisKKwkvKiBSZXN0YXJ0IHJl Y2VpdmVyIGlmIG5lZWRlZCAqLworCWlmIChjaGFuLT5pc19yeF9oYWx0ZWQgJiYgIWNoYW4tPmlz X3J4X3N0b3BwZWQpIHsKKwkJLyogUmVzdGFydCByZWNlaXZlciAqLworCQlpZiAoY2hhbi0+bW9k ZSA9PSBRTUNfVFJBTlNQQVJFTlQpCisJCQlxbWNfd3JpdGUzMihjaGFuLT5zX3BhcmFtICsgUU1D X1NQRV9aRFNUQVRFLCAweDE4MDAwMDgwKTsKKwkJZWxzZQorCQkJcW1jX3dyaXRlMzIoY2hhbi0+ c19wYXJhbSArIFFNQ19TUEVfWkRTVEFURSwgMHgwMDAwMDA4MCk7CisJCXFtY193cml0ZTMyKGNo YW4tPnNfcGFyYW0gKyBRTUNfU1BFX1JTVEFURSwgMHgzMTAwMDAwMCk7CisJCWNoYW4tPmlzX3J4 X2hhbHRlZCA9IGZhbHNlOworCX0KKwljaGFuLT5yeF9wZW5kaW5nKys7CisKKwlpZiAoY3RybCAm IFFNQ19CRF9SWF9XKQorCQljaGFuLT5yeGJkX2ZyZWUgPSBjaGFuLT5yeGJkczsKKwllbHNlCisJ CWNoYW4tPnJ4YmRfZnJlZSsrOworCisJcmV0ID0gMDsKK2VuZDoKKwlzcGluX3VubG9ja19pcnFy ZXN0b3JlKCZjaGFuLT5yeF9sb2NrLCBmbGFncyk7CisJcmV0dXJuIHJldDsKK30KK0VYUE9SVF9T WU1CT0wocW1jX2NoYW5fcmVhZF9zdWJtaXQpOworCitzdGF0aWMgdm9pZCBxbWNfY2hhbl9yZWFk X2RvbmUoc3RydWN0IHFtY19jaGFuICpjaGFuKQoreworCXZvaWQgKCpjb21wbGV0ZSkodm9pZCAq Y29udGV4dCwgc2l6ZV90IHNpemUpOworCXN0cnVjdCBxbWNfeGZlcl9kZXNjICp4ZmVyX2Rlc2M7 CisJdW5zaWduZWQgbG9uZyBmbGFnczsKKwljYmRfdCAqX19pb21lbSBiZDsKKwl2b2lkICpjb250 ZXh0OworCXUxNiBkYXRhbGVuOworCXUxNiBjdHJsOworCisJLyogIEUgYml0ICBVQiBiaXQKKwkg KiAgICAgMCAgICAgICAwICA6IFRoZSBCRCBpcyBmcmVlCisJICogICAgIDEgICAgICAgMSAgOiBU aGUgQkQgaXMgaW4gdXNlZCwgd2FpdGluZyBmb3IgdHJhbnNmZXIKKwkgKiAgICAgMCAgICAgICAx ICA6IFRoZSBCRCBpcyBpbiB1c2VkLCB3YWl0aW5nIGZvciBjb21wbGV0aW9uCisJICogICAgIDEg ICAgICAgMCAgOiBTaG91bGQgbm90IGFwcGVuZAorCSAqLworCisJc3Bpbl9sb2NrX2lycXNhdmUo JmNoYW4tPnJ4X2xvY2ssIGZsYWdzKTsKKwliZCA9IGNoYW4tPnJ4YmRfZG9uZTsKKworCWN0cmwg PSBxbWNfcmVhZDE2KCZiZC0+Y2JkX3NjKTsKKwl3aGlsZSAoIShjdHJsICYgUU1DX0JEX1JYX0Up KSB7CisJCWlmICghKGN0cmwgJiBRTUNfQkRfUlhfVUIpKQorCQkJZ290byBlbmQ7CisKKwkJeGZl cl9kZXNjID0gJmNoYW4tPnJ4X2Rlc2NbYmQgLSBjaGFuLT5yeGJkc107CisJCWNvbXBsZXRlID0g eGZlcl9kZXNjLT5yeF9jb21wbGV0ZTsKKwkJY29udGV4dCA9IHhmZXJfZGVzYy0+Y29udGV4dDsK KwkJeGZlcl9kZXNjLT5yeF9jb21wbGV0ZSA9IE5VTEw7CisJCXhmZXJfZGVzYy0+Y29udGV4dCA9 IE5VTEw7CisKKwkJZGF0YWxlbiA9IHFtY19yZWFkMTYoJmJkLT5jYmRfZGF0bGVuKTsKKwkJcW1j X3dyaXRlMTYoJmJkLT5jYmRfc2MsIGN0cmwgJiB+UU1DX0JEX1JYX1VCKTsKKworCQlpZiAoY3Ry bCAmIFFNQ19CRF9SWF9XKQorCQkJY2hhbi0+cnhiZF9kb25lID0gY2hhbi0+cnhiZHM7CisJCWVs c2UKKwkJCWNoYW4tPnJ4YmRfZG9uZSsrOworCisJCWNoYW4tPnJ4X3BlbmRpbmctLTsKKworCQlp ZiAoY29tcGxldGUpIHsKKwkJCXNwaW5fdW5sb2NrX2lycXJlc3RvcmUoJmNoYW4tPnJ4X2xvY2ss IGZsYWdzKTsKKwkJCWNvbXBsZXRlKGNvbnRleHQsIGRhdGFsZW4pOworCQkJc3Bpbl9sb2NrX2ly cXNhdmUoJmNoYW4tPnJ4X2xvY2ssIGZsYWdzKTsKKwkJfQorCisJCWJkID0gY2hhbi0+cnhiZF9k b25lOworCQljdHJsID0gcW1jX3JlYWQxNigmYmQtPmNiZF9zYyk7CisJfQorCitlbmQ6CisJc3Bp bl91bmxvY2tfaXJxcmVzdG9yZSgmY2hhbi0+cnhfbG9jaywgZmxhZ3MpOworfQorCitzdGF0aWMg aW50IHFtY19jaGFuX2NvbW1hbmQoc3RydWN0IHFtY19jaGFuICpjaGFuLCB1OCBxbWNfb3Bjb2Rl KQoreworCXJldHVybiBjcG1fY29tbWFuZChjaGFuLT5pZCA8PCAyLCAocW1jX29wY29kZSA8PCA0 KSB8IDB4MEUpOworfQorCitzdGF0aWMgaW50IHFtY19jaGFuX3N0b3Bfcngoc3RydWN0IHFtY19j aGFuICpjaGFuKQoreworCXVuc2lnbmVkIGxvbmcgZmxhZ3M7CisJaW50IHJldDsKKworCXNwaW5f bG9ja19pcnFzYXZlKCZjaGFuLT5yeF9sb2NrLCBmbGFncyk7CisKKwkvKiBTZW5kIFNUT1AgUkVD RUlWRSBjb21tYW5kICovCisJcmV0ID0gcW1jX2NoYW5fY29tbWFuZChjaGFuLCAweDApOworCWlm IChyZXQpIHsKKwkJZGV2X2VycihjaGFuLT5xbWMtPmRldiwgImNoYW4gJXU6IFNlbmQgU1RPUCBS RUNFSVZFIGZhaWxlZCAoJWQpXG4iLAorCQkJY2hhbi0+aWQsIHJldCk7CisJCWdvdG8gZW5kOwor CX0KKworCWNoYW4tPmlzX3J4X3N0b3BwZWQgPSB0cnVlOworCitlbmQ6CisJc3Bpbl91bmxvY2tf aXJxcmVzdG9yZSgmY2hhbi0+cnhfbG9jaywgZmxhZ3MpOworCXJldHVybiByZXQ7Cit9CisKK3N0 YXRpYyBpbnQgcW1jX2NoYW5fc3RvcF90eChzdHJ1Y3QgcW1jX2NoYW4gKmNoYW4pCit7CisJdW5z aWduZWQgbG9uZyBmbGFnczsKKwlpbnQgcmV0OworCisJc3Bpbl9sb2NrX2lycXNhdmUoJmNoYW4t PnR4X2xvY2ssIGZsYWdzKTsKKworCS8qIFNlbmQgU1RPUCBUUkFOU01JVCBjb21tYW5kICovCisJ cmV0ID0gcW1jX2NoYW5fY29tbWFuZChjaGFuLCAweDEpOworCWlmIChyZXQpIHsKKwkJZGV2X2Vy cihjaGFuLT5xbWMtPmRldiwgImNoYW4gJXU6IFNlbmQgU1RPUCBUUkFOU01JVCBmYWlsZWQgKCVk KVxuIiwKKwkJCWNoYW4tPmlkLCByZXQpOworCQlnb3RvIGVuZDsKKwl9CisKKwljaGFuLT5pc190 eF9zdG9wcGVkID0gdHJ1ZTsKKworZW5kOgorCXNwaW5fdW5sb2NrX2lycXJlc3RvcmUoJmNoYW4t PnR4X2xvY2ssIGZsYWdzKTsKKwlyZXR1cm4gcmV0OworfQorCitpbnQgcW1jX2NoYW5fc3RvcChz dHJ1Y3QgcW1jX2NoYW4gKmNoYW4sIGludCBkaXJlY3Rpb24pCit7CisJaW50IHJldDsKKworCWlm IChkaXJlY3Rpb24gJiBRTUNfQ0hBTl9SRUFEKSB7CisJCXJldCA9IHFtY19jaGFuX3N0b3Bfcngo Y2hhbik7CisJCWlmIChyZXQpCisJCQlyZXR1cm4gcmV0OworCX0KKworCWlmIChkaXJlY3Rpb24g JiBRTUNfQ0hBTl9XUklURSkgeworCQlyZXQgPSBxbWNfY2hhbl9zdG9wX3R4KGNoYW4pOworCQlp ZiAocmV0KQorCQkJcmV0dXJuIHJldDsKKwl9CisKKwlyZXR1cm4gMDsKK30KK0VYUE9SVF9TWU1C T0wocW1jX2NoYW5fc3RvcCk7CisKK3N0YXRpYyB2b2lkIHFtY19jaGFuX3N0YXJ0X3J4KHN0cnVj dCBxbWNfY2hhbiAqY2hhbikKK3sKKwl1bnNpZ25lZCBsb25nIGZsYWdzOworCisJc3Bpbl9sb2Nr X2lycXNhdmUoJmNoYW4tPnJ4X2xvY2ssIGZsYWdzKTsKKworCS8qIFJlc3RhcnQgdGhlIHJlY2Vp dmVyICovCisJaWYgKGNoYW4tPm1vZGUgPT0gUU1DX1RSQU5TUEFSRU5UKQorCQlxbWNfd3JpdGUz MihjaGFuLT5zX3BhcmFtICsgUU1DX1NQRV9aRFNUQVRFLCAweDE4MDAwMDgwKTsKKwllbHNlCisJ CXFtY193cml0ZTMyKGNoYW4tPnNfcGFyYW0gKyBRTUNfU1BFX1pEU1RBVEUsIDB4MDAwMDAwODAp OworCXFtY193cml0ZTMyKGNoYW4tPnNfcGFyYW0gKyBRTUNfU1BFX1JTVEFURSwgMHgzMTAwMDAw MCk7CisJY2hhbi0+aXNfcnhfaGFsdGVkID0gZmFsc2U7CisKKwljaGFuLT5pc19yeF9zdG9wcGVk ID0gZmFsc2U7CisKKwlzcGluX3VubG9ja19pcnFyZXN0b3JlKCZjaGFuLT5yeF9sb2NrLCBmbGFn cyk7Cit9CisKK3N0YXRpYyB2b2lkIHFtY19jaGFuX3N0YXJ0X3R4KHN0cnVjdCBxbWNfY2hhbiAq Y2hhbikKK3sKKwl1bnNpZ25lZCBsb25nIGZsYWdzOworCisJc3Bpbl9sb2NrX2lycXNhdmUoJmNo YW4tPnR4X2xvY2ssIGZsYWdzKTsKKworCS8qIEVuYWJsZSBjaGFubmVsIHRyYW5zbWl0dGVyIGFz IGl0IGNvdWxkIGJlIGRpc2FibGVkIGlmCisJICogcW1jX2NoYW5fcmVzZXQoKSB3YXMgY2FsbGVk CisJICovCisJcW1jX3NldGJpdHMxNihjaGFuLT5zX3BhcmFtICsgUU1DX1NQRV9DSEFNUiwgUU1D X1NQRV9DSEFNUl9FTlQpOworCisJLyogU2V0IHRoZSBQT0wgYml0IGluIHRoZSBjaGFubmVsIG1v ZGUgcmVnaXN0ZXIgKi8KKwlxbWNfc2V0Yml0czE2KGNoYW4tPnNfcGFyYW0gKyBRTUNfU1BFX0NI QU1SLCBRTUNfU1BFX0NIQU1SX1BPTCk7CisKKwljaGFuLT5pc190eF9zdG9wcGVkID0gZmFsc2U7 CisKKwlzcGluX3VubG9ja19pcnFyZXN0b3JlKCZjaGFuLT50eF9sb2NrLCBmbGFncyk7Cit9CisK K2ludCBxbWNfY2hhbl9zdGFydChzdHJ1Y3QgcW1jX2NoYW4gKmNoYW4sIGludCBkaXJlY3Rpb24p Cit7CisJaWYgKGRpcmVjdGlvbiAmIFFNQ19DSEFOX1JFQUQpCisJCXFtY19jaGFuX3N0YXJ0X3J4 KGNoYW4pOworCisJaWYgKGRpcmVjdGlvbiAmIFFNQ19DSEFOX1dSSVRFKQorCQlxbWNfY2hhbl9z dGFydF90eChjaGFuKTsKKworCXJldHVybiAwOworfQorRVhQT1JUX1NZTUJPTChxbWNfY2hhbl9z dGFydCk7CisKK3N0YXRpYyB2b2lkIHFtY19jaGFuX3Jlc2V0X3J4KHN0cnVjdCBxbWNfY2hhbiAq Y2hhbikKK3sKKwlzdHJ1Y3QgcW1jX3hmZXJfZGVzYyAqeGZlcl9kZXNjOworCXVuc2lnbmVkIGxv bmcgZmxhZ3M7CisJY2JkX3QgKl9faW9tZW0gYmQ7CisJdTE2IGN0cmw7CisKKwlzcGluX2xvY2tf aXJxc2F2ZSgmY2hhbi0+cnhfbG9jaywgZmxhZ3MpOworCWJkID0gY2hhbi0+cnhiZHM7CisJZG8g eworCQljdHJsID0gcW1jX3JlYWQxNigmYmQtPmNiZF9zYyk7CisJCXFtY193cml0ZTE2KCZiZC0+ Y2JkX3NjLCBjdHJsICYgfihRTUNfQkRfUlhfVUIgfCBRTUNfQkRfUlhfRSkpOworCisJCXhmZXJf ZGVzYyA9ICZjaGFuLT5yeF9kZXNjW2JkIC0gY2hhbi0+cnhiZHNdOworCQl4ZmVyX2Rlc2MtPnJ4 X2NvbXBsZXRlID0gTlVMTDsKKwkJeGZlcl9kZXNjLT5jb250ZXh0ID0gTlVMTDsKKworCQliZCsr OworCX0gd2hpbGUgKCEoY3RybCAmIFFNQ19CRF9SWF9XKSk7CisKKwljaGFuLT5yeGJkX2ZyZWUg PSBjaGFuLT5yeGJkczsKKwljaGFuLT5yeGJkX2RvbmUgPSBjaGFuLT5yeGJkczsKKwlxbWNfd3Jp dGUxNihjaGFuLT5zX3BhcmFtICsgUU1DX1NQRV9SQlBUUiwKKwkJICAgIHFtY19yZWFkMTYoY2hh bi0+c19wYXJhbSArIFFNQ19TUEVfUkJBU0UpKTsKKworCWNoYW4tPnJ4X3BlbmRpbmcgPSAwOwor CWNoYW4tPmlzX3J4X3N0b3BwZWQgPSBmYWxzZTsKKworCXNwaW5fdW5sb2NrX2lycXJlc3RvcmUo JmNoYW4tPnJ4X2xvY2ssIGZsYWdzKTsKK30KKworc3RhdGljIHZvaWQgcW1jX2NoYW5fcmVzZXRf dHgoc3RydWN0IHFtY19jaGFuICpjaGFuKQoreworCXN0cnVjdCBxbWNfeGZlcl9kZXNjICp4ZmVy X2Rlc2M7CisJdW5zaWduZWQgbG9uZyBmbGFnczsKKwljYmRfdCAqX19pb21lbSBiZDsKKwl1MTYg Y3RybDsKKworCXNwaW5fbG9ja19pcnFzYXZlKCZjaGFuLT50eF9sb2NrLCBmbGFncyk7CisKKwkv KiBEaXNhYmxlIHRyYW5zbWl0dGVyLiBJdCB3aWxsIGJlIHJlLWVuYWJsZSBvbiBxbWNfY2hhbl9z dGFydCgpICovCisJcW1jX2NscmJpdHMxNihjaGFuLT5zX3BhcmFtICsgUU1DX1NQRV9DSEFNUiwg UU1DX1NQRV9DSEFNUl9FTlQpOworCisJYmQgPSBjaGFuLT50eGJkczsKKwlkbyB7CisJCWN0cmwg PSBxbWNfcmVhZDE2KCZiZC0+Y2JkX3NjKTsKKwkJcW1jX3dyaXRlMTYoJmJkLT5jYmRfc2MsIGN0 cmwgJiB+KFFNQ19CRF9UWF9VQiB8IFFNQ19CRF9UWF9SKSk7CisKKwkJeGZlcl9kZXNjID0gJmNo YW4tPnR4X2Rlc2NbYmQgLSBjaGFuLT50eGJkc107CisJCXhmZXJfZGVzYy0+dHhfY29tcGxldGUg PSBOVUxMOworCQl4ZmVyX2Rlc2MtPmNvbnRleHQgPSBOVUxMOworCisJCWJkKys7CisJfSB3aGls ZSAoIShjdHJsICYgUU1DX0JEX1RYX1cpKTsKKworCWNoYW4tPnR4YmRfZnJlZSA9IGNoYW4tPnR4 YmRzOworCWNoYW4tPnR4YmRfZG9uZSA9IGNoYW4tPnR4YmRzOworCXFtY193cml0ZTE2KGNoYW4t PnNfcGFyYW0gKyBRTUNfU1BFX1RCUFRSLAorCQkgICAgcW1jX3JlYWQxNihjaGFuLT5zX3BhcmFt ICsgUU1DX1NQRV9UQkFTRSkpOworCisJLyogUmVzZXQgVFNUQVRFIGFuZCBaSVNUQVRFIHRvIHRo ZWlyIGluaXRpYWwgdmFsdWUgKi8KKwlxbWNfd3JpdGUzMihjaGFuLT5zX3BhcmFtICsgUU1DX1NQ RV9UU1RBVEUsIDB4MzAwMDAwMDApOworCXFtY193cml0ZTMyKGNoYW4tPnNfcGFyYW0gKyBRTUNf U1BFX1pJU1RBVEUsIDB4MDAwMDAxMDApOworCisJc3Bpbl91bmxvY2tfaXJxcmVzdG9yZSgmY2hh bi0+dHhfbG9jaywgZmxhZ3MpOworfQorCitpbnQgcW1jX2NoYW5fcmVzZXQoc3RydWN0IHFtY19j aGFuICpjaGFuLCBpbnQgZGlyZWN0aW9uKQoreworCWlmIChkaXJlY3Rpb24gJiBRTUNfQ0hBTl9S RUFEKQorCQlxbWNfY2hhbl9yZXNldF9yeChjaGFuKTsKKworCWlmIChkaXJlY3Rpb24gJiBRTUNf Q0hBTl9XUklURSkKKwkJcW1jX2NoYW5fcmVzZXRfdHgoY2hhbik7CisKKwlyZXR1cm4gMDsKK30K K0VYUE9SVF9TWU1CT0wocW1jX2NoYW5fcmVzZXQpOworCitzdGF0aWMgaW50IHFtY19jaGVja19j aGFucyhzdHJ1Y3QgcW1jICpxbWMpCit7CisJc3RydWN0IHRzYV9jZWxsX2luZm8gaW5mbzsKKwli b29sIGlzX29uZV90YWJsZSA9IGZhbHNlOworCXN0cnVjdCBxbWNfY2hhbiAqY2hhbjsKKwl1NjQg dHhfdHNfbWFzayA9IDA7CisJdTY0IHJ4X3RzX21hc2sgPSAwOworCXU2NCB0eF90c19hc3NpZ25l ZF9tYXNrOworCXU2NCByeF90c19hc3NpZ25lZF9tYXNrOworCWludCByZXQ7CisKKwkvKiBSZXRy aWV2ZSBpbmZvIGZyb20gVFNBIHJlbGF0ZWQgY2VsbCAqLworCXJldCA9IHRzYV9nZXRfaW5mbyhx bWMtPnRzYSwgcW1jLT50c2FfY2VsbF9pZCwgJmluZm8pOworCWlmIChyZXQpCisJCXJldHVybiBy ZXQ7CisKKwkvKiBJZiBtb3JlIHRoYW4gMzIgVFMgYXJlIGFzc2lnbmVkIHRvIHRoaXMgY2VsbCwg b25lIGNvbW1vbiB0YWJsZQorCSAqIGlzIHVzZWQgZm9yIFR4IGFuZCBSeCBhbmQgc28gbWFza3Mg bXVzdCBiZSBlcXVhbCBmb3IgYWxsIGNoYW5uZWxzLgorCSAqLworCWlmICgoaW5mby5uYl90eF90 cyA+IDMyKSB8fCAoaW5mby5uYl9yeF90cyA+IDMyKSkgeworCQlpZiAoaW5mby5uYl90eF90cyAh PSBpbmZvLm5iX3J4X3RzKSB7CisJCQlkZXZfZXJyKHFtYy0+ZGV2LCAiTnVtYmVyIG9mIFRTQSBU eC9SeCBUUyBhc3NpZ25lZCBhcmUgbm90IGVxdWFsXG4iKTsKKwkJCXJldHVybiAtRUlOVkFMOwor CQl9CisJCWlzX29uZV90YWJsZSA9IHRydWU7CisJfQorCisKKwl0eF90c19hc3NpZ25lZF9tYXNr ID0gKCgodTY0KTEpIDw8IGluZm8ubmJfdHhfdHMpIC0gMTsKKwlyeF90c19hc3NpZ25lZF9tYXNr ID0gKCgodTY0KTEpIDw8IGluZm8ubmJfcnhfdHMpIC0gMTsKKworCWxpc3RfZm9yX2VhY2hfZW50 cnkoY2hhbiwgJnFtYy0+Y2hhbl9oZWFkLCBsaXN0KSB7CisJCWlmIChjaGFuLT50eF90c19tYXNr ID4gdHhfdHNfYXNzaWduZWRfbWFzaykgeworCQkJZGV2X2VycihxbWMtPmRldiwgImNoYW4gJXUg dXNlcyBUU0EgdW5hc3NpZ25lZCBUeCBUU1xuIiwgY2hhbi0+aWQpOworCQkJcmV0dXJuIC1FSU5W QUw7CisJCX0KKwkJaWYgKHR4X3RzX21hc2sgJiBjaGFuLT50eF90c19tYXNrKSB7CisJCQlkZXZf ZXJyKHFtYy0+ZGV2LCAiY2hhbiAldSB1c2VzIGFuIGFscmVhZHkgdXNlZCBUeCBUU1xuIiwgY2hh bi0+aWQpOworCQkJcmV0dXJuIC1FSU5WQUw7CisJCX0KKworCQlpZiAoY2hhbi0+cnhfdHNfbWFz ayA+IHJ4X3RzX2Fzc2lnbmVkX21hc2spIHsKKwkJCWRldl9lcnIocW1jLT5kZXYsICJjaGFuICV1 IHVzZXMgVFNBIHVuYXNzaWduZWQgUnggVFNcbiIsIGNoYW4tPmlkKTsKKwkJCXJldHVybiAtRUlO VkFMOworCQl9CisJCWlmIChyeF90c19tYXNrICYgY2hhbi0+cnhfdHNfbWFzaykgeworCQkJZGV2 X2VycihxbWMtPmRldiwgImNoYW4gJXUgdXNlcyBhbiBhbHJlYWR5IHVzZWQgUnggVFNcbiIsIGNo YW4tPmlkKTsKKwkJCXJldHVybiAtRUlOVkFMOworCQl9CisKKwkJaWYgKGlzX29uZV90YWJsZSAm JiAoY2hhbi0+dHhfdHNfbWFzayAhPSBjaGFuLT5yeF90c19tYXNrKSkgeworCQkJZGV2X2Vycihx bWMtPmRldiwgImNoYW4gJXUgdXNlcyBkaWZmZXJlbnQgUnggYW5kIFR4IFRTXG4iLCBjaGFuLT5p ZCk7CisJCQlyZXR1cm4gLUVJTlZBTDsKKwkJfQorCisJCXR4X3RzX21hc2sgfD0gY2hhbi0+dHhf dHNfbWFzazsKKwkJcnhfdHNfbWFzayB8PSBjaGFuLT5yeF90c19tYXNrOworCX0KKworCXJldHVy biAwOworfQorCitzdGF0aWMgdW5zaWduZWQgaW50IHFtY19uYl9jaGFucyhzdHJ1Y3QgcW1jICpx bWMpCit7CisJdW5zaWduZWQgaW50IGNvdW50ID0gMDsKKwlzdHJ1Y3QgcW1jX2NoYW4gKmNoYW47 CisKKwlsaXN0X2Zvcl9lYWNoX2VudHJ5KGNoYW4sICZxbWMtPmNoYW5faGVhZCwgbGlzdCkKKwkJ Y291bnQrKzsKKworCXJldHVybiBjb3VudDsKK30KKworc3RhdGljIGludCBxbWNfb2ZfcGFyc2Vf Y2hhbnMoc3RydWN0IHFtYyAqcW1jLCBzdHJ1Y3QgZGV2aWNlX25vZGUgKm5wKQoreworCXN0cnVj dCBkZXZpY2Vfbm9kZSAqY2hhbl9ucDsKKwlzdHJ1Y3QgcW1jX2NoYW4gKmNoYW47CisJY29uc3Qg Y2hhciAqbW9kZTsKKwl1MzIgY2hhbl9pZDsKKwl1NjQgdHNfbWFzazsKKwlpbnQgcmV0OworCisJ Zm9yX2VhY2hfYXZhaWxhYmxlX2NoaWxkX29mX25vZGUobnAsIGNoYW5fbnApIHsKKwkJcmV0ID0g b2ZfcHJvcGVydHlfcmVhZF91MzIoY2hhbl9ucCwgInJlZyIsICZjaGFuX2lkKTsKKwkJaWYgKHJl dCkgeworCQkJZGV2X2VycihxbWMtPmRldiwgIiVwT0Y6IGZhaWxlZCB0byByZWFkIHJlZ1xuIiwg Y2hhbl9ucCk7CisJCQlvZl9ub2RlX3B1dChjaGFuX25wKTsKKwkJCXJldHVybiByZXQ7CisJCX0K KwkJaWYgKGNoYW5faWQgPiA2MykgeworCQkJZGV2X2VycihxbWMtPmRldiwgIiVwT0Y6IEludmFs aWQgY2hhbl9pZFxuIiwgY2hhbl9ucCk7CisJCQlvZl9ub2RlX3B1dChjaGFuX25wKTsKKwkJCXJl dHVybiAtRUlOVkFMOworCQl9CisKKwkJY2hhbiA9IGRldm1fa3phbGxvYyhxbWMtPmRldiwgc2l6 ZW9mKCpjaGFuKSwgR0ZQX0tFUk5FTCk7CisJCWlmICghY2hhbikgeworCQkJb2Zfbm9kZV9wdXQo Y2hhbl9ucCk7CisJCQlyZXR1cm4gLUVOT01FTTsKKwkJfQorCisJCWNoYW4tPmlkID0gY2hhbl9p ZDsKKwkJc3Bpbl9sb2NrX2luaXQoJmNoYW4tPnJ4X2xvY2spOworCQlzcGluX2xvY2tfaW5pdCgm Y2hhbi0+dHhfbG9jayk7CisKKwkJcmV0ID0gb2ZfcHJvcGVydHlfcmVhZF91NjQoY2hhbl9ucCwg ImZzbCx0eC10cy1tYXNrIiwgJnRzX21hc2spOworCQlpZiAocmV0KSB7CisJCQlkZXZfZXJyKHFt Yy0+ZGV2LCAiJXBPRjogZmFpbGVkIHRvIHJlYWQgZnNsLHR4LXRzLW1hc2tcbiIsCisJCQkJY2hh bl9ucCk7CisJCQlvZl9ub2RlX3B1dChjaGFuX25wKTsKKwkJCXJldHVybiByZXQ7CisJCX0KKwkJ Y2hhbi0+dHhfdHNfbWFzayA9IHRzX21hc2s7CisKKwkJcmV0ID0gb2ZfcHJvcGVydHlfcmVhZF91 NjQoY2hhbl9ucCwgImZzbCxyeC10cy1tYXNrIiwgJnRzX21hc2spOworCQlpZiAocmV0KSB7CisJ CQlkZXZfZXJyKHFtYy0+ZGV2LCAiJXBPRjogZmFpbGVkIHRvIHJlYWQgZnNsLHJ4LXRzLW1hc2tc biIsCisJCQkJY2hhbl9ucCk7CisJCQlvZl9ub2RlX3B1dChjaGFuX25wKTsKKwkJCXJldHVybiBy ZXQ7CisJCX0KKwkJY2hhbi0+cnhfdHNfbWFzayA9IHRzX21hc2s7CisKKwkJbW9kZSA9ICJ0cmFu c3BhcmVudCI7CisJCXJldCA9IG9mX3Byb3BlcnR5X3JlYWRfc3RyaW5nKGNoYW5fbnAsICJmc2ws bW9kZSIsICZtb2RlKTsKKwkJaWYgKHJldCAmJiByZXQgIT0gLUVJTlZBTCkgeworCQkJZGV2X2Vy cihxbWMtPmRldiwgIiVwT0Y6IGZhaWxlZCB0byByZWFkIGZzbCxtb2RlXG4iLCBjaGFuX25wKTsK KwkJCW9mX25vZGVfcHV0KGNoYW5fbnApOworCQkJcmV0dXJuIHJldDsKKwkJfQorCQlpZiAoIXN0 cmNtcChtb2RlLCAidHJhbnNwYXJlbnQiKSkgeworCQkJY2hhbi0+bW9kZSA9IFFNQ19UUkFOU1BB UkVOVDsKKwkJfSBlbHNlIGlmICghc3RyY21wKG1vZGUsICJoZGxjIikpIHsKKwkJCWNoYW4tPm1v ZGUgPSBRTUNfSERMQzsKKwkJfSBlbHNlIHsKKwkJCWRldl9lcnIocW1jLT5kZXYsICIlcE9GOiBJ bnZhbGlkIGZzbCxtb2RlICglcylcbiIsIGNoYW5fbnAsCisJCQkJbW9kZSk7CisJCQlvZl9ub2Rl X3B1dChjaGFuX25wKTsKKwkJCXJldHVybiAtRUlOVkFMOworCQl9CisKKwkJY2hhbi0+aXNfcmV2 ZXJzZV9kYXRhID0gb2ZfcHJvcGVydHlfcmVhZF9ib29sKGNoYW5fbnAsCisJCQkJCQkJICAgICAg ImZzbCxyZXZlcnNlLWRhdGEiKTsKKworCQlsaXN0X2FkZF90YWlsKCZjaGFuLT5saXN0LCAmcW1j LT5jaGFuX2hlYWQpOworCQlxbWMtPmNoYW5zW2NoYW4tPmlkXSA9IGNoYW47CisJfQorCisJcmV0 dXJuIHFtY19jaGVja19jaGFucyhxbWMpOworfQorCitzdGF0aWMgaW50IHFtY19zZXR1cF90c2Ff NjRyeHR4KHN0cnVjdCBxbWMgKnFtYywgY29uc3Qgc3RydWN0IHRzYV9jZWxsX2luZm8gKmluZm8p Cit7CisJc3RydWN0IHFtY19jaGFuICpjaGFuOworCXVuc2lnbmVkIGludCBpOworCXUxNiB2YWw7 CisKKwkvKiBVc2UgYSBjb21tb24gVHgvUnggNjQgZW50cmllcyB0YWJsZQorCSAqIEV2ZXJ5dGhp bmcgd2FzIHByZXZpb3VzbHkgY2hlY2tlZCwgVHggYW5kIFJ4IHJlbGF0ZWQgc3R1ZmZzIGFyZQor CSAqIGlkZW50aWNhbCAtPiBVc2VkIFJ4IHJlbGF0ZWQgc3R1ZmYgdG8gYnVpbGQgdGhlIHRhYmxl CisJICovCisKKwkvKiBJbnZhbGlkYXRlIGFsbCBlbnRyaWVzICovCisJZm9yIChpID0gMDsgaSA8 IDY0OyBpKyspCisJCXFtY193cml0ZTE2KHFtYy0+c2NjX3ByYW0gKyBRTUNfR0JMX1RTQVRSWCAr IChpICogMiksIDB4MDAwMCk7CisKKwkvKiBTZXQgZW50cmllcyBiYXNlZCBvbiBSeCBzdHVmZiov CisJbGlzdF9mb3JfZWFjaF9lbnRyeShjaGFuLCAmcW1jLT5jaGFuX2hlYWQsIGxpc3QpIHsKKwkJ Zm9yIChpID0gMDsgaSA8IGluZm8tPm5iX3J4X3RzOyBpKyspIHsKKwkJCWlmICghKGNoYW4tPnJ4 X3RzX21hc2sgJiAoKCh1NjQpMSkgPDwgaSkpKQorCQkJCWNvbnRpbnVlOworCisJCQl2YWwgPSBR TUNfVFNBX1ZBTElEIHwgUU1DX1RTQV9NQVNLIHwKKwkJCSAgICAgIFFNQ19UU0FfQ0hBTk5FTChj aGFuLT5pZCk7CisJCQlxbWNfd3JpdGUxNihxbWMtPnNjY19wcmFtICsgUU1DX0dCTF9UU0FUUlgg KyAoaSAqIDIpLCB2YWwpOworCQl9CisJfQorCisJLyogU2V0IFdyYXAgYml0IG9uIGxhc3QgZW50 cnkgKi8KKwlxbWNfc2V0Yml0czE2KHFtYy0+c2NjX3ByYW0gKyBRTUNfR0JMX1RTQVRSWCArICgo aW5mby0+bmJfcnhfdHMgLSAxKSAqIDIpLAorCQkgICAgICBRTUNfVFNBX1dSQVApOworCisJLyog SW5pdCBwb2ludGVycyB0byB0aGUgdGFibGUgKi8KKwl2YWwgPSBxbWMtPnNjY19wcmFtX29mZnNl dCArIFFNQ19HQkxfVFNBVFJYOworCXFtY193cml0ZTE2KHFtYy0+c2NjX3ByYW0gKyBRTUNfR0JM X1JYX1NfUFRSLCB2YWwpOworCXFtY193cml0ZTE2KHFtYy0+c2NjX3ByYW0gKyBRTUNfR0JMX1JY UFRSLCB2YWwpOworCXFtY193cml0ZTE2KHFtYy0+c2NjX3ByYW0gKyBRTUNfR0JMX1RYX1NfUFRS LCB2YWwpOworCXFtY193cml0ZTE2KHFtYy0+c2NjX3ByYW0gKyBRTUNfR0JMX1RYUFRSLCB2YWwp OworCisJcmV0dXJuIDA7Cit9CisKK3N0YXRpYyBpbnQgcW1jX3NldHVwX3RzYV8zMnJ4XzMydHgo c3RydWN0IHFtYyAqcW1jLCBjb25zdCBzdHJ1Y3QgdHNhX2NlbGxfaW5mbyAqaW5mbykKK3sKKwlz dHJ1Y3QgcW1jX2NoYW4gKmNoYW47CisJdW5zaWduZWQgaW50IGk7CisJdTE2IHZhbDsKKworCS8q IFVzZSBhIFR4IDMyIGVudHJpZXMgdGFibGUgYW5kIGEgUnggMzIgZW50cmllcyB0YWJsZS4KKwkg KiBFdmVyeXRoaW5nIHdhcyBwcmV2aW91c2x5IGNoZWNrZWQuCisJICovCisKKwkvKiBJbnZhbGlk YXRlIGFsbCBlbnRyaWVzICovCisJZm9yIChpID0gMDsgaSA8IDMyOyBpKyspIHsKKwkJcW1jX3dy aXRlMTYocW1jLT5zY2NfcHJhbSArIFFNQ19HQkxfVFNBVFJYICsgKGkgKiAyKSwgMHgwMDAwKTsK KwkJcW1jX3dyaXRlMTYocW1jLT5zY2NfcHJhbSArIFFNQ19HQkxfVFNBVFRYICsgKGkgKiAyKSwg MHgwMDAwKTsKKwl9CisKKwkvKiBTZXQgZW50cmllcyBiYXNlZCBvbiBSeCBhbmQgVHggc3R1ZmYq LworCWxpc3RfZm9yX2VhY2hfZW50cnkoY2hhbiwgJnFtYy0+Y2hhbl9oZWFkLCBsaXN0KSB7CisJ CS8qIFJ4IHBhcnQgKi8KKwkJZm9yIChpID0gMDsgaSA8IGluZm8tPm5iX3J4X3RzOyBpKyspIHsK KwkJCWlmICghKGNoYW4tPnJ4X3RzX21hc2sgJiAoKCh1NjQpMSkgPDwgaSkpKQorCQkJCWNvbnRp bnVlOworCisJCQl2YWwgPSBRTUNfVFNBX1ZBTElEIHwgUU1DX1RTQV9NQVNLIHwKKwkJCSAgICAg IFFNQ19UU0FfQ0hBTk5FTChjaGFuLT5pZCk7CisJCQlxbWNfd3JpdGUxNihxbWMtPnNjY19wcmFt ICsgUU1DX0dCTF9UU0FUUlggKyAoaSAqIDIpLCB2YWwpOworCQl9CisJCS8qIFR4IHBhcnQgKi8K KwkJZm9yIChpID0gMDsgaSA8IGluZm8tPm5iX3R4X3RzOyBpKyspIHsKKwkJCWlmICghKGNoYW4t PnR4X3RzX21hc2sgJiAoKCh1NjQpMSkgPDwgaSkpKQorCQkJCWNvbnRpbnVlOworCisJCQl2YWwg PSBRTUNfVFNBX1ZBTElEIHwgUU1DX1RTQV9NQVNLIHwKKwkJCSAgICAgIFFNQ19UU0FfQ0hBTk5F TChjaGFuLT5pZCk7CisJCQlxbWNfd3JpdGUxNihxbWMtPnNjY19wcmFtICsgUU1DX0dCTF9UU0FU VFggKyAoaSAqIDIpLCB2YWwpOworCQl9CisJfQorCisJLyogU2V0IFdyYXAgYml0IG9uIGxhc3Qg ZW50cmllcyAqLworCXFtY19zZXRiaXRzMTYocW1jLT5zY2NfcHJhbSArIFFNQ19HQkxfVFNBVFJY ICsgKChpbmZvLT5uYl9yeF90cyAtIDEpICogMiksCisJCSAgICAgIFFNQ19UU0FfV1JBUCk7CisJ cW1jX3NldGJpdHMxNihxbWMtPnNjY19wcmFtICsgUU1DX0dCTF9UU0FUVFggKyAoKGluZm8tPm5i X3R4X3RzIC0gMSkgKiAyKSwKKwkJICAgICAgUU1DX1RTQV9XUkFQKTsKKworCS8qIEluaXQgUngg cG9pbnRlcnMgLi4uKi8KKwl2YWwgPSBxbWMtPnNjY19wcmFtX29mZnNldCArIFFNQ19HQkxfVFNB VFJYOworCXFtY193cml0ZTE2KHFtYy0+c2NjX3ByYW0gKyBRTUNfR0JMX1JYX1NfUFRSLCB2YWwp OworCXFtY193cml0ZTE2KHFtYy0+c2NjX3ByYW0gKyBRTUNfR0JMX1JYUFRSLCB2YWwpOworCisJ LyogLi4uIGFuZCBUeCBwb2ludGVycyAqLworCXZhbCA9IHFtYy0+c2NjX3ByYW1fb2Zmc2V0ICsg UU1DX0dCTF9UU0FUVFg7CisJcW1jX3dyaXRlMTYocW1jLT5zY2NfcHJhbSArIFFNQ19HQkxfVFhf U19QVFIsIHZhbCk7CisJcW1jX3dyaXRlMTYocW1jLT5zY2NfcHJhbSArIFFNQ19HQkxfVFhQVFIs IHZhbCk7CisKKwlyZXR1cm4gMDsKK30KKworc3RhdGljIGludCBxbWNfc2V0dXBfdHNhKHN0cnVj dCBxbWMgKnFtYykKK3sKKwlzdHJ1Y3QgdHNhX2NlbGxfaW5mbyBpbmZvOworCWludCByZXQ7CisK KwkvKiBSZXRyaWV2ZSBpbmZvIGZyb20gVFNBIHJlbGF0ZWQgY2VsbCAqLworCXJldCA9IHRzYV9n ZXRfaW5mbyhxbWMtPnRzYSwgcW1jLT50c2FfY2VsbF9pZCwgJmluZm8pOworCWlmIChyZXQpCisJ CXJldHVybiByZXQ7CisKKwkvKiBTZXR1cCBvbmUgY29tbW9uIDY0IGVudHJpZXMgdGFibGUgb3Ig dHdvIDMyIGVudHJpZXMgKG9uZSBmb3IgVHggYW5kCisJICogb25lIGZvciBUeCkgYWNjb3JkaW5n IHRvIGFzc2lnbmVkIFRTIG51bWJlcnMuCisJICovCisJcmV0dXJuICgoaW5mby5uYl90eF90cyA+ IDMyKSB8fCAoaW5mby5uYl9yeF90cyA+IDMyKSkgPworCQlxbWNfc2V0dXBfdHNhXzY0cnh0eChx bWMsICZpbmZvKSA6CisJCXFtY19zZXR1cF90c2FfMzJyeF8zMnR4KHFtYywgJmluZm8pOworfQor CitzdGF0aWMgaW50IHFtY19zZXR1cF9jaGFuX3RybnN5bmMoc3RydWN0IHFtYyAqcW1jLCBzdHJ1 Y3QgcW1jX2NoYW4gKmNoYW4pCit7CisJc3RydWN0IHRzYV9jZWxsX2luZm8gaW5mbzsKKwl1MTYg Zmlyc3RfcngsIGxhc3RfdHg7CisJdTE2IHRybnN5bmM7CisJaW50IHJldDsKKworCS8qIFJldHJp ZXZlIGluZm8gZnJvbSBUU0EgcmVsYXRlZCBjZWxsICovCisJcmV0ID0gdHNhX2dldF9pbmZvKHFt Yy0+dHNhLCBxbWMtPnRzYV9jZWxsX2lkLCAmaW5mbyk7CisJaWYgKHJldCkKKwkJcmV0dXJuIHJl dDsKKworCS8qIEZpbmQgdGhlIGZpcnN0IFJ4IFRTIGFsbG9jYXRlZCB0byB0aGUgY2hhbm5lbCAq LworCWZpcnN0X3J4ID0gY2hhbi0+cnhfdHNfbWFzayA/IF9fZmZzNjQoY2hhbi0+cnhfdHNfbWFz aykgKyAxIDogMDsKKworCS8qIEZpbmQgdGhlIGxhc3QgVHggVFMgYWxsb2NhdGVkIHRvIHRoZSBj aGFubmVsICovCisJbGFzdF90eCA9IGZsczY0KGNoYW4tPnR4X3RzX21hc2spOworCisJdHJuc3lu YyA9IDA7CisJaWYgKGluZm8ubmJfcnhfdHMpCisJCXRybnN5bmMgfD0gUU1DX1NQRV9UUk5TWU5D X1JYKChmaXJzdF9yeCAlIGluZm8ubmJfcnhfdHMpICogMik7CisJaWYgKGluZm8ubmJfdHhfdHMp CisJCXRybnN5bmMgfD0gUU1DX1NQRV9UUk5TWU5DX1RYKChsYXN0X3R4ICUgaW5mby5uYl90eF90 cykgKiAyKTsKKworCXFtY193cml0ZTE2KGNoYW4tPnNfcGFyYW0gKyBRTUNfU1BFX1RSTlNZTkMs IHRybnN5bmMpOworCisJZGV2X2RiZyhxbWMtPmRldiwgImNoYW4gJXU6IHRybnN5bmM9MHglMDR4 LCByeCAldS8ldSAweCVsbHgsIHR4ICV1LyV1IDB4JWxseFxuIiwKKwkJY2hhbi0+aWQsIHRybnN5 bmMsCisJCWZpcnN0X3J4LCBpbmZvLm5iX3J4X3RzLCBjaGFuLT5yeF90c19tYXNrLAorCQlsYXN0 X3R4LCBpbmZvLm5iX3R4X3RzLCBjaGFuLT50eF90c19tYXNrKTsKKworCXJldHVybiAwOworfQor CitzdGF0aWMgaW50IHFtY19zZXR1cF9jaGFuKHN0cnVjdCBxbWMgKnFtYywgc3RydWN0IHFtY19j aGFuICpjaGFuKQoreworCXVuc2lnbmVkIGludCBpOworCWNiZF90IF9faW9tZW0gKmJkOworCWlu dCByZXQ7CisJdTE2IHZhbDsKKworCWNoYW4tPnFtYyA9IHFtYzsKKworCS8qIFNldCBjaGFubmVs IHNwZWNpZmljIHBhcmFtZXRlciBiYXNlIGFkZHJlc3MgKi8KKwljaGFuLT5zX3BhcmFtID0gcW1j LT5kcHJhbSArIChjaGFuLT5pZCAqIDY0KTsKKwkvKiAxNiBiZCBwZXIgY2hhbm5lbCAoOCByeCBh bmQgOCB0eCkgKi8KKwljaGFuLT50eGJkcyA9IHFtYy0+YmRfdGFibGUgKyAoY2hhbi0+aWQgKiAo UU1DX05CX1RYQkRTICsgUU1DX05CX1JYQkRTKSk7CisJY2hhbi0+cnhiZHMgPSBxbWMtPmJkX3Rh YmxlICsgKGNoYW4tPmlkICogKFFNQ19OQl9UWEJEUyArIFFNQ19OQl9SWEJEUykpICsgUU1DX05C X1RYQkRTOworCisJY2hhbi0+dHhiZF9mcmVlID0gY2hhbi0+dHhiZHM7CisJY2hhbi0+dHhiZF9k b25lID0gY2hhbi0+dHhiZHM7CisJY2hhbi0+cnhiZF9mcmVlID0gY2hhbi0+cnhiZHM7CisJY2hh bi0+cnhiZF9kb25lID0gY2hhbi0+cnhiZHM7CisKKwkvKiBUQkFTRSBhbmQgVEJQVFIqLworCXZh bCA9IGNoYW4tPmlkICogKFFNQ19OQl9UWEJEUyArIFFNQ19OQl9SWEJEUykgKiBzaXplb2YoY2Jk X3QpOworCXFtY193cml0ZTE2KGNoYW4tPnNfcGFyYW0gKyBRTUNfU1BFX1RCQVNFLCB2YWwpOwor CXFtY193cml0ZTE2KGNoYW4tPnNfcGFyYW0gKyBRTUNfU1BFX1RCUFRSLCB2YWwpOworCisJLyog UkJBU0UgYW5kIFJCUFRSKi8KKwl2YWwgPSAoKGNoYW4tPmlkICogKFFNQ19OQl9UWEJEUyArIFFN Q19OQl9SWEJEUykpICsgUU1DX05CX1RYQkRTKSAqIHNpemVvZihjYmRfdCk7CisJcW1jX3dyaXRl MTYoY2hhbi0+c19wYXJhbSArIFFNQ19TUEVfUkJBU0UsIHZhbCk7CisJcW1jX3dyaXRlMTYoY2hh bi0+c19wYXJhbSArIFFNQ19TUEVfUkJQVFIsIHZhbCk7CisJcW1jX3dyaXRlMzIoY2hhbi0+c19w YXJhbSArIFFNQ19TUEVfVFNUQVRFLCAweDMwMDAwMDAwKTsKKwlxbWNfd3JpdGUzMihjaGFuLT5z X3BhcmFtICsgUU1DX1NQRV9SU1RBVEUsIDB4MzEwMDAwMDApOworCXFtY193cml0ZTMyKGNoYW4t PnNfcGFyYW0gKyBRTUNfU1BFX1pJU1RBVEUsIDB4MDAwMDAxMDApOworCWlmIChjaGFuLT5tb2Rl ID09IFFNQ19UUkFOU1BBUkVOVCkgeworCQlxbWNfd3JpdGUzMihjaGFuLT5zX3BhcmFtICsgUU1D X1NQRV9aRFNUQVRFLCAweDE4MDAwMDgwKTsKKwkJcW1jX3dyaXRlMTYoY2hhbi0+c19wYXJhbSAr IFFNQ19TUEVfVE1SQkxSLCA2MCk7CisJCXZhbCA9IFFNQ19TUEVfQ0hBTVJfTU9ERV9UUkFOU1Ag fCBRTUNfU1BFX0NIQU1SX1RSQU5TUF9TWU5DOworCQlpZiAoY2hhbi0+aXNfcmV2ZXJzZV9kYXRh KQorCQkJdmFsIHw9IFFNQ19TUEVfQ0hBTVJfVFJBTlNQX1JEOworCQlxbWNfd3JpdGUxNihjaGFu LT5zX3BhcmFtICsgUU1DX1NQRV9DSEFNUiwgdmFsKTsKKwkJcmV0ID0gcW1jX3NldHVwX2NoYW5f dHJuc3luYyhxbWMsIGNoYW4pOworCQlpZiAocmV0KQorCQkJcmV0dXJuIHJldDsKKwl9IGVsc2Ug eworCQlxbWNfd3JpdGUzMihjaGFuLT5zX3BhcmFtICsgUU1DX1NQRV9aRFNUQVRFLCAweDAwMDAw MDgwKTsKKwkJcW1jX3dyaXRlMTYoY2hhbi0+c19wYXJhbSArIFFNQ19TUEVfTUZMUiwgNjApOwor CQlxbWNfd3JpdGUxNihjaGFuLT5zX3BhcmFtICsgUU1DX1NQRV9DSEFNUiwKKwkJCVFNQ19TUEVf Q0hBTVJfTU9ERV9IRExDIHwgUU1DX1NQRV9DSEFNUl9IRExDX0lETE0pOworCX0KKworCS8qIERv IG5vdCBlbmFibGUgaW50ZXJydXB0cyBub3cuIFRoZXkgd2lsbCBiZSBlbmFibGVkIGxhdGVyICov CisJcW1jX3dyaXRlMTYoY2hhbi0+c19wYXJhbSArIFFNQ19TUEVfSU5UTVNLLCAweDAwMDApOwor CisJLyogSW5pdCBSeCBCRHMgYW5kIHNldCBXcmFwIGJpdCBvbiBsYXN0IGRlc2NyaXB0b3IgKi8K KwlCVUlMRF9CVUdfT04oUU1DX05CX1JYQkRTID09IDApOworCXZhbCA9IFFNQ19CRF9SWF9JOwor CWZvciAoaSA9IDA7IGkgPCBRTUNfTkJfUlhCRFM7IGkrKykgeworCQliZCA9IGNoYW4tPnJ4YmRz ICsgaTsKKwkJcW1jX3dyaXRlMTYoJmJkLT5jYmRfc2MsIHZhbCk7CisJfQorCWJkID0gY2hhbi0+ cnhiZHMgKyBRTUNfTkJfUlhCRFMgLSAxOworCXFtY193cml0ZTE2KCZiZC0+Y2JkX3NjLCB2YWwg fCBRTUNfQkRfUlhfVyk7CisKKwkvKiBJbml0IFR4IEJEcyBhbmQgc2V0IFdyYXAgYml0IG9uIGxh c3QgZGVzY3JpcHRvciAqLworCUJVSUxEX0JVR19PTihRTUNfTkJfVFhCRFMgPT0gMCk7CisJdmFs ID0gUU1DX0JEX1RYX0k7CisJaWYgKGNoYW4tPm1vZGUgPT0gUU1DX0hETEMpCisJCXZhbCB8PSBR TUNfQkRfVFhfTCB8IFFNQ19CRF9UWF9UQzsKKwlmb3IgKGkgPSAwOyBpIDwgUU1DX05CX1RYQkRT OyBpKyspIHsKKwkJYmQgPSBjaGFuLT50eGJkcyArIGk7CisJCXFtY193cml0ZTE2KCZiZC0+Y2Jk X3NjLCB2YWwpOworCX0KKwliZCA9IGNoYW4tPnR4YmRzICsgUU1DX05CX1RYQkRTIC0gMTsKKwlx bWNfd3JpdGUxNigmYmQtPmNiZF9zYywgdmFsIHwgUU1DX0JEX1RYX1cpOworCisJcmV0dXJuIDA7 Cit9CisKK3N0YXRpYyBpbnQgcW1jX3NldHVwX2NoYW5zKHN0cnVjdCBxbWMgKnFtYykKK3sKKwlz dHJ1Y3QgcW1jX2NoYW4gKmNoYW47CisJaW50IHJldDsKKworCWxpc3RfZm9yX2VhY2hfZW50cnko Y2hhbiwgJnFtYy0+Y2hhbl9oZWFkLCBsaXN0KSB7CisJCXJldCA9IHFtY19zZXR1cF9jaGFuKHFt YywgY2hhbik7CisJCWlmIChyZXQpCisJCQlyZXR1cm4gcmV0OworCX0KKworCXJldHVybiAwOwor fQorCitzdGF0aWMgaW50IHFtY19maW5hbGl6ZV9jaGFucyhzdHJ1Y3QgcW1jICpxbWMpCit7CisJ c3RydWN0IHFtY19jaGFuICpjaGFuOworCWludCByZXQ7CisKKwlsaXN0X2Zvcl9lYWNoX2VudHJ5 KGNoYW4sICZxbWMtPmNoYW5faGVhZCwgbGlzdCkgeworCQkvKiBVbm1hc2sgY2hhbm5lbCBpbnRl cnJ1cHRzICovCisJCWlmIChjaGFuLT5tb2RlID09IFFNQ19IRExDKSB7CisJCQlxbWNfd3JpdGUx NihjaGFuLT5zX3BhcmFtICsgUU1DX1NQRV9JTlRNU0ssCisJCQkJICAgIFFNQ19JTlRfTklEIHwg UU1DX0lOVF9JREwgfCBRTUNfSU5UX01SRiB8CisJCQkJICAgIFFNQ19JTlRfVU4gfCBRTUNfSU5U X1JYRiB8IFFNQ19JTlRfQlNZIHwKKwkJCQkgICAgUU1DX0lOVF9UWEIgfCBRTUNfSU5UX1JYQik7 CisJCX0gZWxzZSB7CisJCQlxbWNfd3JpdGUxNihjaGFuLT5zX3BhcmFtICsgUU1DX1NQRV9JTlRN U0ssCisJCQkJICAgIFFNQ19JTlRfVU4gfCBRTUNfSU5UX0JTWSB8CisJCQkJICAgIFFNQ19JTlRf VFhCIHwgUU1DX0lOVF9SWEIpOworCQl9CisKKwkJLyogRm9yY2VkIHN0b3AgdGhlIGNoYW5uZWwg Ki8KKwkJcmV0ID0gcW1jX2NoYW5fc3RvcChjaGFuLCBRTUNfQ0hBTl9BTEwpOworCQlpZiAocmV0 KQorCQkJcmV0dXJuIHJldDsKKwl9CisKKwlyZXR1cm4gMDsKK30KKworc3RhdGljIGludCBxbWNf c2V0dXBfaW50cyhzdHJ1Y3QgcW1jICpxbWMpCit7CisJdW5zaWduZWQgaW50IGk7CisJdTE2IF9f aW9tZW0gKmxhc3Q7CisKKwkvKiBSYXogYWxsIGVudHJpZXMgKi8KKwlmb3IgKGkgPSAwOyBpIDwg KHFtYy0+aW50X3NpemUgLyBzaXplb2YodTE2KSk7IGkrKykKKwkJcW1jX3dyaXRlMTYocW1jLT5p bnRfdGFibGUgKyBpLCAweDAwMDApOworCisJLyogU2V0IFdyYXAgYml0IG9uIGxhc3QgZW50cnkg Ki8KKwlpZiAocW1jLT5pbnRfc2l6ZSA+PSBzaXplb2YodTE2KSkgeworCQlsYXN0ID0gcW1jLT5p bnRfdGFibGUgKyAocW1jLT5pbnRfc2l6ZSAvIHNpemVvZih1MTYpKSAtIDE7CisJCXFtY193cml0 ZTE2KGxhc3QsIFFNQ19JTlRfVyk7CisJfQorCisJcmV0dXJuIDA7Cit9CisKK3N0YXRpYyB2b2lk IHFtY19pcnFfZ2ludChzdHJ1Y3QgcW1jICpxbWMpCit7CisJc3RydWN0IHFtY19jaGFuICpjaGFu OworCXVuc2lnbmVkIGludCBjaGFuX2lkOworCXVuc2lnbmVkIGxvbmcgZmxhZ3M7CisJdTE2IGlu dF9lbnRyeTsKKworCWludF9lbnRyeSA9IHFtY19yZWFkMTYocW1jLT5pbnRfY3Vycik7CisJd2hp bGUgKGludF9lbnRyeSAmIFFNQ19JTlRfVikgeworCQkvKiBDbGVhciBhbGwgYnV0IHRoZSBXcmFw IGJpdCAqLworCQlxbWNfd3JpdGUxNihxbWMtPmludF9jdXJyLCBpbnRfZW50cnkgJiBRTUNfSU5U X1cpOworCisJCWNoYW5faWQgPSBRTUNfSU5UX0dFVF9DSEFOTkVMKGludF9lbnRyeSk7CisJCWNo YW4gPSBxbWMtPmNoYW5zW2NoYW5faWRdOworCQlpZiAoIWNoYW4pIHsKKwkJCWRldl9lcnIocW1j LT5kZXYsICJpbnRlcnJ1cHQgb24gaW52YWxpZCBjaGFuICV1XG4iLCBjaGFuX2lkKTsKKwkJCWdv dG8gaW50X25leHQ7CisJCX0KKworCQlpZiAoaW50X2VudHJ5ICYgUU1DX0lOVF9UWEIpCisJCQlx bWNfY2hhbl93cml0ZV9kb25lKGNoYW4pOworCisJCWlmIChpbnRfZW50cnkgJiBRTUNfSU5UX1VO KSB7CisJCQlkZXZfaW5mbyhxbWMtPmRldiwgImludHIgY2hhbiAldSwgMHglMDR4IChVTilcbiIs IGNoYW5faWQsCisJCQkJIGludF9lbnRyeSk7CisJCQljaGFuLT5uYl90eF91bmRlcnJ1bisrOwor CQl9CisKKwkJaWYgKGludF9lbnRyeSAmIFFNQ19JTlRfQlNZKSB7CisJCQlkZXZfaW5mbyhxbWMt PmRldiwgImludHIgY2hhbiAldSwgMHglMDR4IChCU1kpXG4iLCBjaGFuX2lkLAorCQkJCSBpbnRf ZW50cnkpOworCQkJY2hhbi0+bmJfcnhfYnVzeSsrOworCQkJLyogUmVzdGFydCB0aGUgcmVjZWl2 ZXIgaWYgbmVlZGVkICovCisJCQlzcGluX2xvY2tfaXJxc2F2ZSgmY2hhbi0+cnhfbG9jaywgZmxh Z3MpOworCQkJaWYgKGNoYW4tPnJ4X3BlbmRpbmcgJiYgIWNoYW4tPmlzX3J4X3N0b3BwZWQpIHsK KwkJCQlpZiAoY2hhbi0+bW9kZSA9PSBRTUNfVFJBTlNQQVJFTlQpCisJCQkJCXFtY193cml0ZTMy KGNoYW4tPnNfcGFyYW0gKyBRTUNfU1BFX1pEU1RBVEUsIDB4MTgwMDAwODApOworCQkJCWVsc2UK KwkJCQkJcW1jX3dyaXRlMzIoY2hhbi0+c19wYXJhbSArIFFNQ19TUEVfWkRTVEFURSwgMHgwMDAw MDA4MCk7CisJCQkJcW1jX3dyaXRlMzIoY2hhbi0+c19wYXJhbSArIFFNQ19TUEVfUlNUQVRFLCAw eDMxMDAwMDAwKTsKKwkJCQljaGFuLT5pc19yeF9oYWx0ZWQgPSBmYWxzZTsKKwkJCX0gZWxzZSB7 CisJCQkJY2hhbi0+aXNfcnhfaGFsdGVkID0gdHJ1ZTsKKwkJCX0KKwkJCXNwaW5fdW5sb2NrX2ly cXJlc3RvcmUoJmNoYW4tPnJ4X2xvY2ssIGZsYWdzKTsKKwkJfQorCisJCWlmIChpbnRfZW50cnkg JiBRTUNfSU5UX1JYQikKKwkJCXFtY19jaGFuX3JlYWRfZG9uZShjaGFuKTsKKworaW50X25leHQ6 CisJCWlmIChpbnRfZW50cnkgJiBRTUNfSU5UX1cpCisJCQlxbWMtPmludF9jdXJyID0gcW1jLT5p bnRfdGFibGU7CisJCWVsc2UKKwkJCXFtYy0+aW50X2N1cnIrKzsKKwkJaW50X2VudHJ5ID0gcW1j X3JlYWQxNihxbWMtPmludF9jdXJyKTsKKwl9Cit9CisKK3N0YXRpYyBpcnFyZXR1cm5fdCBxbWNf aXJxX2hhbmRsZXIoaW50IGlycSwgdm9pZCAqcHJpdikKK3sKKwlzdHJ1Y3QgcW1jICpxbWMgPSAo c3RydWN0IHFtYyAqKXByaXY7CisJdTE2IHNjY2U7CisKKwlzY2NlID0gcW1jX3JlYWQxNihxbWMt PnNjY19yZWdzICsgU0NDX1NDQ0UpOworCXFtY193cml0ZTE2KHFtYy0+c2NjX3JlZ3MgKyBTQ0Nf U0NDRSwgc2NjZSk7CisKKwlpZiAodW5saWtlbHkoc2NjZSAmIFNDQ19TQ0NFX0lRT1YpKQorCQlk ZXZfaW5mbyhxbWMtPmRldiwgIklSUSBxdWV1ZSBvdmVyZmxvd1xuIik7CisKKwlpZiAodW5saWtl bHkoc2NjZSAmIFNDQ19TQ0NFX0dVTikpCisJCWRldl9lcnIocW1jLT5kZXYsICJHbG9iYWwgdHJh bnNtaXR0ZXIgdW5kZXJydW5cbiIpOworCisJaWYgKHVubGlrZWx5KHNjY2UgJiBTQ0NfU0NDRV9H T1YpKQorCQlkZXZfZXJyKHFtYy0+ZGV2LCAiR2xvYmFsIHJlY2VpdmVyIG92ZXJydW5cbiIpOwor CisJLyogbm9ybWFsIGludGVycnVwdCAqLworCWlmIChsaWtlbHkoc2NjZSAmIFNDQ19TQ0NFX0dJ TlQpKQorCQlxbWNfaXJxX2dpbnQocW1jKTsKKworCXJldHVybiBJUlFfSEFORExFRDsKK30KKwor c3RhdGljIGludCBxbWNfcHJvYmUoc3RydWN0IHBsYXRmb3JtX2RldmljZSAqcGRldikKK3sKKwlz dHJ1Y3QgZGV2aWNlX25vZGUgKm5wID0gcGRldi0+ZGV2Lm9mX25vZGU7CisJdW5zaWduZWQgaW50 IG5iX2NoYW5zOworCXN0cnVjdCByZXNvdXJjZSAqcmVzOworCXN0cnVjdCBxbWMgKnFtYzsKKwl1 MzIgdmFsOworCWludCBpcnE7CisJaW50IHJldDsKKworCXFtYyA9IGRldm1fa3phbGxvYygmcGRl di0+ZGV2LCBzaXplb2YoKnFtYyksIEdGUF9LRVJORUwpOworCWlmICghcW1jKQorCQlyZXR1cm4g LUVOT01FTTsKKworCXFtYy0+ZGV2ID0gJnBkZXYtPmRldjsKKwlJTklUX0xJU1RfSEVBRCgmcW1j LT5jaGFuX2hlYWQpOworCisJcW1jLT5zY2NfcmVncyA9IGRldm1fcGxhdGZvcm1faW9yZW1hcF9y ZXNvdXJjZV9ieW5hbWUocGRldiwgInNjY19yZWdzIik7CisJaWYgKElTX0VSUihxbWMtPnNjY19y ZWdzKSkKKwkJcmV0dXJuIFBUUl9FUlIocW1jLT5zY2NfcmVncyk7CisKKworCXJlcyA9IHBsYXRm b3JtX2dldF9yZXNvdXJjZV9ieW5hbWUocGRldiwgSU9SRVNPVVJDRV9NRU0sICJzY2NfcHJhbSIp OworCWlmICghcmVzKQorCQlyZXR1cm4gLUVJTlZBTDsKKwlxbWMtPnNjY19wcmFtX29mZnNldCA9 IHJlcy0+c3RhcnQgLSBnZXRfaW1tcmJhc2UoKTsKKwlxbWMtPnNjY19wcmFtID0gZGV2bV9pb3Jl bWFwX3Jlc291cmNlKHFtYy0+ZGV2LCByZXMpOworCWlmIChJU19FUlIocW1jLT5zY2NfcHJhbSkp CisJCXJldHVybiBQVFJfRVJSKHFtYy0+c2NjX3ByYW0pOworCisJcW1jLT5kcHJhbSAgPSBkZXZt X3BsYXRmb3JtX2lvcmVtYXBfcmVzb3VyY2VfYnluYW1lKHBkZXYsICJkcHJhbSIpOworCWlmIChJ U19FUlIocW1jLT5kcHJhbSkpCisJCXJldHVybiBQVFJfRVJSKHFtYy0+ZHByYW0pOworCisJcmV0 ID0gb2ZfcHJvcGVydHlfcmVhZF91MzIobnAsICJmc2wsdHNhLWNlbGwtaWQiLCAmdmFsKTsKKwlp ZiAocmV0KSB7CisJCWRldl9lcnIocW1jLT5kZXYsICIlcE9GOiBmYWlsZWQgdG8gcmVhZCBmc2ws dHNhLWNlbGwtaWRcbiIsIG5wKTsKKwkJcmV0dXJuIHJldDsKKwl9CisJcW1jLT50c2FfY2VsbF9p ZCA9IHZhbDsKKworCXFtYy0+dHNhID0gZGV2bV90c2FfZ2V0X2J5cGhhbmRsZShxbWMtPmRldiwg bnAsICJmc2wsdHNhIik7CisJaWYgKElTX0VSUihxbWMtPnRzYSkpIHsKKwkJcmV0dXJuIGRldl9l cnJfcHJvYmUocW1jLT5kZXYsIFBUUl9FUlIocW1jLT50c2EpLAorCQkJCSAgICAgIkZhaWxlZCB0 byBnZXQgVFNBXG4iKTsKKwl9CisKKwkvKiBDb25uZWN0IHRoZSBTQ0MgdG8gVFNBICovCisJcmV0 ID0gdHNhX2Nvbm5lY3QocW1jLT50c2EsIHFtYy0+dHNhX2NlbGxfaWQpOworCWlmIChyZXQpIHsK KwkJZGV2X2VycihxbWMtPmRldiwgIkZhaWxlZCB0byBjb25uZWN0IFRTQVxuIik7CisJCXJldHVy biByZXQ7CisJfQorCisJLyogUGFyc2UgY2hhbm5lbHMgaW5mb3JtYXRpb25zcyAqLworCXJldCA9 IHFtY19vZl9wYXJzZV9jaGFucyhxbWMsIG5wKTsKKwlpZiAocmV0KQorCQlnb3RvIGVycl90c2Ff ZGlzY29ubmVjdDsKKworCW5iX2NoYW5zID0gcW1jX25iX2NoYW5zKHFtYyk7CisKKwkvKiBJbml0 IEdNU1JfSCBhbmQgR01TUl9MIHJlZ2lzdGVycyAqLworCXFtY193cml0ZTMyKHFtYy0+c2NjX3Jl Z3MgKyBTQ0NfR1NNUkgsCisJCSAgICBTQ0NfR1NNUkhfQ0RTIHwgU0NDX0dTTVJIX0NUU1MgfCBT Q0NfR1NNUkhfQ0RQIHwgU0NDX0dTTVJIX0NUU1ApOworCisJLyogZW5hYmxlIFFNQyBtb2RlICov CisJcW1jX3dyaXRlMzIocW1jLT5zY2NfcmVncyArIFNDQ19HU01STCwgU0NDX0dTTVJMX01PREVf UU1DKTsKKworCS8qIEFsbG9jYXRlIHRoZSBidWZmZXIgZGVzY3JpcHRvciB0YWJsZQorCSAqIDgg cnggYW5kIDggdHggZGVzY3JpcHRvcnMgcGVyIGNoYW5uZWwKKwkgKi8KKwlxbWMtPmJkX3NpemUg PSAobmJfY2hhbnMgKiAoUU1DX05CX1RYQkRTICsgUU1DX05CX1JYQkRTKSkgKiBzaXplb2YoY2Jk X3QpOworCXFtYy0+YmRfdGFibGUgPSBkbWFtX2FsbG9jX2NvaGVyZW50KHFtYy0+ZGV2LCBxbWMt PmJkX3NpemUsCisJCSZxbWMtPmJkX2RtYV9hZGRyLCBHRlBfS0VSTkVMKTsKKwlpZiAoIXFtYy0+ YmRfdGFibGUpIHsKKwkJZGV2X2VycihxbWMtPmRldiwgIkZhaWxlZCB0byBhbGxvY2F0ZSBiZCB0 YWJsZVxuIik7CisJCXJldCA9IC1FTk9NRU07CisJCWdvdG8gZXJyX3RzYV9kaXNjb25uZWN0Owor CX0KKwltZW1zZXQocW1jLT5iZF90YWJsZSwgMCwgcW1jLT5iZF9zaXplKTsKKworCXFtY193cml0 ZTMyKHFtYy0+c2NjX3ByYW0gKyBRTUNfR0JMX01DQkFTRSwgcW1jLT5iZF9kbWFfYWRkcik7CisK KwkvKiBBbGxvY2F0ZSB0aGUgaW50ZXJydXB0IHRhYmxlICovCisJcW1jLT5pbnRfc2l6ZSA9IFFN Q19OQl9JTlRTICogc2l6ZW9mKHUxNik7CisJcW1jLT5pbnRfdGFibGUgPSBkbWFtX2FsbG9jX2Nv aGVyZW50KHFtYy0+ZGV2LCBxbWMtPmludF9zaXplLAorCQkmcW1jLT5pbnRfZG1hX2FkZHIsIEdG UF9LRVJORUwpOworCWlmICghcW1jLT5pbnRfdGFibGUpIHsKKwkJZGV2X2VycihxbWMtPmRldiwg IkZhaWxlZCB0byBhbGxvY2F0ZSBpbnRlcnJ1cHQgdGFibGVcbiIpOworCQlyZXQgPSAtRU5PTUVN OworCQlnb3RvIGVycl90c2FfZGlzY29ubmVjdDsKKwl9CisJbWVtc2V0KHFtYy0+aW50X3RhYmxl LCAwLCBxbWMtPmludF9zaXplKTsKKworCXFtYy0+aW50X2N1cnIgPSBxbWMtPmludF90YWJsZTsK KwlxbWNfd3JpdGUzMihxbWMtPnNjY19wcmFtICsgUU1DX0dCTF9JTlRCQVNFLCBxbWMtPmludF9k bWFfYWRkcik7CisJcW1jX3dyaXRlMzIocW1jLT5zY2NfcHJhbSArIFFNQ19HQkxfSU5UUFRSLCBx bWMtPmludF9kbWFfYWRkcik7CisKKwkvKiBTZXQgTVJCTFIgKHZhbGlkIGZvciBIRExDIG9ubHkp IG1heCBNUlUgKyBtYXggQ1JDICovCisJcW1jX3dyaXRlMTYocW1jLT5zY2NfcHJhbSArIFFNQ19H QkxfTVJCTFIsIEhETENfTUFYX01SVSArIDQpOworCisJcW1jX3dyaXRlMTYocW1jLT5zY2NfcHJh bSArIFFNQ19HQkxfR1JGVEhSLCAxKTsKKwlxbWNfd3JpdGUxNihxbWMtPnNjY19wcmFtICsgUU1D X0dCTF9HUkZDTlQsIDEpOworCisJcW1jX3dyaXRlMzIocW1jLT5zY2NfcHJhbSArIFFNQ19HQkxf Q19NQVNLMzIsIDB4REVCQjIwRTMpOworCXFtY193cml0ZTE2KHFtYy0+c2NjX3ByYW0gKyBRTUNf R0JMX0NfTUFTSzE2LCAweEYwQjgpOworCisJcmV0ID0gcW1jX3NldHVwX3RzYShxbWMpOworCWlm IChyZXQpCisJCWdvdG8gZXJyX3RzYV9kaXNjb25uZWN0OworCisJcW1jX3dyaXRlMTYocW1jLT5z Y2NfcHJhbSArIFFNQ19HQkxfUU1DU1RBVEUsIDB4ODAwMCk7CisKKwlyZXQgPSBxbWNfc2V0dXBf Y2hhbnMocW1jKTsKKwlpZiAocmV0KQorCQlnb3RvIGVycl90c2FfZGlzY29ubmVjdDsKKworCS8q IEluaXQgaW50ZXJydXB0cyB0YWJsZSAqLworCXJldCA9IHFtY19zZXR1cF9pbnRzKHFtYyk7CisJ aWYgKHJldCkKKwkJZ290byBlcnJfdHNhX2Rpc2Nvbm5lY3Q7CisKKwkvKiBEaXNhYmxlIGFuZCBj bGVhciBpbnRlcnJ1cHRzLCAgc2V0IHRoZSBpcnEgaGFuZGxlciAqLworCXFtY193cml0ZTE2KHFt Yy0+c2NjX3JlZ3MgKyBTQ0NfU0NDTSwgMHgwMDAwKTsKKwlxbWNfd3JpdGUxNihxbWMtPnNjY19y ZWdzICsgU0NDX1NDQ0UsIDB4MDAwRik7CisJaXJxID0gcGxhdGZvcm1fZ2V0X2lycShwZGV2LCAw KTsKKwlpZiAoaXJxIDwgMCkKKwkJZ290byBlcnJfdHNhX2Rpc2Nvbm5lY3Q7CisJcmV0ID0gZGV2 bV9yZXF1ZXN0X2lycShxbWMtPmRldiwgaXJxLCBxbWNfaXJxX2hhbmRsZXIsIDAsICJxbWMiLCBx bWMpOworCWlmIChyZXQgPCAwKQorCQlnb3RvIGVycl90c2FfZGlzY29ubmVjdDsKKworCS8qIEVu YWJsZSBpbnRlcnJ1cHRzICovCisJcW1jX3dyaXRlMTYocW1jLT5zY2NfcmVncyArIFNDQ19TQ0NN LAorCQlTQ0NfU0NDRV9JUU9WIHwgU0NDX1NDQ0VfR0lOVCB8IFNDQ19TQ0NFX0dVTiB8IFNDQ19T Q0NFX0dPVik7CisKKwlyZXQgPSBxbWNfZmluYWxpemVfY2hhbnMocW1jKTsKKwlpZiAocmV0IDwg MCkKKwkJZ290byBlcnJfZGlzYWJsZV9pbnRyOworCisJLyogRW5hYmxlIHRyYW5zbWl0ZXIgYW5k IHJlY2VpdmVyICovCisJcW1jX3NldGJpdHMzMihxbWMtPnNjY19yZWdzICsgU0NDX0dTTVJMLCBT Q0NfR1NNUkxfRU5SIHwgU0NDX0dTTVJMX0VOVCk7CisKKwlwbGF0Zm9ybV9zZXRfZHJ2ZGF0YShw ZGV2LCBxbWMpOworCisJcmV0dXJuIDA7CisKK2Vycl9kaXNhYmxlX2ludHI6CisJcW1jX3dyaXRl MTYocW1jLT5zY2NfcmVncyArIFNDQ19TQ0NNLCAwKTsKKworZXJyX3RzYV9kaXNjb25uZWN0Ogor CXRzYV9kaXNjb25uZWN0KHFtYy0+dHNhLCBxbWMtPnRzYV9jZWxsX2lkKTsKKwlyZXR1cm4gcmV0 OworfQorCitzdGF0aWMgaW50IHFtY19yZW1vdmUoc3RydWN0IHBsYXRmb3JtX2RldmljZSAqcGRl dikKK3sKKwlzdHJ1Y3QgcW1jICpxbWMgPSBwbGF0Zm9ybV9nZXRfZHJ2ZGF0YShwZGV2KTsKKwor CS8qIERpc2FibGUgdHJhbnNtaXRlciBhbmQgcmVjZWl2ZXIgKi8KKwlxbWNfc2V0Yml0czMyKHFt Yy0+c2NjX3JlZ3MgKyBTQ0NfR1NNUkwsIDApOworCisJLyogRGlzYWJsZSBpbnRlcnJ1cHRzICov CisJcW1jX3dyaXRlMTYocW1jLT5zY2NfcmVncyArIFNDQ19TQ0NNLCAwKTsKKworCS8qIERpc2Nv bm5lY3QgZnJvbSB0c2EgKi8KKwl0c2FfZGlzY29ubmVjdChxbWMtPnRzYSwgcW1jLT50c2FfY2Vs bF9pZCk7CisKKwlyZXR1cm4gMDsKK30KKworc3RhdGljIGNvbnN0IHN0cnVjdCBvZl9kZXZpY2Vf aWQgcW1jX2lkX3RhYmxlW10gPSB7CisJeyAuY29tcGF0aWJsZSA9ICJmc2wsY3BtMS1zY2MtcW1j IiB9LAorCXt9IC8qIHNlbnRpbmVsICovCit9OworTU9EVUxFX0RFVklDRV9UQUJMRShvZiwgcW1j X2lkX3RhYmxlKTsKKworc3RhdGljIHN0cnVjdCBwbGF0Zm9ybV9kcml2ZXIgcW1jX2RyaXZlciA9 IHsKKwkuZHJpdmVyID0geworCQkubmFtZSA9ICJmc2wtcW1jIiwKKwkJLm9mX21hdGNoX3RhYmxl ID0gb2ZfbWF0Y2hfcHRyKHFtY19pZF90YWJsZSksCisJfSwKKwkucHJvYmUgPSBxbWNfcHJvYmUs CisJLnJlbW92ZSA9IHFtY19yZW1vdmUsCit9OworbW9kdWxlX3BsYXRmb3JtX2RyaXZlcihxbWNf ZHJpdmVyKTsKKworc3RydWN0IHFtY19jaGFuICpxbWNfY2hhbl9nZXRfYnlwaGFuZGxlKHN0cnVj dCBkZXZpY2Vfbm9kZSAqbnAsIGNvbnN0IGNoYXIgKnBoYW5kbGVfbmFtZSkKK3sKKwlzdHJ1Y3Qg b2ZfcGhhbmRsZV9hcmdzIG91dF9hcmdzOworCXN0cnVjdCBwbGF0Zm9ybV9kZXZpY2UgKnBkZXY7 CisJc3RydWN0IHFtY19jaGFuICpxbWNfY2hhbjsKKwlzdHJ1Y3QgcW1jICpxbWM7CisJaW50IHJl dDsKKworCXJldCA9IG9mX3BhcnNlX3BoYW5kbGVfd2l0aF9maXhlZF9hcmdzKG5wLCBwaGFuZGxl X25hbWUsIDEsIDAsCisJCQkJCSAgICAgICAmb3V0X2FyZ3MpOworCWlmIChyZXQgPCAwKQorCQly ZXR1cm4gRVJSX1BUUihyZXQpOworCisJaWYgKCFvZl9tYXRjaF9ub2RlKHFtY19kcml2ZXIuZHJp dmVyLm9mX21hdGNoX3RhYmxlLCBvdXRfYXJncy5ucCkpIHsKKwkJb2Zfbm9kZV9wdXQob3V0X2Fy Z3MubnApOworCQlyZXR1cm4gRVJSX1BUUigtRUlOVkFMKTsKKwl9CisKKwlwZGV2ID0gb2ZfZmlu ZF9kZXZpY2VfYnlfbm9kZShvdXRfYXJncy5ucCk7CisJb2Zfbm9kZV9wdXQob3V0X2FyZ3MubnAp OworCWlmICghcGRldikKKwkJcmV0dXJuIEVSUl9QVFIoLUVOT0RFVik7CisKKwlxbWMgPSBwbGF0 Zm9ybV9nZXRfZHJ2ZGF0YShwZGV2KTsKKwlpZiAoIXFtYykgeworCQlwbGF0Zm9ybV9kZXZpY2Vf cHV0KHBkZXYpOworCQlyZXR1cm4gRVJSX1BUUigtRVBST0JFX0RFRkVSKTsKKwl9CisKKwlpZiAo b3V0X2FyZ3MuYXJnc19jb3VudCAhPSAxKSB7CisJCXBsYXRmb3JtX2RldmljZV9wdXQocGRldik7 CisJCXJldHVybiBFUlJfUFRSKC1FSU5WQUwpOworCX0KKworCWlmIChvdXRfYXJncy5hcmdzWzBd ID49IEFSUkFZX1NJWkUocW1jLT5jaGFucykpIHsKKwkJcGxhdGZvcm1fZGV2aWNlX3B1dChwZGV2 KTsKKwkJcmV0dXJuIEVSUl9QVFIoLUVJTlZBTCk7CisJfQorCisJcW1jX2NoYW4gPSBxbWMtPmNo YW5zW291dF9hcmdzLmFyZ3NbMF1dOworCWlmICghcW1jX2NoYW4pIHsKKwkJcGxhdGZvcm1fZGV2 aWNlX3B1dChwZGV2KTsKKwkJcmV0dXJuIEVSUl9QVFIoLUVOT0VOVCk7CisJfQorCisJcmV0dXJu IHFtY19jaGFuOworfQorRVhQT1JUX1NZTUJPTChxbWNfY2hhbl9nZXRfYnlwaGFuZGxlKTsKKwor dm9pZCBxbWNfY2hhbl9wdXQoc3RydWN0IHFtY19jaGFuICpjaGFuKQoreworCXB1dF9kZXZpY2Uo Y2hhbi0+cW1jLT5kZXYpOworfQorRVhQT1JUX1NZTUJPTChxbWNfY2hhbl9wdXQpOworCitzdGF0 aWMgdm9pZCBkZXZtX3FtY19jaGFuX3JlbGVhc2Uoc3RydWN0IGRldmljZSAqZGV2LCB2b2lkICpy ZXMpCit7CisJc3RydWN0IHFtY19jaGFuICoqcW1jX2NoYW4gPSByZXM7CisKKwlxbWNfY2hhbl9w dXQoKnFtY19jaGFuKTsKK30KKworc3RydWN0IHFtY19jaGFuICpkZXZtX3FtY19jaGFuX2dldF9i eXBoYW5kbGUoc3RydWN0IGRldmljZSAqZGV2LAorCQkJCQkgICAgIHN0cnVjdCBkZXZpY2Vfbm9k ZSAqbnAsCisJCQkJCSAgICAgY29uc3QgY2hhciAqcGhhbmRsZV9uYW1lKQoreworCXN0cnVjdCBx bWNfY2hhbiAqcW1jX2NoYW47CisJc3RydWN0IHFtY19jaGFuICoqZHI7CisKKwlkciA9IGRldnJl c19hbGxvYyhkZXZtX3FtY19jaGFuX3JlbGVhc2UsIHNpemVvZigqZHIpLCBHRlBfS0VSTkVMKTsK KwlpZiAoIWRyKQorCQlyZXR1cm4gRVJSX1BUUigtRU5PTUVNKTsKKworCXFtY19jaGFuID0gcW1j X2NoYW5fZ2V0X2J5cGhhbmRsZShucCwgcGhhbmRsZV9uYW1lKTsKKwlpZiAoIUlTX0VSUihxbWNf Y2hhbikpIHsKKwkJKmRyID0gcW1jX2NoYW47CisJCWRldnJlc19hZGQoZGV2LCBkcik7CisJfSBl bHNlIHsKKwkJZGV2cmVzX2ZyZWUoZHIpOworCX0KKworCXJldHVybiBxbWNfY2hhbjsKK30KK0VY UE9SVF9TWU1CT0woZGV2bV9xbWNfY2hhbl9nZXRfYnlwaGFuZGxlKTsKKworTU9EVUxFX0FVVEhP UigiSGVydmUgQ29kaW5hIDxoZXJ2ZS5jb2RpbmFAYm9vdGxpbi5jb20+Iik7CitNT0RVTEVfREVT Q1JJUFRJT04oIkNQTSBRTUMgZHJpdmVyIik7CitNT0RVTEVfTElDRU5TRSgiR1BMIik7CmRpZmYg LS1naXQgYS9pbmNsdWRlL3NvYy9mc2wvcWUvcW1jLmggYi9pbmNsdWRlL3NvYy9mc2wvcWUvcW1j LmgKbmV3IGZpbGUgbW9kZSAxMDA2NDQKaW5kZXggMDAwMDAwMDAwMDAwLi4zYzYxYTUwZDJhZTIK LS0tIC9kZXYvbnVsbAorKysgYi9pbmNsdWRlL3NvYy9mc2wvcWUvcW1jLmgKQEAgLTAsMCArMSw3 MSBAQAorLyogU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEdQTC0yLjAtb3ItbGF0ZXIgKi8KKy8q CisgKiBRTUMgbWFuYWdlbWVudAorICoKKyAqIENvcHlyaWdodCAyMDIyIENTIEdST1VQIEZyYW5j ZQorICoKKyAqIEF1dGhvcjogSGVydmUgQ29kaW5hIDxoZXJ2ZS5jb2RpbmFAYm9vdGxpbi5jb20+ CisgKi8KKyNpZm5kZWYgX19TT0NfRlNMX1FNQ19IX18KKyNkZWZpbmUgX19TT0NfRlNMX1FNQ19I X18KKworI2luY2x1ZGUgPGxpbnV4L3R5cGVzLmg+CisKK3N0cnVjdCBkZXZpY2Vfbm9kZTsKK3N0 cnVjdCBkZXZpY2U7CitzdHJ1Y3QgcW1jX2NoYW47CisKK3N0cnVjdCBxbWNfY2hhbiAqcW1jX2No YW5fZ2V0X2J5cGhhbmRsZShzdHJ1Y3QgZGV2aWNlX25vZGUgKm5wLCBjb25zdCBjaGFyICpwaGFu ZGxlX25hbWUpOwordm9pZCBxbWNfY2hhbl9wdXQoc3RydWN0IHFtY19jaGFuICpjaGFuKTsKK3N0 cnVjdCBxbWNfY2hhbiAqZGV2bV9xbWNfY2hhbl9nZXRfYnlwaGFuZGxlKHN0cnVjdCBkZXZpY2Ug KmRldiwgc3RydWN0IGRldmljZV9ub2RlICpucCwKKwkJCQkJICAgICBjb25zdCBjaGFyICpwaGFu ZGxlX25hbWUpOworCitlbnVtIHFtY19tb2RlIHsKKwlRTUNfVFJBTlNQQVJFTlQsCisJUU1DX0hE TEMsCit9OworCitzdHJ1Y3QgcW1jX2NoYW5faW5mbyB7CisJZW51bSBxbWNfbW9kZSBtb2RlOwor CXVuc2lnbmVkIGxvbmcgcnhfZnNfcmF0ZTsKKwl1bnNpZ25lZCBsb25nIHJ4X2JpdF9yYXRlOwor CXU4IG5iX3J4X3RzOworCXVuc2lnbmVkIGxvbmcgdHhfZnNfcmF0ZTsKKwl1bnNpZ25lZCBsb25n IHR4X2JpdF9yYXRlOworCXU4IG5iX3R4X3RzOworfTsKKworaW50IHFtY19jaGFuX2dldF9pbmZv KHN0cnVjdCBxbWNfY2hhbiAqY2hhbiwgc3RydWN0IHFtY19jaGFuX2luZm8gKmluZm8pOworCitz dHJ1Y3QgcW1jX2NoYW5fcGFyYW0geworCWVudW0gcW1jX21vZGUgbW9kZTsKKwl1bmlvbiB7CisJ CXN0cnVjdCB7CisJCQl1MTYgbWF4X3J4X2J1Zl9zaXplOworCQkJdTE2IG1heF9yeF9mcmFtZV9z aXplOworCQkJYm9vbCBpc19jcmMzMjsKKwkJfSBoZGxjOworCQlzdHJ1Y3QgeworCQkJdTE2IG1h eF9yeF9idWZfc2l6ZTsKKwkJfSB0cmFuc3A7CisJfTsKK307CisKK2ludCBxbWNfY2hhbl9zZXRf cGFyYW0oc3RydWN0IHFtY19jaGFuICpjaGFuLCBjb25zdCBzdHJ1Y3QgcW1jX2NoYW5fcGFyYW0g KnBhcmFtKTsKKworaW50IHFtY19jaGFuX3dyaXRlX3N1Ym1pdChzdHJ1Y3QgcW1jX2NoYW4gKmNo YW4sIGRtYV9hZGRyX3QgYWRkciwgc2l6ZV90IGxlbmd0aCwKKwkJCSAgdm9pZCAoKmNvbXBsZXRl KSh2b2lkICpjb250ZXh0KSwgdm9pZCAqY29udGV4dCk7CisKK2ludCBxbWNfY2hhbl9yZWFkX3N1 Ym1pdChzdHJ1Y3QgcW1jX2NoYW4gKmNoYW4sIGRtYV9hZGRyX3QgYWRkciwgc2l6ZV90IGxlbmd0 aCwKKwkJCSB2b2lkICgqY29tcGxldGUpKHZvaWQgKmNvbnRleHQsIHNpemVfdCBsZW5ndGgpLAor CQkJIHZvaWQgKmNvbnRleHQpOworCisjZGVmaW5lIFFNQ19DSEFOX1JFQUQgICgxPDwwKQorI2Rl ZmluZSBRTUNfQ0hBTl9XUklURSAoMTw8MSkKKyNkZWZpbmUgUU1DX0NIQU5fQUxMICAgKFFNQ19D SEFOX1JFQUQgfCBRTUNfQ0hBTl9XUklURSkKKworaW50IHFtY19jaGFuX3N0YXJ0KHN0cnVjdCBx bWNfY2hhbiAqY2hhbiwgaW50IGRpcmVjdGlvbik7CitpbnQgcW1jX2NoYW5fc3RvcChzdHJ1Y3Qg cW1jX2NoYW4gKmNoYW4sIGludCBkaXJlY3Rpb24pOworaW50IHFtY19jaGFuX3Jlc2V0KHN0cnVj dCBxbWNfY2hhbiAqY2hhbiwgaW50IGRpcmVjdGlvbik7CisKKyNlbmRpZiAvKiBfX1NPQ19GU0xf UU1DX0hfXyAqLwotLSAKMi4zOC4xCgoKX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19f X19fX19fX19fX19fX18KbGludXgtYXJtLWtlcm5lbCBtYWlsaW5nIGxpc3QKbGludXgtYXJtLWtl cm5lbEBsaXN0cy5pbmZyYWRlYWQub3JnCmh0dHA6Ly9saXN0cy5pbmZyYWRlYWQub3JnL21haWxt YW4vbGlzdGluZm8vbGludXgtYXJtLWtlcm5lbAo= From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B0F8AC678D6 for ; Fri, 13 Jan 2023 10:40:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240742AbjAMKkP (ORCPT ); Fri, 13 Jan 2023 05:40:15 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39022 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S241237AbjAMKjF (ORCPT ); Fri, 13 Jan 2023 05:39:05 -0500 Received: from relay9-d.mail.gandi.net (relay9-d.mail.gandi.net [217.70.183.199]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id EEF4159F87; Fri, 13 Jan 2023 02:38:29 -0800 (PST) Received: (Authenticated sender: herve.codina@bootlin.com) by mail.gandi.net (Postfix) with ESMTPA id D53B5FF818; Fri, 13 Jan 2023 10:38:24 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1673606308; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=H7KIwJYPQrAhWdDUPPixMuaTGE+4u6gAL2K6MQ/lFPI=; b=ZgoI1XZMLKS5Iby0R/H2lYWFdbeiC+jf5zM4HEifY2DHdwpmV655+CdeqzgrBQzoO9EUUJ SGKbLVckraiFG7Qg6nR3e2Yt4lmgFgKb0cV6vdFhF2Wuq2N18JlkkvafMImq5mme03cSve zMRscQ4L4qcQ15P9L7v+wawo2FpGfgTkfpwFFRvGuudFuDsNMpEuvxhXinWJHsCGmfWV9U wJ3V9Nvmvh+4GqHly1lbO1LbZsqgAHHQPamEzOPMCNXVT1kyTVo2CFRel2qkBvwtqivQsY HqpWo1i4090sTzowRaD3/vkzzcT+DSe34Ok1so89wL0E8W6SDHTwJgjdJP1vIA== From: Herve Codina To: Herve Codina , Li Yang , Rob Herring , Krzysztof Kozlowski , Liam Girdwood , Mark Brown , Christophe Leroy , Michael Ellerman , Nicholas Piggin , Qiang Zhao , Jaroslav Kysela , Takashi Iwai , Shengjiu Wang , Xiubo Li , Fabio Estevam , Nicolin Chen Cc: linuxppc-dev@lists.ozlabs.org, linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, alsa-devel@alsa-project.org, Thomas Petazzoni Subject: [PATCH v3 06/10] soc: fsl: cmp1: Add support for QMC Date: Fri, 13 Jan 2023 11:37:55 +0100 Message-Id: <20230113103759.327698-7-herve.codina@bootlin.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20230113103759.327698-1-herve.codina@bootlin.com> References: <20230113103759.327698-1-herve.codina@bootlin.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org The QMC (QUICC Multichannel Controller) emulates up to 64 channels within one serial controller using the same TDM physical interface routed from the TSA. It is available in some PowerQUICC SoC such as the MPC885 or MPC866. It is also available on some Quicc Engine SoCs. This current version support CPM1 SoCs only and some enhancement are needed to support Quicc Engine SoCs. Signed-off-by: Herve Codina --- drivers/soc/fsl/qe/Kconfig | 12 + drivers/soc/fsl/qe/Makefile | 1 + drivers/soc/fsl/qe/qmc.c | 1531 +++++++++++++++++++++++++++++++++++ include/soc/fsl/qe/qmc.h | 71 ++ 4 files changed, 1615 insertions(+) create mode 100644 drivers/soc/fsl/qe/qmc.c create mode 100644 include/soc/fsl/qe/qmc.h diff --git a/drivers/soc/fsl/qe/Kconfig b/drivers/soc/fsl/qe/Kconfig index 60ec11c9f4d9..25b218351ae3 100644 --- a/drivers/soc/fsl/qe/Kconfig +++ b/drivers/soc/fsl/qe/Kconfig @@ -44,6 +44,18 @@ config CPM_TSA This option enables support for this controller +config CPM_QMC + tristate "CPM QMC support" + depends on OF && HAS_IOMEM + depends on CPM1 || (PPC && COMPILE_TEST) + depends on CPM_TSA + help + Freescale CPM QUICC Multichannel Controller + (QMC) + + This option enables support for this + controller + config QE_TDM bool default y if FSL_UCC_HDLC diff --git a/drivers/soc/fsl/qe/Makefile b/drivers/soc/fsl/qe/Makefile index 45c961acc81b..ec8506e13113 100644 --- a/drivers/soc/fsl/qe/Makefile +++ b/drivers/soc/fsl/qe/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_QUICC_ENGINE)+= qe.o qe_common.o qe_ic.o qe_io.o obj-$(CONFIG_CPM) += qe_common.o obj-$(CONFIG_CPM_TSA) += tsa.o +obj-$(CONFIG_CPM_QMC) += qmc.o obj-$(CONFIG_UCC) += ucc.o obj-$(CONFIG_UCC_SLOW) += ucc_slow.o obj-$(CONFIG_UCC_FAST) += ucc_fast.o diff --git a/drivers/soc/fsl/qe/qmc.c b/drivers/soc/fsl/qe/qmc.c new file mode 100644 index 000000000000..85a99538bde7 --- /dev/null +++ b/drivers/soc/fsl/qe/qmc.c @@ -0,0 +1,1531 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * QMC driver + * + * Copyright 2022 CS GROUP France + * + * Author: Herve Codina + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tsa.h" + +/* SCC general mode register high (32 bits) */ +#define SCC_GSMRL 0x00 +#define SCC_GSMRL_ENR (1 << 5) +#define SCC_GSMRL_ENT (1 << 4) +#define SCC_GSMRL_MODE_QMC (0x0A << 0) + +/* SCC general mode register low (32 bits) */ +#define SCC_GSMRH 0x04 +#define SCC_GSMRH_CTSS (1 << 7) +#define SCC_GSMRH_CDS (1 << 8) +#define SCC_GSMRH_CTSP (1 << 9) +#define SCC_GSMRH_CDP (1 << 10) + +/* SCC event register (16 bits) */ +#define SCC_SCCE 0x10 +#define SCC_SCCE_IQOV (1 << 3) +#define SCC_SCCE_GINT (1 << 2) +#define SCC_SCCE_GUN (1 << 1) +#define SCC_SCCE_GOV (1 << 0) + +/* SCC mask register (16 bits) */ +#define SCC_SCCM 0x14 +/* Multichannel base pointer (32 bits) */ +#define QMC_GBL_MCBASE 0x00 +/* Multichannel controller state (16 bits) */ +#define QMC_GBL_QMCSTATE 0x04 +/* Maximum receive buffer length (16 bits) */ +#define QMC_GBL_MRBLR 0x06 +/* Tx time-slot assignment table pointer (16 bits) */ +#define QMC_GBL_TX_S_PTR 0x08 +/* Rx pointer (16 bits) */ +#define QMC_GBL_RXPTR 0x0A +/* Global receive frame threshold (16 bits) */ +#define QMC_GBL_GRFTHR 0x0C +/* Global receive frame count (16 bits) */ +#define QMC_GBL_GRFCNT 0x0E +/* Multichannel interrupt base address (32 bits) */ +#define QMC_GBL_INTBASE 0x10 +/* Multichannel interrupt pointer (32 bits) */ +#define QMC_GBL_INTPTR 0x14 +/* Rx time-slot assignment table pointer (16 bits) */ +#define QMC_GBL_RX_S_PTR 0x18 +/* Tx pointer (16 bits) */ +#define QMC_GBL_TXPTR 0x1A +/* CRC constant (32 bits) */ +#define QMC_GBL_C_MASK32 0x1C +/* Time slot assignment table Rx (32 x 16 bits) */ +#define QMC_GBL_TSATRX 0x20 +/* Time slot assignment table Tx (32 x 16 bits) */ +#define QMC_GBL_TSATTX 0x60 +/* CRC constant (16 bits) */ +#define QMC_GBL_C_MASK16 0xA0 + +/* TSA entry (16bit entry in TSATRX and TSATTX) */ +#define QMC_TSA_VALID (1 << 15) +#define QMC_TSA_WRAP (1 << 14) +#define QMC_TSA_MASK (0x303F) +#define QMC_TSA_CHANNEL(x) ((x) << 6) + +/* Tx buffer descriptor base address (16 bits, offset from MCBASE) */ +#define QMC_SPE_TBASE 0x00 + +/* Channel mode register (16 bits) */ +#define QMC_SPE_CHAMR 0x02 +#define QMC_SPE_CHAMR_MODE_HDLC (1 << 15) +#define QMC_SPE_CHAMR_MODE_TRANSP ((0 << 15) | (1 << 13)) +#define QMC_SPE_CHAMR_ENT (1 << 12) +#define QMC_SPE_CHAMR_POL (1 << 8) +#define QMC_SPE_CHAMR_HDLC_IDLM (1 << 13) +#define QMC_SPE_CHAMR_HDLC_CRC (1 << 7) +#define QMC_SPE_CHAMR_HDLC_NOF (0x0f << 0) +#define QMC_SPE_CHAMR_TRANSP_RD (1 << 14) +#define QMC_SPE_CHAMR_TRANSP_SYNC (1 << 10) + +/* Tx internal state (32 bits) */ +#define QMC_SPE_TSTATE 0x04 +/* Tx buffer descriptor pointer (16 bits) */ +#define QMC_SPE_TBPTR 0x0C +/* Zero-insertion state (32 bits) */ +#define QMC_SPE_ZISTATE 0x14 +/* Channel’s interrupt mask flags (16 bits) */ +#define QMC_SPE_INTMSK 0x1C +/* Rx buffer descriptor base address (16 bits, offset from MCBASE) */ +#define QMC_SPE_RBASE 0x20 +/* HDLC: Maximum frame length register (16 bits) */ +#define QMC_SPE_MFLR 0x22 +/* TRANSPARENT: Transparent maximum receive length (16 bits) */ +#define QMC_SPE_TMRBLR 0x22 +/* Rx internal state (32 bits) */ +#define QMC_SPE_RSTATE 0x24 +/* Rx buffer descriptor pointer (16 bits) */ +#define QMC_SPE_RBPTR 0x2C +/* Packs 4 bytes to 1 long word before writing to buffer (32 bits) */ +#define QMC_SPE_RPACK 0x30 +/* Zero deletion state (32 bits) */ +#define QMC_SPE_ZDSTATE 0x34 + +/* Transparent synchronization (16 bits) */ +#define QMC_SPE_TRNSYNC 0x3C +#define QMC_SPE_TRNSYNC_RX(x) ((x) << 8) +#define QMC_SPE_TRNSYNC_TX(x) ((x) << 0) + +/* Interrupt related registers bits */ +#define QMC_INT_V (1 << 15) +#define QMC_INT_W (1 << 14) +#define QMC_INT_NID (1 << 13) +#define QMC_INT_IDL (1 << 12) +#define QMC_INT_GET_CHANNEL(x) (((x) & 0x0FC0) >> 6) +#define QMC_INT_MRF (1 << 5) +#define QMC_INT_UN (1 << 4) +#define QMC_INT_RXF (1 << 3) +#define QMC_INT_BSY (1 << 2) +#define QMC_INT_TXB (1 << 1) +#define QMC_INT_RXB (1 << 0) + +/* BD related registers bits */ +#define QMC_BD_RX_E (1 << 15) +#define QMC_BD_RX_W (1 << 13) +#define QMC_BD_RX_I (1 << 12) +#define QMC_BD_RX_L (1 << 11) +#define QMC_BD_RX_F (1 << 10) +#define QMC_BD_RX_CM (1 << 9) +#define QMC_BD_RX_UB (1 << 7) +#define QMC_BD_RX_LG (1 << 5) +#define QMC_BD_RX_NO (1 << 4) +#define QMC_BD_RX_AB (1 << 3) +#define QMC_BD_RX_CR (1 << 2) + +#define QMC_BD_TX_R (1 << 15) +#define QMC_BD_TX_W (1 << 13) +#define QMC_BD_TX_I (1 << 12) +#define QMC_BD_TX_L (1 << 11) +#define QMC_BD_TX_TC (1 << 10) +#define QMC_BD_TX_CM (1 << 9) +#define QMC_BD_TX_UB (1 << 7) +#define QMC_BD_TX_PAD (0x0f << 0) + +/* Numbers of BDs and interrupt items */ +#define QMC_NB_TXBDS 8 +#define QMC_NB_RXBDS 8 +#define QMC_NB_INTS 128 + +struct qmc_xfer_desc { + union { + void (*tx_complete)(void *context); + void (*rx_complete)(void *context, size_t length); + }; + void *context; +}; + +struct qmc_chan { + struct list_head list; + unsigned int id; + struct qmc *qmc; + void *__iomem s_param; + enum qmc_mode mode; + u64 tx_ts_mask; + u64 rx_ts_mask; + bool is_reverse_data; + + spinlock_t tx_lock; + cbd_t __iomem *txbds; + cbd_t __iomem *txbd_free; + cbd_t __iomem *txbd_done; + struct qmc_xfer_desc tx_desc[QMC_NB_TXBDS]; + u64 nb_tx_underrun; + bool is_tx_stopped; + + spinlock_t rx_lock; + cbd_t __iomem *rxbds; + cbd_t __iomem *rxbd_free; + cbd_t __iomem *rxbd_done; + struct qmc_xfer_desc rx_desc[QMC_NB_RXBDS]; + u64 nb_rx_busy; + int rx_pending; + bool is_rx_halted; + bool is_rx_stopped; +}; + +struct qmc { + struct device *dev; + struct tsa *tsa; + void *__iomem scc_regs; + void *__iomem scc_pram; + void *__iomem dpram; + u16 scc_pram_offset; + unsigned int tsa_cell_id; + cbd_t __iomem *bd_table; + dma_addr_t bd_dma_addr; + size_t bd_size; + u16 __iomem *int_table; + u16 __iomem *int_curr; + dma_addr_t int_dma_addr; + size_t int_size; + struct list_head chan_head; + struct qmc_chan *chans[64]; +}; + +static inline void qmc_write16(void *__iomem addr, u16 val) +{ + iowrite16be(val, addr); +} + +static inline u16 qmc_read16(void *__iomem addr) +{ + return ioread16be(addr); +} + +static inline void qmc_setbits16(void *__iomem addr, u16 set) +{ + qmc_write16(addr, qmc_read16(addr) | set); +} + +static inline void qmc_clrbits16(void *__iomem addr, u16 clr) +{ + qmc_write16(addr, qmc_read16(addr) & ~clr); +} + +static inline void qmc_write32(void *__iomem addr, u32 val) +{ + iowrite32be(val, addr); +} + +static inline u32 qmc_read32(void *__iomem addr) +{ + return ioread32be(addr); +} + +static inline void qmc_setbits32(void *__iomem addr, u32 set) +{ + qmc_write32(addr, qmc_read32(addr) | set); +} + + +int qmc_chan_get_info(struct qmc_chan *chan, struct qmc_chan_info *info) +{ + struct tsa_cell_info tsa_info; + int ret; + + /* Retrieve info from TSA related cell */ + ret = tsa_get_info(chan->qmc->tsa, chan->qmc->tsa_cell_id, &tsa_info); + if (ret) + return ret; + + info->mode = chan->mode; + info->rx_fs_rate = tsa_info.rx_fs_rate; + info->rx_bit_rate = tsa_info.rx_bit_rate; + info->nb_tx_ts = hweight64(chan->tx_ts_mask); + info->tx_fs_rate = tsa_info.tx_fs_rate; + info->tx_bit_rate = tsa_info.tx_bit_rate; + info->nb_rx_ts = hweight64(chan->rx_ts_mask); + + return 0; +} +EXPORT_SYMBOL(qmc_chan_get_info); + +int qmc_chan_set_param(struct qmc_chan *chan, const struct qmc_chan_param *param) +{ + if (param->mode != chan->mode) + return -EINVAL; + + switch (param->mode) { + case QMC_HDLC: + if ((param->hdlc.max_rx_buf_size % 4) || + (param->hdlc.max_rx_buf_size < 8)) + return -EINVAL; + + qmc_write16(chan->qmc->scc_pram + QMC_GBL_MRBLR, + param->hdlc.max_rx_buf_size - 8); + qmc_write16(chan->s_param + QMC_SPE_MFLR, + param->hdlc.max_rx_frame_size); + if (param->hdlc.is_crc32) { + qmc_setbits16(chan->s_param + QMC_SPE_CHAMR, + QMC_SPE_CHAMR_HDLC_CRC); + } else { + qmc_clrbits16(chan->s_param + QMC_SPE_CHAMR, + QMC_SPE_CHAMR_HDLC_CRC); + } + break; + + case QMC_TRANSPARENT: + qmc_write16(chan->s_param + QMC_SPE_TMRBLR, + param->transp.max_rx_buf_size); + break; + + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(qmc_chan_set_param); + +int qmc_chan_write_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length, + void (*complete)(void *context), void *context) +{ + struct qmc_xfer_desc *xfer_desc; + unsigned long flags; + cbd_t *__iomem bd; + u16 ctrl; + int ret; + + /* R bit UB bit + * 0 0 : The BD is free + * 1 1 : The BD is in used, waiting for transfer + * 0 1 : The BD is in used, waiting for completion + * 1 0 : Should not append + */ + + spin_lock_irqsave(&chan->tx_lock, flags); + bd = chan->txbd_free; + + ctrl = qmc_read16(&bd->cbd_sc); + if (ctrl & (QMC_BD_TX_R | QMC_BD_TX_UB)) { + /* We are full ... */ + ret = -EBUSY; + goto end; + } + + qmc_write16(&bd->cbd_datlen, length); + qmc_write32(&bd->cbd_bufaddr, addr); + + xfer_desc = &chan->tx_desc[bd - chan->txbds]; + xfer_desc->tx_complete = complete; + xfer_desc->context = context; + + /* Activate the descriptor */ + ctrl |= (QMC_BD_TX_R | QMC_BD_TX_UB); + wmb(); /* Be sure to flush the descriptor before control update */ + qmc_write16(&bd->cbd_sc, ctrl); + + if (!chan->is_tx_stopped) + qmc_setbits16(chan->s_param + QMC_SPE_CHAMR, QMC_SPE_CHAMR_POL); + + if (ctrl & QMC_BD_TX_W) + chan->txbd_free = chan->txbds; + else + chan->txbd_free++; + + ret = 0; + +end: + spin_unlock_irqrestore(&chan->tx_lock, flags); + return ret; +} +EXPORT_SYMBOL(qmc_chan_write_submit); + +static void qmc_chan_write_done(struct qmc_chan *chan) +{ + struct qmc_xfer_desc *xfer_desc; + void (*complete)(void *context); + unsigned long flags; + void *context; + cbd_t *__iomem bd; + u16 ctrl; + + /* R bit UB bit + * 0 0 : The BD is free + * 1 1 : The BD is in used, waiting for transfer + * 0 1 : The BD is in used, waiting for completion + * 1 0 : Should not append + */ + + spin_lock_irqsave(&chan->tx_lock, flags); + bd = chan->txbd_done; + + ctrl = qmc_read16(&bd->cbd_sc); + while (!(ctrl & QMC_BD_TX_R)) { + if (!(ctrl & QMC_BD_TX_UB)) + goto end; + + xfer_desc = &chan->tx_desc[bd - chan->txbds]; + complete = xfer_desc->tx_complete; + context = xfer_desc->context; + xfer_desc->tx_complete = NULL; + xfer_desc->context = NULL; + + qmc_write16(&bd->cbd_sc, ctrl & ~QMC_BD_TX_UB); + + if (ctrl & QMC_BD_TX_W) + chan->txbd_done = chan->txbds; + else + chan->txbd_done++; + + if (complete) { + spin_unlock_irqrestore(&chan->tx_lock, flags); + complete(context); + spin_lock_irqsave(&chan->tx_lock, flags); + } + + bd = chan->txbd_done; + ctrl = qmc_read16(&bd->cbd_sc); + } + +end: + spin_unlock_irqrestore(&chan->tx_lock, flags); +} + +int qmc_chan_read_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length, + void (*complete)(void *context, size_t length), void *context) +{ + struct qmc_xfer_desc *xfer_desc; + unsigned long flags; + cbd_t *__iomem bd; + u16 ctrl; + int ret; + + /* E bit UB bit + * 0 0 : The BD is free + * 1 1 : The BD is in used, waiting for transfer + * 0 1 : The BD is in used, waiting for completion + * 1 0 : Should not append + */ + + spin_lock_irqsave(&chan->rx_lock, flags); + bd = chan->rxbd_free; + + ctrl = qmc_read16(&bd->cbd_sc); + if (ctrl & (QMC_BD_RX_E | QMC_BD_RX_UB)) { + /* We are full ... */ + ret = -EBUSY; + goto end; + } + + qmc_write16(&bd->cbd_datlen, 0); /* data length is updated by the QMC */ + qmc_write32(&bd->cbd_bufaddr, addr); + + xfer_desc = &chan->rx_desc[bd - chan->rxbds]; + xfer_desc->rx_complete = complete; + xfer_desc->context = context; + + /* Activate the descriptor */ + ctrl |= (QMC_BD_RX_E | QMC_BD_RX_UB); + wmb(); /* Be sure to flush data before descriptor activation */ + qmc_write16(&bd->cbd_sc, ctrl); + + /* Restart receiver if needed */ + if (chan->is_rx_halted && !chan->is_rx_stopped) { + /* Restart receiver */ + if (chan->mode == QMC_TRANSPARENT) + qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x18000080); + else + qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x00000080); + qmc_write32(chan->s_param + QMC_SPE_RSTATE, 0x31000000); + chan->is_rx_halted = false; + } + chan->rx_pending++; + + if (ctrl & QMC_BD_RX_W) + chan->rxbd_free = chan->rxbds; + else + chan->rxbd_free++; + + ret = 0; +end: + spin_unlock_irqrestore(&chan->rx_lock, flags); + return ret; +} +EXPORT_SYMBOL(qmc_chan_read_submit); + +static void qmc_chan_read_done(struct qmc_chan *chan) +{ + void (*complete)(void *context, size_t size); + struct qmc_xfer_desc *xfer_desc; + unsigned long flags; + cbd_t *__iomem bd; + void *context; + u16 datalen; + u16 ctrl; + + /* E bit UB bit + * 0 0 : The BD is free + * 1 1 : The BD is in used, waiting for transfer + * 0 1 : The BD is in used, waiting for completion + * 1 0 : Should not append + */ + + spin_lock_irqsave(&chan->rx_lock, flags); + bd = chan->rxbd_done; + + ctrl = qmc_read16(&bd->cbd_sc); + while (!(ctrl & QMC_BD_RX_E)) { + if (!(ctrl & QMC_BD_RX_UB)) + goto end; + + xfer_desc = &chan->rx_desc[bd - chan->rxbds]; + complete = xfer_desc->rx_complete; + context = xfer_desc->context; + xfer_desc->rx_complete = NULL; + xfer_desc->context = NULL; + + datalen = qmc_read16(&bd->cbd_datlen); + qmc_write16(&bd->cbd_sc, ctrl & ~QMC_BD_RX_UB); + + if (ctrl & QMC_BD_RX_W) + chan->rxbd_done = chan->rxbds; + else + chan->rxbd_done++; + + chan->rx_pending--; + + if (complete) { + spin_unlock_irqrestore(&chan->rx_lock, flags); + complete(context, datalen); + spin_lock_irqsave(&chan->rx_lock, flags); + } + + bd = chan->rxbd_done; + ctrl = qmc_read16(&bd->cbd_sc); + } + +end: + spin_unlock_irqrestore(&chan->rx_lock, flags); +} + +static int qmc_chan_command(struct qmc_chan *chan, u8 qmc_opcode) +{ + return cpm_command(chan->id << 2, (qmc_opcode << 4) | 0x0E); +} + +static int qmc_chan_stop_rx(struct qmc_chan *chan) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&chan->rx_lock, flags); + + /* Send STOP RECEIVE command */ + ret = qmc_chan_command(chan, 0x0); + if (ret) { + dev_err(chan->qmc->dev, "chan %u: Send STOP RECEIVE failed (%d)\n", + chan->id, ret); + goto end; + } + + chan->is_rx_stopped = true; + +end: + spin_unlock_irqrestore(&chan->rx_lock, flags); + return ret; +} + +static int qmc_chan_stop_tx(struct qmc_chan *chan) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&chan->tx_lock, flags); + + /* Send STOP TRANSMIT command */ + ret = qmc_chan_command(chan, 0x1); + if (ret) { + dev_err(chan->qmc->dev, "chan %u: Send STOP TRANSMIT failed (%d)\n", + chan->id, ret); + goto end; + } + + chan->is_tx_stopped = true; + +end: + spin_unlock_irqrestore(&chan->tx_lock, flags); + return ret; +} + +int qmc_chan_stop(struct qmc_chan *chan, int direction) +{ + int ret; + + if (direction & QMC_CHAN_READ) { + ret = qmc_chan_stop_rx(chan); + if (ret) + return ret; + } + + if (direction & QMC_CHAN_WRITE) { + ret = qmc_chan_stop_tx(chan); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL(qmc_chan_stop); + +static void qmc_chan_start_rx(struct qmc_chan *chan) +{ + unsigned long flags; + + spin_lock_irqsave(&chan->rx_lock, flags); + + /* Restart the receiver */ + if (chan->mode == QMC_TRANSPARENT) + qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x18000080); + else + qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x00000080); + qmc_write32(chan->s_param + QMC_SPE_RSTATE, 0x31000000); + chan->is_rx_halted = false; + + chan->is_rx_stopped = false; + + spin_unlock_irqrestore(&chan->rx_lock, flags); +} + +static void qmc_chan_start_tx(struct qmc_chan *chan) +{ + unsigned long flags; + + spin_lock_irqsave(&chan->tx_lock, flags); + + /* Enable channel transmitter as it could be disabled if + * qmc_chan_reset() was called + */ + qmc_setbits16(chan->s_param + QMC_SPE_CHAMR, QMC_SPE_CHAMR_ENT); + + /* Set the POL bit in the channel mode register */ + qmc_setbits16(chan->s_param + QMC_SPE_CHAMR, QMC_SPE_CHAMR_POL); + + chan->is_tx_stopped = false; + + spin_unlock_irqrestore(&chan->tx_lock, flags); +} + +int qmc_chan_start(struct qmc_chan *chan, int direction) +{ + if (direction & QMC_CHAN_READ) + qmc_chan_start_rx(chan); + + if (direction & QMC_CHAN_WRITE) + qmc_chan_start_tx(chan); + + return 0; +} +EXPORT_SYMBOL(qmc_chan_start); + +static void qmc_chan_reset_rx(struct qmc_chan *chan) +{ + struct qmc_xfer_desc *xfer_desc; + unsigned long flags; + cbd_t *__iomem bd; + u16 ctrl; + + spin_lock_irqsave(&chan->rx_lock, flags); + bd = chan->rxbds; + do { + ctrl = qmc_read16(&bd->cbd_sc); + qmc_write16(&bd->cbd_sc, ctrl & ~(QMC_BD_RX_UB | QMC_BD_RX_E)); + + xfer_desc = &chan->rx_desc[bd - chan->rxbds]; + xfer_desc->rx_complete = NULL; + xfer_desc->context = NULL; + + bd++; + } while (!(ctrl & QMC_BD_RX_W)); + + chan->rxbd_free = chan->rxbds; + chan->rxbd_done = chan->rxbds; + qmc_write16(chan->s_param + QMC_SPE_RBPTR, + qmc_read16(chan->s_param + QMC_SPE_RBASE)); + + chan->rx_pending = 0; + chan->is_rx_stopped = false; + + spin_unlock_irqrestore(&chan->rx_lock, flags); +} + +static void qmc_chan_reset_tx(struct qmc_chan *chan) +{ + struct qmc_xfer_desc *xfer_desc; + unsigned long flags; + cbd_t *__iomem bd; + u16 ctrl; + + spin_lock_irqsave(&chan->tx_lock, flags); + + /* Disable transmitter. It will be re-enable on qmc_chan_start() */ + qmc_clrbits16(chan->s_param + QMC_SPE_CHAMR, QMC_SPE_CHAMR_ENT); + + bd = chan->txbds; + do { + ctrl = qmc_read16(&bd->cbd_sc); + qmc_write16(&bd->cbd_sc, ctrl & ~(QMC_BD_TX_UB | QMC_BD_TX_R)); + + xfer_desc = &chan->tx_desc[bd - chan->txbds]; + xfer_desc->tx_complete = NULL; + xfer_desc->context = NULL; + + bd++; + } while (!(ctrl & QMC_BD_TX_W)); + + chan->txbd_free = chan->txbds; + chan->txbd_done = chan->txbds; + qmc_write16(chan->s_param + QMC_SPE_TBPTR, + qmc_read16(chan->s_param + QMC_SPE_TBASE)); + + /* Reset TSTATE and ZISTATE to their initial value */ + qmc_write32(chan->s_param + QMC_SPE_TSTATE, 0x30000000); + qmc_write32(chan->s_param + QMC_SPE_ZISTATE, 0x00000100); + + spin_unlock_irqrestore(&chan->tx_lock, flags); +} + +int qmc_chan_reset(struct qmc_chan *chan, int direction) +{ + if (direction & QMC_CHAN_READ) + qmc_chan_reset_rx(chan); + + if (direction & QMC_CHAN_WRITE) + qmc_chan_reset_tx(chan); + + return 0; +} +EXPORT_SYMBOL(qmc_chan_reset); + +static int qmc_check_chans(struct qmc *qmc) +{ + struct tsa_cell_info info; + bool is_one_table = false; + struct qmc_chan *chan; + u64 tx_ts_mask = 0; + u64 rx_ts_mask = 0; + u64 tx_ts_assigned_mask; + u64 rx_ts_assigned_mask; + int ret; + + /* Retrieve info from TSA related cell */ + ret = tsa_get_info(qmc->tsa, qmc->tsa_cell_id, &info); + if (ret) + return ret; + + /* If more than 32 TS are assigned to this cell, one common table + * is used for Tx and Rx and so masks must be equal for all channels. + */ + if ((info.nb_tx_ts > 32) || (info.nb_rx_ts > 32)) { + if (info.nb_tx_ts != info.nb_rx_ts) { + dev_err(qmc->dev, "Number of TSA Tx/Rx TS assigned are not equal\n"); + return -EINVAL; + } + is_one_table = true; + } + + + tx_ts_assigned_mask = (((u64)1) << info.nb_tx_ts) - 1; + rx_ts_assigned_mask = (((u64)1) << info.nb_rx_ts) - 1; + + list_for_each_entry(chan, &qmc->chan_head, list) { + if (chan->tx_ts_mask > tx_ts_assigned_mask) { + dev_err(qmc->dev, "chan %u uses TSA unassigned Tx TS\n", chan->id); + return -EINVAL; + } + if (tx_ts_mask & chan->tx_ts_mask) { + dev_err(qmc->dev, "chan %u uses an already used Tx TS\n", chan->id); + return -EINVAL; + } + + if (chan->rx_ts_mask > rx_ts_assigned_mask) { + dev_err(qmc->dev, "chan %u uses TSA unassigned Rx TS\n", chan->id); + return -EINVAL; + } + if (rx_ts_mask & chan->rx_ts_mask) { + dev_err(qmc->dev, "chan %u uses an already used Rx TS\n", chan->id); + return -EINVAL; + } + + if (is_one_table && (chan->tx_ts_mask != chan->rx_ts_mask)) { + dev_err(qmc->dev, "chan %u uses different Rx and Tx TS\n", chan->id); + return -EINVAL; + } + + tx_ts_mask |= chan->tx_ts_mask; + rx_ts_mask |= chan->rx_ts_mask; + } + + return 0; +} + +static unsigned int qmc_nb_chans(struct qmc *qmc) +{ + unsigned int count = 0; + struct qmc_chan *chan; + + list_for_each_entry(chan, &qmc->chan_head, list) + count++; + + return count; +} + +static int qmc_of_parse_chans(struct qmc *qmc, struct device_node *np) +{ + struct device_node *chan_np; + struct qmc_chan *chan; + const char *mode; + u32 chan_id; + u64 ts_mask; + int ret; + + for_each_available_child_of_node(np, chan_np) { + ret = of_property_read_u32(chan_np, "reg", &chan_id); + if (ret) { + dev_err(qmc->dev, "%pOF: failed to read reg\n", chan_np); + of_node_put(chan_np); + return ret; + } + if (chan_id > 63) { + dev_err(qmc->dev, "%pOF: Invalid chan_id\n", chan_np); + of_node_put(chan_np); + return -EINVAL; + } + + chan = devm_kzalloc(qmc->dev, sizeof(*chan), GFP_KERNEL); + if (!chan) { + of_node_put(chan_np); + return -ENOMEM; + } + + chan->id = chan_id; + spin_lock_init(&chan->rx_lock); + spin_lock_init(&chan->tx_lock); + + ret = of_property_read_u64(chan_np, "fsl,tx-ts-mask", &ts_mask); + if (ret) { + dev_err(qmc->dev, "%pOF: failed to read fsl,tx-ts-mask\n", + chan_np); + of_node_put(chan_np); + return ret; + } + chan->tx_ts_mask = ts_mask; + + ret = of_property_read_u64(chan_np, "fsl,rx-ts-mask", &ts_mask); + if (ret) { + dev_err(qmc->dev, "%pOF: failed to read fsl,rx-ts-mask\n", + chan_np); + of_node_put(chan_np); + return ret; + } + chan->rx_ts_mask = ts_mask; + + mode = "transparent"; + ret = of_property_read_string(chan_np, "fsl,mode", &mode); + if (ret && ret != -EINVAL) { + dev_err(qmc->dev, "%pOF: failed to read fsl,mode\n", chan_np); + of_node_put(chan_np); + return ret; + } + if (!strcmp(mode, "transparent")) { + chan->mode = QMC_TRANSPARENT; + } else if (!strcmp(mode, "hdlc")) { + chan->mode = QMC_HDLC; + } else { + dev_err(qmc->dev, "%pOF: Invalid fsl,mode (%s)\n", chan_np, + mode); + of_node_put(chan_np); + return -EINVAL; + } + + chan->is_reverse_data = of_property_read_bool(chan_np, + "fsl,reverse-data"); + + list_add_tail(&chan->list, &qmc->chan_head); + qmc->chans[chan->id] = chan; + } + + return qmc_check_chans(qmc); +} + +static int qmc_setup_tsa_64rxtx(struct qmc *qmc, const struct tsa_cell_info *info) +{ + struct qmc_chan *chan; + unsigned int i; + u16 val; + + /* Use a common Tx/Rx 64 entries table + * Everything was previously checked, Tx and Rx related stuffs are + * identical -> Used Rx related stuff to build the table + */ + + /* Invalidate all entries */ + for (i = 0; i < 64; i++) + qmc_write16(qmc->scc_pram + QMC_GBL_TSATRX + (i * 2), 0x0000); + + /* Set entries based on Rx stuff*/ + list_for_each_entry(chan, &qmc->chan_head, list) { + for (i = 0; i < info->nb_rx_ts; i++) { + if (!(chan->rx_ts_mask & (((u64)1) << i))) + continue; + + val = QMC_TSA_VALID | QMC_TSA_MASK | + QMC_TSA_CHANNEL(chan->id); + qmc_write16(qmc->scc_pram + QMC_GBL_TSATRX + (i * 2), val); + } + } + + /* Set Wrap bit on last entry */ + qmc_setbits16(qmc->scc_pram + QMC_GBL_TSATRX + ((info->nb_rx_ts - 1) * 2), + QMC_TSA_WRAP); + + /* Init pointers to the table */ + val = qmc->scc_pram_offset + QMC_GBL_TSATRX; + qmc_write16(qmc->scc_pram + QMC_GBL_RX_S_PTR, val); + qmc_write16(qmc->scc_pram + QMC_GBL_RXPTR, val); + qmc_write16(qmc->scc_pram + QMC_GBL_TX_S_PTR, val); + qmc_write16(qmc->scc_pram + QMC_GBL_TXPTR, val); + + return 0; +} + +static int qmc_setup_tsa_32rx_32tx(struct qmc *qmc, const struct tsa_cell_info *info) +{ + struct qmc_chan *chan; + unsigned int i; + u16 val; + + /* Use a Tx 32 entries table and a Rx 32 entries table. + * Everything was previously checked. + */ + + /* Invalidate all entries */ + for (i = 0; i < 32; i++) { + qmc_write16(qmc->scc_pram + QMC_GBL_TSATRX + (i * 2), 0x0000); + qmc_write16(qmc->scc_pram + QMC_GBL_TSATTX + (i * 2), 0x0000); + } + + /* Set entries based on Rx and Tx stuff*/ + list_for_each_entry(chan, &qmc->chan_head, list) { + /* Rx part */ + for (i = 0; i < info->nb_rx_ts; i++) { + if (!(chan->rx_ts_mask & (((u64)1) << i))) + continue; + + val = QMC_TSA_VALID | QMC_TSA_MASK | + QMC_TSA_CHANNEL(chan->id); + qmc_write16(qmc->scc_pram + QMC_GBL_TSATRX + (i * 2), val); + } + /* Tx part */ + for (i = 0; i < info->nb_tx_ts; i++) { + if (!(chan->tx_ts_mask & (((u64)1) << i))) + continue; + + val = QMC_TSA_VALID | QMC_TSA_MASK | + QMC_TSA_CHANNEL(chan->id); + qmc_write16(qmc->scc_pram + QMC_GBL_TSATTX + (i * 2), val); + } + } + + /* Set Wrap bit on last entries */ + qmc_setbits16(qmc->scc_pram + QMC_GBL_TSATRX + ((info->nb_rx_ts - 1) * 2), + QMC_TSA_WRAP); + qmc_setbits16(qmc->scc_pram + QMC_GBL_TSATTX + ((info->nb_tx_ts - 1) * 2), + QMC_TSA_WRAP); + + /* Init Rx pointers ...*/ + val = qmc->scc_pram_offset + QMC_GBL_TSATRX; + qmc_write16(qmc->scc_pram + QMC_GBL_RX_S_PTR, val); + qmc_write16(qmc->scc_pram + QMC_GBL_RXPTR, val); + + /* ... and Tx pointers */ + val = qmc->scc_pram_offset + QMC_GBL_TSATTX; + qmc_write16(qmc->scc_pram + QMC_GBL_TX_S_PTR, val); + qmc_write16(qmc->scc_pram + QMC_GBL_TXPTR, val); + + return 0; +} + +static int qmc_setup_tsa(struct qmc *qmc) +{ + struct tsa_cell_info info; + int ret; + + /* Retrieve info from TSA related cell */ + ret = tsa_get_info(qmc->tsa, qmc->tsa_cell_id, &info); + if (ret) + return ret; + + /* Setup one common 64 entries table or two 32 entries (one for Tx and + * one for Tx) according to assigned TS numbers. + */ + return ((info.nb_tx_ts > 32) || (info.nb_rx_ts > 32)) ? + qmc_setup_tsa_64rxtx(qmc, &info) : + qmc_setup_tsa_32rx_32tx(qmc, &info); +} + +static int qmc_setup_chan_trnsync(struct qmc *qmc, struct qmc_chan *chan) +{ + struct tsa_cell_info info; + u16 first_rx, last_tx; + u16 trnsync; + int ret; + + /* Retrieve info from TSA related cell */ + ret = tsa_get_info(qmc->tsa, qmc->tsa_cell_id, &info); + if (ret) + return ret; + + /* Find the first Rx TS allocated to the channel */ + first_rx = chan->rx_ts_mask ? __ffs64(chan->rx_ts_mask) + 1 : 0; + + /* Find the last Tx TS allocated to the channel */ + last_tx = fls64(chan->tx_ts_mask); + + trnsync = 0; + if (info.nb_rx_ts) + trnsync |= QMC_SPE_TRNSYNC_RX((first_rx % info.nb_rx_ts) * 2); + if (info.nb_tx_ts) + trnsync |= QMC_SPE_TRNSYNC_TX((last_tx % info.nb_tx_ts) * 2); + + qmc_write16(chan->s_param + QMC_SPE_TRNSYNC, trnsync); + + dev_dbg(qmc->dev, "chan %u: trnsync=0x%04x, rx %u/%u 0x%llx, tx %u/%u 0x%llx\n", + chan->id, trnsync, + first_rx, info.nb_rx_ts, chan->rx_ts_mask, + last_tx, info.nb_tx_ts, chan->tx_ts_mask); + + return 0; +} + +static int qmc_setup_chan(struct qmc *qmc, struct qmc_chan *chan) +{ + unsigned int i; + cbd_t __iomem *bd; + int ret; + u16 val; + + chan->qmc = qmc; + + /* Set channel specific parameter base address */ + chan->s_param = qmc->dpram + (chan->id * 64); + /* 16 bd per channel (8 rx and 8 tx) */ + chan->txbds = qmc->bd_table + (chan->id * (QMC_NB_TXBDS + QMC_NB_RXBDS)); + chan->rxbds = qmc->bd_table + (chan->id * (QMC_NB_TXBDS + QMC_NB_RXBDS)) + QMC_NB_TXBDS; + + chan->txbd_free = chan->txbds; + chan->txbd_done = chan->txbds; + chan->rxbd_free = chan->rxbds; + chan->rxbd_done = chan->rxbds; + + /* TBASE and TBPTR*/ + val = chan->id * (QMC_NB_TXBDS + QMC_NB_RXBDS) * sizeof(cbd_t); + qmc_write16(chan->s_param + QMC_SPE_TBASE, val); + qmc_write16(chan->s_param + QMC_SPE_TBPTR, val); + + /* RBASE and RBPTR*/ + val = ((chan->id * (QMC_NB_TXBDS + QMC_NB_RXBDS)) + QMC_NB_TXBDS) * sizeof(cbd_t); + qmc_write16(chan->s_param + QMC_SPE_RBASE, val); + qmc_write16(chan->s_param + QMC_SPE_RBPTR, val); + qmc_write32(chan->s_param + QMC_SPE_TSTATE, 0x30000000); + qmc_write32(chan->s_param + QMC_SPE_RSTATE, 0x31000000); + qmc_write32(chan->s_param + QMC_SPE_ZISTATE, 0x00000100); + if (chan->mode == QMC_TRANSPARENT) { + qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x18000080); + qmc_write16(chan->s_param + QMC_SPE_TMRBLR, 60); + val = QMC_SPE_CHAMR_MODE_TRANSP | QMC_SPE_CHAMR_TRANSP_SYNC; + if (chan->is_reverse_data) + val |= QMC_SPE_CHAMR_TRANSP_RD; + qmc_write16(chan->s_param + QMC_SPE_CHAMR, val); + ret = qmc_setup_chan_trnsync(qmc, chan); + if (ret) + return ret; + } else { + qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x00000080); + qmc_write16(chan->s_param + QMC_SPE_MFLR, 60); + qmc_write16(chan->s_param + QMC_SPE_CHAMR, + QMC_SPE_CHAMR_MODE_HDLC | QMC_SPE_CHAMR_HDLC_IDLM); + } + + /* Do not enable interrupts now. They will be enabled later */ + qmc_write16(chan->s_param + QMC_SPE_INTMSK, 0x0000); + + /* Init Rx BDs and set Wrap bit on last descriptor */ + BUILD_BUG_ON(QMC_NB_RXBDS == 0); + val = QMC_BD_RX_I; + for (i = 0; i < QMC_NB_RXBDS; i++) { + bd = chan->rxbds + i; + qmc_write16(&bd->cbd_sc, val); + } + bd = chan->rxbds + QMC_NB_RXBDS - 1; + qmc_write16(&bd->cbd_sc, val | QMC_BD_RX_W); + + /* Init Tx BDs and set Wrap bit on last descriptor */ + BUILD_BUG_ON(QMC_NB_TXBDS == 0); + val = QMC_BD_TX_I; + if (chan->mode == QMC_HDLC) + val |= QMC_BD_TX_L | QMC_BD_TX_TC; + for (i = 0; i < QMC_NB_TXBDS; i++) { + bd = chan->txbds + i; + qmc_write16(&bd->cbd_sc, val); + } + bd = chan->txbds + QMC_NB_TXBDS - 1; + qmc_write16(&bd->cbd_sc, val | QMC_BD_TX_W); + + return 0; +} + +static int qmc_setup_chans(struct qmc *qmc) +{ + struct qmc_chan *chan; + int ret; + + list_for_each_entry(chan, &qmc->chan_head, list) { + ret = qmc_setup_chan(qmc, chan); + if (ret) + return ret; + } + + return 0; +} + +static int qmc_finalize_chans(struct qmc *qmc) +{ + struct qmc_chan *chan; + int ret; + + list_for_each_entry(chan, &qmc->chan_head, list) { + /* Unmask channel interrupts */ + if (chan->mode == QMC_HDLC) { + qmc_write16(chan->s_param + QMC_SPE_INTMSK, + QMC_INT_NID | QMC_INT_IDL | QMC_INT_MRF | + QMC_INT_UN | QMC_INT_RXF | QMC_INT_BSY | + QMC_INT_TXB | QMC_INT_RXB); + } else { + qmc_write16(chan->s_param + QMC_SPE_INTMSK, + QMC_INT_UN | QMC_INT_BSY | + QMC_INT_TXB | QMC_INT_RXB); + } + + /* Forced stop the channel */ + ret = qmc_chan_stop(chan, QMC_CHAN_ALL); + if (ret) + return ret; + } + + return 0; +} + +static int qmc_setup_ints(struct qmc *qmc) +{ + unsigned int i; + u16 __iomem *last; + + /* Raz all entries */ + for (i = 0; i < (qmc->int_size / sizeof(u16)); i++) + qmc_write16(qmc->int_table + i, 0x0000); + + /* Set Wrap bit on last entry */ + if (qmc->int_size >= sizeof(u16)) { + last = qmc->int_table + (qmc->int_size / sizeof(u16)) - 1; + qmc_write16(last, QMC_INT_W); + } + + return 0; +} + +static void qmc_irq_gint(struct qmc *qmc) +{ + struct qmc_chan *chan; + unsigned int chan_id; + unsigned long flags; + u16 int_entry; + + int_entry = qmc_read16(qmc->int_curr); + while (int_entry & QMC_INT_V) { + /* Clear all but the Wrap bit */ + qmc_write16(qmc->int_curr, int_entry & QMC_INT_W); + + chan_id = QMC_INT_GET_CHANNEL(int_entry); + chan = qmc->chans[chan_id]; + if (!chan) { + dev_err(qmc->dev, "interrupt on invalid chan %u\n", chan_id); + goto int_next; + } + + if (int_entry & QMC_INT_TXB) + qmc_chan_write_done(chan); + + if (int_entry & QMC_INT_UN) { + dev_info(qmc->dev, "intr chan %u, 0x%04x (UN)\n", chan_id, + int_entry); + chan->nb_tx_underrun++; + } + + if (int_entry & QMC_INT_BSY) { + dev_info(qmc->dev, "intr chan %u, 0x%04x (BSY)\n", chan_id, + int_entry); + chan->nb_rx_busy++; + /* Restart the receiver if needed */ + spin_lock_irqsave(&chan->rx_lock, flags); + if (chan->rx_pending && !chan->is_rx_stopped) { + if (chan->mode == QMC_TRANSPARENT) + qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x18000080); + else + qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x00000080); + qmc_write32(chan->s_param + QMC_SPE_RSTATE, 0x31000000); + chan->is_rx_halted = false; + } else { + chan->is_rx_halted = true; + } + spin_unlock_irqrestore(&chan->rx_lock, flags); + } + + if (int_entry & QMC_INT_RXB) + qmc_chan_read_done(chan); + +int_next: + if (int_entry & QMC_INT_W) + qmc->int_curr = qmc->int_table; + else + qmc->int_curr++; + int_entry = qmc_read16(qmc->int_curr); + } +} + +static irqreturn_t qmc_irq_handler(int irq, void *priv) +{ + struct qmc *qmc = (struct qmc *)priv; + u16 scce; + + scce = qmc_read16(qmc->scc_regs + SCC_SCCE); + qmc_write16(qmc->scc_regs + SCC_SCCE, scce); + + if (unlikely(scce & SCC_SCCE_IQOV)) + dev_info(qmc->dev, "IRQ queue overflow\n"); + + if (unlikely(scce & SCC_SCCE_GUN)) + dev_err(qmc->dev, "Global transmitter underrun\n"); + + if (unlikely(scce & SCC_SCCE_GOV)) + dev_err(qmc->dev, "Global receiver overrun\n"); + + /* normal interrupt */ + if (likely(scce & SCC_SCCE_GINT)) + qmc_irq_gint(qmc); + + return IRQ_HANDLED; +} + +static int qmc_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + unsigned int nb_chans; + struct resource *res; + struct qmc *qmc; + u32 val; + int irq; + int ret; + + qmc = devm_kzalloc(&pdev->dev, sizeof(*qmc), GFP_KERNEL); + if (!qmc) + return -ENOMEM; + + qmc->dev = &pdev->dev; + INIT_LIST_HEAD(&qmc->chan_head); + + qmc->scc_regs = devm_platform_ioremap_resource_byname(pdev, "scc_regs"); + if (IS_ERR(qmc->scc_regs)) + return PTR_ERR(qmc->scc_regs); + + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "scc_pram"); + if (!res) + return -EINVAL; + qmc->scc_pram_offset = res->start - get_immrbase(); + qmc->scc_pram = devm_ioremap_resource(qmc->dev, res); + if (IS_ERR(qmc->scc_pram)) + return PTR_ERR(qmc->scc_pram); + + qmc->dpram = devm_platform_ioremap_resource_byname(pdev, "dpram"); + if (IS_ERR(qmc->dpram)) + return PTR_ERR(qmc->dpram); + + ret = of_property_read_u32(np, "fsl,tsa-cell-id", &val); + if (ret) { + dev_err(qmc->dev, "%pOF: failed to read fsl,tsa-cell-id\n", np); + return ret; + } + qmc->tsa_cell_id = val; + + qmc->tsa = devm_tsa_get_byphandle(qmc->dev, np, "fsl,tsa"); + if (IS_ERR(qmc->tsa)) { + return dev_err_probe(qmc->dev, PTR_ERR(qmc->tsa), + "Failed to get TSA\n"); + } + + /* Connect the SCC to TSA */ + ret = tsa_connect(qmc->tsa, qmc->tsa_cell_id); + if (ret) { + dev_err(qmc->dev, "Failed to connect TSA\n"); + return ret; + } + + /* Parse channels informationss */ + ret = qmc_of_parse_chans(qmc, np); + if (ret) + goto err_tsa_disconnect; + + nb_chans = qmc_nb_chans(qmc); + + /* Init GMSR_H and GMSR_L registers */ + qmc_write32(qmc->scc_regs + SCC_GSMRH, + SCC_GSMRH_CDS | SCC_GSMRH_CTSS | SCC_GSMRH_CDP | SCC_GSMRH_CTSP); + + /* enable QMC mode */ + qmc_write32(qmc->scc_regs + SCC_GSMRL, SCC_GSMRL_MODE_QMC); + + /* Allocate the buffer descriptor table + * 8 rx and 8 tx descriptors per channel + */ + qmc->bd_size = (nb_chans * (QMC_NB_TXBDS + QMC_NB_RXBDS)) * sizeof(cbd_t); + qmc->bd_table = dmam_alloc_coherent(qmc->dev, qmc->bd_size, + &qmc->bd_dma_addr, GFP_KERNEL); + if (!qmc->bd_table) { + dev_err(qmc->dev, "Failed to allocate bd table\n"); + ret = -ENOMEM; + goto err_tsa_disconnect; + } + memset(qmc->bd_table, 0, qmc->bd_size); + + qmc_write32(qmc->scc_pram + QMC_GBL_MCBASE, qmc->bd_dma_addr); + + /* Allocate the interrupt table */ + qmc->int_size = QMC_NB_INTS * sizeof(u16); + qmc->int_table = dmam_alloc_coherent(qmc->dev, qmc->int_size, + &qmc->int_dma_addr, GFP_KERNEL); + if (!qmc->int_table) { + dev_err(qmc->dev, "Failed to allocate interrupt table\n"); + ret = -ENOMEM; + goto err_tsa_disconnect; + } + memset(qmc->int_table, 0, qmc->int_size); + + qmc->int_curr = qmc->int_table; + qmc_write32(qmc->scc_pram + QMC_GBL_INTBASE, qmc->int_dma_addr); + qmc_write32(qmc->scc_pram + QMC_GBL_INTPTR, qmc->int_dma_addr); + + /* Set MRBLR (valid for HDLC only) max MRU + max CRC */ + qmc_write16(qmc->scc_pram + QMC_GBL_MRBLR, HDLC_MAX_MRU + 4); + + qmc_write16(qmc->scc_pram + QMC_GBL_GRFTHR, 1); + qmc_write16(qmc->scc_pram + QMC_GBL_GRFCNT, 1); + + qmc_write32(qmc->scc_pram + QMC_GBL_C_MASK32, 0xDEBB20E3); + qmc_write16(qmc->scc_pram + QMC_GBL_C_MASK16, 0xF0B8); + + ret = qmc_setup_tsa(qmc); + if (ret) + goto err_tsa_disconnect; + + qmc_write16(qmc->scc_pram + QMC_GBL_QMCSTATE, 0x8000); + + ret = qmc_setup_chans(qmc); + if (ret) + goto err_tsa_disconnect; + + /* Init interrupts table */ + ret = qmc_setup_ints(qmc); + if (ret) + goto err_tsa_disconnect; + + /* Disable and clear interrupts, set the irq handler */ + qmc_write16(qmc->scc_regs + SCC_SCCM, 0x0000); + qmc_write16(qmc->scc_regs + SCC_SCCE, 0x000F); + irq = platform_get_irq(pdev, 0); + if (irq < 0) + goto err_tsa_disconnect; + ret = devm_request_irq(qmc->dev, irq, qmc_irq_handler, 0, "qmc", qmc); + if (ret < 0) + goto err_tsa_disconnect; + + /* Enable interrupts */ + qmc_write16(qmc->scc_regs + SCC_SCCM, + SCC_SCCE_IQOV | SCC_SCCE_GINT | SCC_SCCE_GUN | SCC_SCCE_GOV); + + ret = qmc_finalize_chans(qmc); + if (ret < 0) + goto err_disable_intr; + + /* Enable transmiter and receiver */ + qmc_setbits32(qmc->scc_regs + SCC_GSMRL, SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + platform_set_drvdata(pdev, qmc); + + return 0; + +err_disable_intr: + qmc_write16(qmc->scc_regs + SCC_SCCM, 0); + +err_tsa_disconnect: + tsa_disconnect(qmc->tsa, qmc->tsa_cell_id); + return ret; +} + +static int qmc_remove(struct platform_device *pdev) +{ + struct qmc *qmc = platform_get_drvdata(pdev); + + /* Disable transmiter and receiver */ + qmc_setbits32(qmc->scc_regs + SCC_GSMRL, 0); + + /* Disable interrupts */ + qmc_write16(qmc->scc_regs + SCC_SCCM, 0); + + /* Disconnect from tsa */ + tsa_disconnect(qmc->tsa, qmc->tsa_cell_id); + + return 0; +} + +static const struct of_device_id qmc_id_table[] = { + { .compatible = "fsl,cpm1-scc-qmc" }, + {} /* sentinel */ +}; +MODULE_DEVICE_TABLE(of, qmc_id_table); + +static struct platform_driver qmc_driver = { + .driver = { + .name = "fsl-qmc", + .of_match_table = of_match_ptr(qmc_id_table), + }, + .probe = qmc_probe, + .remove = qmc_remove, +}; +module_platform_driver(qmc_driver); + +struct qmc_chan *qmc_chan_get_byphandle(struct device_node *np, const char *phandle_name) +{ + struct of_phandle_args out_args; + struct platform_device *pdev; + struct qmc_chan *qmc_chan; + struct qmc *qmc; + int ret; + + ret = of_parse_phandle_with_fixed_args(np, phandle_name, 1, 0, + &out_args); + if (ret < 0) + return ERR_PTR(ret); + + if (!of_match_node(qmc_driver.driver.of_match_table, out_args.np)) { + of_node_put(out_args.np); + return ERR_PTR(-EINVAL); + } + + pdev = of_find_device_by_node(out_args.np); + of_node_put(out_args.np); + if (!pdev) + return ERR_PTR(-ENODEV); + + qmc = platform_get_drvdata(pdev); + if (!qmc) { + platform_device_put(pdev); + return ERR_PTR(-EPROBE_DEFER); + } + + if (out_args.args_count != 1) { + platform_device_put(pdev); + return ERR_PTR(-EINVAL); + } + + if (out_args.args[0] >= ARRAY_SIZE(qmc->chans)) { + platform_device_put(pdev); + return ERR_PTR(-EINVAL); + } + + qmc_chan = qmc->chans[out_args.args[0]]; + if (!qmc_chan) { + platform_device_put(pdev); + return ERR_PTR(-ENOENT); + } + + return qmc_chan; +} +EXPORT_SYMBOL(qmc_chan_get_byphandle); + +void qmc_chan_put(struct qmc_chan *chan) +{ + put_device(chan->qmc->dev); +} +EXPORT_SYMBOL(qmc_chan_put); + +static void devm_qmc_chan_release(struct device *dev, void *res) +{ + struct qmc_chan **qmc_chan = res; + + qmc_chan_put(*qmc_chan); +} + +struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev, + struct device_node *np, + const char *phandle_name) +{ + struct qmc_chan *qmc_chan; + struct qmc_chan **dr; + + dr = devres_alloc(devm_qmc_chan_release, sizeof(*dr), GFP_KERNEL); + if (!dr) + return ERR_PTR(-ENOMEM); + + qmc_chan = qmc_chan_get_byphandle(np, phandle_name); + if (!IS_ERR(qmc_chan)) { + *dr = qmc_chan; + devres_add(dev, dr); + } else { + devres_free(dr); + } + + return qmc_chan; +} +EXPORT_SYMBOL(devm_qmc_chan_get_byphandle); + +MODULE_AUTHOR("Herve Codina "); +MODULE_DESCRIPTION("CPM QMC driver"); +MODULE_LICENSE("GPL"); diff --git a/include/soc/fsl/qe/qmc.h b/include/soc/fsl/qe/qmc.h new file mode 100644 index 000000000000..3c61a50d2ae2 --- /dev/null +++ b/include/soc/fsl/qe/qmc.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QMC management + * + * Copyright 2022 CS GROUP France + * + * Author: Herve Codina + */ +#ifndef __SOC_FSL_QMC_H__ +#define __SOC_FSL_QMC_H__ + +#include + +struct device_node; +struct device; +struct qmc_chan; + +struct qmc_chan *qmc_chan_get_byphandle(struct device_node *np, const char *phandle_name); +void qmc_chan_put(struct qmc_chan *chan); +struct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev, struct device_node *np, + const char *phandle_name); + +enum qmc_mode { + QMC_TRANSPARENT, + QMC_HDLC, +}; + +struct qmc_chan_info { + enum qmc_mode mode; + unsigned long rx_fs_rate; + unsigned long rx_bit_rate; + u8 nb_rx_ts; + unsigned long tx_fs_rate; + unsigned long tx_bit_rate; + u8 nb_tx_ts; +}; + +int qmc_chan_get_info(struct qmc_chan *chan, struct qmc_chan_info *info); + +struct qmc_chan_param { + enum qmc_mode mode; + union { + struct { + u16 max_rx_buf_size; + u16 max_rx_frame_size; + bool is_crc32; + } hdlc; + struct { + u16 max_rx_buf_size; + } transp; + }; +}; + +int qmc_chan_set_param(struct qmc_chan *chan, const struct qmc_chan_param *param); + +int qmc_chan_write_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length, + void (*complete)(void *context), void *context); + +int qmc_chan_read_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length, + void (*complete)(void *context, size_t length), + void *context); + +#define QMC_CHAN_READ (1<<0) +#define QMC_CHAN_WRITE (1<<1) +#define QMC_CHAN_ALL (QMC_CHAN_READ | QMC_CHAN_WRITE) + +int qmc_chan_start(struct qmc_chan *chan, int direction); +int qmc_chan_stop(struct qmc_chan *chan, int direction); +int qmc_chan_reset(struct qmc_chan *chan, int direction); + +#endif /* __SOC_FSL_QMC_H__ */ -- 2.38.1