From patchwork Tue Nov 22 11:24:16 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?b?WWFuY2hhbyBZYW5nICjmnajlvabotoUp?= X-Patchwork-Id: 24309 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:adf:f944:0:0:0:0:0 with SMTP id q4csp2149063wrr; Tue, 22 Nov 2022 03:42:52 -0800 (PST) X-Google-Smtp-Source: AA0mqf51fyoZFXPc4h5VmyIjsmLJfuuWdLO/0dVvTzlmBosstBoP/4nmBg7DqmQMn7/9zwjUYeoy X-Received: by 2002:a17:90a:a003:b0:214:1a8a:a415 with SMTP id q3-20020a17090aa00300b002141a8aa415mr24956718pjp.197.1669117372320; Tue, 22 Nov 2022 03:42:52 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1669117372; cv=none; d=google.com; s=arc-20160816; b=LrsHyrXhP1zw/foCj89BVTKPj2ne4g8mMMvWw8Kc9NkSdHn02fRI7GzE5hOKBxeQSr HYU76AENi1IMOkRqekTX+9NEDr6UlRH6VdYxL2AoRccCGVO4NetDiWGpsgNKqo9Jw7Th xEG8MNZcNXvlOrVFIcyH6KxkQPjQEWxiwCfRxdarFqqAmy4XxIjocAWSCK0f1CBb5ejv mE0C2l/e/yLjcX1w/LJLPfCPf9ycNNhMXOIVnY7Fq7OCK097R5+o47adauVQJ0QSYLfy rU2Y0TrNQNnHJ6I1Kp2ofGeDiXoyBUYC24XputwwjDA19e+Nhg6VsnTUeBD8YpFsLwok J2ng== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:mime-version:message-id:date:subject:cc:to:from :dkim-signature; bh=gPQ5B+gEfn3XCjbg4Rjff52wNdxbfyG/GbCFrOA/3Po=; b=WrUuRKw8duf+WC2pC+GiFhOYk6zZUays+F9/Hz7qatGO7+eXs+m8bFcBIi6U/kngZ+ DmkoZBTheNbWfXspSwuj04hSyjtIf+xRGobsC3XvayUmSFlwrCQ/Ikal0jDitgXyuwbu QUkeBkJIO549bAYwlXPgS0Z8/EiYlo4tEn5kbbZz4zF3uePXaPfBy4h86yTsvo3N4cI8 JEnnpDYVVgkiztOe/uBMqCfq1vSSZ2pWf+5ZZFguZ010Lq0dF07m/ou2nHn/PLuGazXL Wkpo4xHR7ZpijUT8uJJZ/nzfYWTJf/hzuEz6XCG2bFy11lkNyQgkP90TPgSBY+UsKqNp i9oA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@mediatek.com header.s=dk header.b=iOZHQgth; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=mediatek.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id j8-20020a634a48000000b0046abab88fd9si13221609pgl.677.2022.11.22.03.42.37; Tue, 22 Nov 2022 03:42:52 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@mediatek.com header.s=dk header.b=iOZHQgth; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=mediatek.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233301AbiKVLbj (ORCPT + 99 others); Tue, 22 Nov 2022 06:31:39 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50044 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233361AbiKVLa4 (ORCPT ); Tue, 22 Nov 2022 06:30:56 -0500 Received: from mailgw01.mediatek.com (unknown [60.244.123.138]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5DDBD654D0; Tue, 22 Nov 2022 03:24:33 -0800 (PST) X-UUID: d887a2e6ce774faeaadd73561ba80f49-20221122 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mediatek.com; s=dk; h=Content-Type:MIME-Version:Message-ID:Date:Subject:CC:To:From; bh=gPQ5B+gEfn3XCjbg4Rjff52wNdxbfyG/GbCFrOA/3Po=; b=iOZHQgthBocG55wF+xvLPIXXjT7qWfCT/WjvfmFqq/sqWzixFmURT/JjZsggVezsC+ZKgCXH7arozoGE8zngeCJMaCX0tM+w+55gkdlXxZ8//ysgIzS9JX6U81seBWIhQkhMo6QwIsl23GJrk+g2upvPXr1YCx8ea5AAahEc4hc=; X-CID-P-RULE: Release_Ham X-CID-O-INFO: VERSION:1.1.13,REQID:b6b00d61-9e88-40d2-aa86-0137fb28e95c,IP:0,U RL:0,TC:0,Content:-25,EDM:0,RT:0,SF:0,FILE:0,BULK:0,RULE:Release_Ham,ACTIO N:release,TS:-25 X-CID-META: VersionHash:d12e911,CLOUDID:ec96fbf8-3a34-4838-abcf-dfedf9dd068e,B ulkID:nil,BulkQuantity:0,Recheck:0,SF:102,TC:nil,Content:0,EDM:-3,IP:nil,U RL:11|1,File:nil,Bulk:nil,QS:nil,BEC:nil,COL:0 X-UUID: d887a2e6ce774faeaadd73561ba80f49-20221122 Received: from mtkexhb01.mediatek.inc [(172.21.101.102)] by mailgw01.mediatek.com (envelope-from ) (Generic MTA with TLSv1.2 ECDHE-RSA-AES256-SHA384 256/256) with ESMTP id 1999148922; Tue, 22 Nov 2022 19:24:28 +0800 Received: from mtkmbs11n2.mediatek.inc (172.21.101.187) by mtkmbs10n1.mediatek.inc (172.21.101.34) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.792.15; Tue, 22 Nov 2022 19:24:26 +0800 Received: from mcddlt001.gcn.mediatek.inc (10.19.240.15) by mtkmbs11n2.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 15.2.792.15 via Frontend Transport; Tue, 22 Nov 2022 19:24:24 +0800 From: Yanchao Yang To: Loic Poulain , Sergey Ryazanov , Johannes Berg , "David S . Miller" , Eric Dumazet , "Jakub Kicinski" , Paolo Abeni , netdev ML , kernel ML CC: MTK ML , Liang Lu , Haijun Liu , Hua Yang , Ting Wang , Felix Chen , Mingliang Xu , Min Dong , Aiden Wang , Guohao Zhang , Chris Feng , "Yanchao Yang" , Lambert Wang , Mingchuang Qiao , Xiayu Zhang , Haozhe Chang , MediaTek Corporation Subject: [PATCH net-next v1 11/13] net: wwan: tmi: Add exception handling service Date: Tue, 22 Nov 2022 19:24:16 +0800 Message-ID: <20221122112417.160844-1-yanchao.yang@mediatek.com> X-Mailer: git-send-email 2.18.0 MIME-Version: 1.0 X-MTK: N X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_MSPIKE_H2,SPF_HELO_PASS, SPF_PASS,UNPARSEABLE_RELAY autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1750196417535159934?= X-GMAIL-MSGID: =?utf-8?q?1750196417535159934?= From: MediaTek Corporation The exception handling service aims to recover the entire system when the host driver detects some exceptions. The scenarios that could trigger exceptions include: - Read/Write error from the transaction layer when the PCIe link brokes. - An RGU interrupt is received. - The OS reports PCIe link failure, e.g., an AER is detected. When an exception happens, the exception module will receive an exception event, and it will use FLDR or PLDR to reset the device. The exception module will also start a timer to check if the PCIe link is back by reading the vendor ID of the device, and it will re-initialize the host driver when the PCIe link comes back. Signed-off-by: Mingliang Xu Signed-off-by: MediaTek Corporation --- drivers/net/wwan/mediatek/Makefile | 3 +- drivers/net/wwan/mediatek/mtk_cldma.c | 21 ++- drivers/net/wwan/mediatek/mtk_dev.c | 8 + drivers/net/wwan/mediatek/mtk_dev.h | 78 ++++++++ drivers/net/wwan/mediatek/mtk_dpmaif.c | 16 +- drivers/net/wwan/mediatek/mtk_dpmaif_drv.h | 10 +- drivers/net/wwan/mediatek/mtk_except.c | 176 ++++++++++++++++++ drivers/net/wwan/mediatek/mtk_fsm.c | 2 + .../wwan/mediatek/pcie/mtk_cldma_drv_t800.c | 15 +- drivers/net/wwan/mediatek/pcie/mtk_pci.c | 47 +++++ 10 files changed, 358 insertions(+), 18 deletions(-) create mode 100644 drivers/net/wwan/mediatek/mtk_except.c diff --git a/drivers/net/wwan/mediatek/Makefile b/drivers/net/wwan/mediatek/Makefile index 72655ce948bf..f0601d2eb604 100644 --- a/drivers/net/wwan/mediatek/Makefile +++ b/drivers/net/wwan/mediatek/Makefile @@ -15,7 +15,8 @@ mtk_tmi-y = \ mtk_fsm.o \ mtk_dpmaif.o \ mtk_wwan.o \ - mtk_ethtool.o + mtk_ethtool.o \ + mtk_except.o ccflags-y += -I$(srctree)/$(src)/ ccflags-y += -I$(srctree)/$(src)/pcie/ diff --git a/drivers/net/wwan/mediatek/mtk_cldma.c b/drivers/net/wwan/mediatek/mtk_cldma.c index 723237547650..4c8852f8ae9c 100644 --- a/drivers/net/wwan/mediatek/mtk_cldma.c +++ b/drivers/net/wwan/mediatek/mtk_cldma.c @@ -180,19 +180,29 @@ static int mtk_cldma_submit_tx(void *dev, struct sk_buff *skb) struct tx_req *req; struct virtq *vq; struct txq *txq; + int ret = 0; int err; vq = cd->trans->vq_tbl + trb->vqno; hw = cd->cldma_hw[vq->hif_id & HIF_ID_BITMASK]; txq = hw->txq[vq->txqno]; - if (!txq->req_budget) - return -EAGAIN; + if (!txq->req_budget) { + if (mtk_hw_mmio_check(hw->mdev)) { + mtk_except_report_evt(hw->mdev, EXCEPT_LINK_ERR); + ret = -EFAULT; + } else { + ret = -EAGAIN; + } + goto err; + } err = mtk_dma_map_single(hw->mdev, &data_dma_addr, skb->data, skb->len, DMA_TO_DEVICE); - if (err) - return -EFAULT; + if (err) { + ret = -EFAULT; + goto err; + } mutex_lock(&txq->lock); txq->req_budget--; @@ -213,7 +223,8 @@ static int mtk_cldma_submit_tx(void *dev, struct sk_buff *skb) wmb(); /* ensure GPD setup done before HW start */ - return 0; +err: + return ret; } /* cldma_trb_process() - Dispatch trb request to low-level CLDMA routine diff --git a/drivers/net/wwan/mediatek/mtk_dev.c b/drivers/net/wwan/mediatek/mtk_dev.c index d4472491ce9a..d64cbca5b56d 100644 --- a/drivers/net/wwan/mediatek/mtk_dev.c +++ b/drivers/net/wwan/mediatek/mtk_dev.c @@ -29,6 +29,13 @@ int mtk_dev_init(struct mtk_md_dev *mdev) if (ret) goto err_data_init; + ret = mtk_except_init(mdev); + if (ret) + goto err_except_init; + + return 0; +err_except_init: + mtk_data_exit(mdev); err_data_init: mtk_ctrl_exit(mdev); err_ctrl_init: @@ -46,6 +53,7 @@ void mtk_dev_exit(struct mtk_md_dev *mdev) mtk_data_exit(mdev); mtk_ctrl_exit(mdev); mtk_bm_exit(mdev); + mtk_except_exit(mdev); mtk_fsm_exit(mdev); } diff --git a/drivers/net/wwan/mediatek/mtk_dev.h b/drivers/net/wwan/mediatek/mtk_dev.h index 2739b8068a31..010c789e4dda 100644 --- a/drivers/net/wwan/mediatek/mtk_dev.h +++ b/drivers/net/wwan/mediatek/mtk_dev.h @@ -39,6 +39,7 @@ enum mtk_reset_type { RESET_FLDR, RESET_PLDR, RESET_RGU, + RESET_NONE }; enum mtk_reinit_type { @@ -51,6 +52,15 @@ enum mtk_l1ss_grp { L1SS_EXT_EVT, }; +enum mtk_except_evt { + EXCEPT_LINK_ERR, + EXCEPT_RGU, + EXCEPT_AER_DETECTED, + EXCEPT_AER_RESET, + EXCEPT_AER_RESUME, + EXCEPT_MAX +}; + #define L1SS_BIT_L1(grp) BIT(((grp) << 2) + 1) #define L1SS_BIT_L1_1(grp) BIT(((grp) << 2) + 2) #define L1SS_BIT_L1_2(grp) BIT(((grp) << 2) + 3) @@ -83,6 +93,7 @@ struct mtk_md_dev; * @get_ext_evt_status:Callback to get HW Layer external event status. * @reset: Callback to reset device. * @reinit: Callback to execute device re-initialization. + * @link_check: Callback to execute hardware link check. * @get_hp_status: Callback to get link hotplug status. */ struct mtk_hw_ops { @@ -119,10 +130,18 @@ struct mtk_hw_ops { int (*reset)(struct mtk_md_dev *mdev, enum mtk_reset_type type); int (*reinit)(struct mtk_md_dev *mdev, enum mtk_reinit_type type); + bool (*link_check)(struct mtk_md_dev *mdev); bool (*mmio_check)(struct mtk_md_dev *mdev); int (*get_hp_status)(struct mtk_md_dev *mdev); }; +struct mtk_md_except { + atomic_t flag; + enum mtk_reset_type type; + int pci_ext_irq_id; + struct timer_list timer; +}; + /* mtk_md_dev defines the structure of MTK modem device */ struct mtk_md_dev { struct device *dev; @@ -136,6 +155,7 @@ struct mtk_md_dev { void *ctrl_blk; void *data_blk; struct mtk_bm_ctrl *bm_ctrl; + struct mtk_md_except except; }; int mtk_dev_init(struct mtk_md_dev *mdev); @@ -429,6 +449,17 @@ static inline int mtk_hw_reinit(struct mtk_md_dev *mdev, enum mtk_reinit_type ty return mdev->hw_ops->reinit(mdev, type); } +/* mtk_hw_link_check() -Check if the link is down. + * + * @mdev: Device instance. + * + * Return: 0 indicates link normally, other value indicates link down. + */ +static inline bool mtk_hw_link_check(struct mtk_md_dev *mdev) +{ + return mdev->hw_ops->link_check(mdev); +} + /* mtk_hw_mmio_check() -Check if the PCIe MMIO is ready. * * @mdev: Device instance. @@ -517,4 +548,51 @@ static inline int mtk_dma_unmap_page(struct mtk_md_dev *mdev, return 0; } +/* mtk_except_report_evt() - Report exception event. + * + * @mdev: pointer to mtk_md_dev + * @evt: exception event + * + * Return: + * 0 - OK + * -EFAULT - exception feature is not ready + */ +int mtk_except_report_evt(struct mtk_md_dev *mdev, enum mtk_except_evt evt); + +/* mtk_except_start() - Start exception service. + * + * @mdev: pointer to mtk_md_dev + * + * Return: + * void + */ +void mtk_except_start(struct mtk_md_dev *mdev); + +/* mtk_except_stop() - Stop exception service. + * + * @mdev: pointer to mtk_md_dev + * + * Return: + * void + */ +void mtk_except_stop(struct mtk_md_dev *mdev); + +/* mtk_except_init() - Initialize exception feature. + * + * @mdev: pointer to mtk_md_dev + * + * Return: + * 0 - OK + */ +int mtk_except_init(struct mtk_md_dev *mdev); + +/* mtk_except_exit() - De-Initialize exception feature. + * + * @mdev: pointer to mtk_md_dev + * + * Return: + * 0 - OK + */ +int mtk_except_exit(struct mtk_md_dev *mdev); + #endif /* __MTK_DEV_H__ */ diff --git a/drivers/net/wwan/mediatek/mtk_dpmaif.c b/drivers/net/wwan/mediatek/mtk_dpmaif.c index 27b7a5dee707..b6085110c62a 100644 --- a/drivers/net/wwan/mediatek/mtk_dpmaif.c +++ b/drivers/net/wwan/mediatek/mtk_dpmaif.c @@ -536,10 +536,12 @@ static void mtk_dpmaif_common_err_handle(struct mtk_dpmaif_ctlb *dcb, bool is_hw return; } - if (mtk_hw_mmio_check(DCB_TO_MDEV(dcb))) + if (mtk_hw_mmio_check(DCB_TO_MDEV(dcb))) { dev_err(DCB_TO_DEV(dcb), "Failed to access mmio\n"); - else + mtk_except_report_evt(DCB_TO_MDEV(dcb), EXCEPT_LINK_ERR); + } else { mtk_dpmaif_trigger_dev_exception(dcb); + } } static unsigned int mtk_dpmaif_pit_bid(struct dpmaif_pd_pit *pit_info) @@ -1354,7 +1356,7 @@ static unsigned int mtk_dpmaif_poll_tx_drb(struct dpmaif_txq *txq) old_sw_rd_idx = txq->drb_rd_idx; ret = mtk_dpmaif_drv_get_ring_idx(dcb->drv_info, DPMAIF_DRB_RIDX, txq->id); if (unlikely(ret < 0)) { - dev_err(DCB_TO_DEV(dcb), "Failed to read txq%u drb_rd_idx, ret=%d", txq->id, ret); + dev_err(DCB_TO_DEV(dcb), "Failed to read txq%u drb_rd_idx, ret=%d\n", txq->id, ret); mtk_dpmaif_common_err_handle(dcb, true); return 0; } @@ -2274,7 +2276,6 @@ static void mtk_dpmaif_trans_disable(struct mtk_dpmaif_ctlb *dcb) static void mtk_dpmaif_trans_ctl(struct mtk_dpmaif_ctlb *dcb, bool enable) { mutex_lock(&dcb->trans_ctl_lock); - if (enable) { if (!dcb->trans_enabled) { if (dcb->dpmaif_state == DPMAIF_STATE_PWRON && @@ -2641,7 +2642,8 @@ static int mtk_dpmaif_drv_res_init(struct mtk_dpmaif_ctlb *dcb) if (DPMAIF_GET_HW_VER(dcb) == 0x0800) { dcb->drv_info->drv_ops = &dpmaif_drv_ops_t800; } else { - dev_err(DCB_TO_DEV(dcb), "Unsupported mdev, hw_ver=0x%x", DPMAIF_GET_HW_VER(dcb)); + devm_kfree(DCB_TO_DEV(dcb), dcb->drv_info); + dev_err(DCB_TO_DEV(dcb), "Unsupported mdev, hw_ver=0x%x\n", DPMAIF_GET_HW_VER(dcb)); ret = -EFAULT; } @@ -2791,7 +2793,8 @@ static int mtk_dpmaif_irq_init(struct mtk_dpmaif_ctlb *dcb) irq_param->dpmaif_irq_src = irq_src; irq_param->dev_irq_id = mtk_hw_get_irq_id(DCB_TO_MDEV(dcb), irq_src); if (irq_param->dev_irq_id < 0) { - dev_err(DCB_TO_DEV(dcb), "Failed to allocate irq id, irq_src=%d", irq_src); + dev_err(DCB_TO_DEV(dcb), "Failed to allocate irq id, irq_src=%d\n", + irq_src); ret = -EINVAL; goto err_reg_irq; } @@ -3489,6 +3492,7 @@ static int mtk_dpmaif_pit_bid_frag_check(struct dpmaif_rxq *rxq, unsigned int cu bat_ring = &rxq->dcb->bat_info.frag_bat_ring; cur_bat_record = bat_ring->sw_record_base + cur_bid; + if (unlikely(!cur_bat_record->frag.page || cur_bid >= bat_ring->bat_cnt)) { dev_err(DCB_TO_DEV(dcb), "Invalid parameter rxq%u bat%d, bid=%u, bat_cnt=%u\n", diff --git a/drivers/net/wwan/mediatek/mtk_dpmaif_drv.h b/drivers/net/wwan/mediatek/mtk_dpmaif_drv.h index 34ec846e6336..29b6c99bba42 100644 --- a/drivers/net/wwan/mediatek/mtk_dpmaif_drv.h +++ b/drivers/net/wwan/mediatek/mtk_dpmaif_drv.h @@ -84,12 +84,12 @@ enum mtk_drv_err { enum { DPMAIF_CLEAR_INTR, - DPMAIF_UNMASK_INTR + DPMAIF_UNMASK_INTR, }; enum dpmaif_drv_dlq_id { DPMAIF_DLQ0 = 0, - DPMAIF_DLQ1 + DPMAIF_DLQ1, }; struct dpmaif_drv_dlq { @@ -132,7 +132,7 @@ enum dpmaif_drv_ring_type { DPMAIF_PIT, DPMAIF_BAT, DPMAIF_FRAG, - DPMAIF_DRB + DPMAIF_DRB, }; enum dpmaif_drv_ring_idx { @@ -143,7 +143,7 @@ enum dpmaif_drv_ring_idx { DPMAIF_FRAG_WIDX, DPMAIF_FRAG_RIDX, DPMAIF_DRB_WIDX, - DPMAIF_DRB_RIDX + DPMAIF_DRB_RIDX, }; struct dpmaif_drv_irq_en_mask { @@ -184,7 +184,7 @@ enum dpmaif_drv_intr_type { DPMAIF_INTR_DL_FRGCNT_LEN_ERR, DPMAIF_INTR_DL_PITCNT_LEN_ERR, DPMAIF_INTR_DL_DONE, - DPMAIF_INTR_MAX, + DPMAIF_INTR_MAX }; #define DPMAIF_INTR_COUNT ((DPMAIF_INTR_MAX) - (DPMAIF_INTR_MIN) - 1) diff --git a/drivers/net/wwan/mediatek/mtk_except.c b/drivers/net/wwan/mediatek/mtk_except.c new file mode 100644 index 000000000000..e35592d9d2c3 --- /dev/null +++ b/drivers/net/wwan/mediatek/mtk_except.c @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2022, MediaTek Inc. + */ + +#include +#include +#include + +#include "mtk_dev.h" +#include "mtk_fsm.h" + +#define MTK_EXCEPT_HOST_RESET_TIME (2) +#define MTK_EXCEPT_SELF_RESET_TIME (35) +#define MTK_EXCEPT_RESET_TYPE_PLDR BIT(26) +#define MTK_EXCEPT_RESET_TYPE_FLDR BIT(27) + +static void mtk_except_start_monitor(struct mtk_md_dev *mdev, unsigned long expires) +{ + struct mtk_md_except *except = &mdev->except; + + if (!timer_pending(&except->timer) && !mtk_hw_get_hp_status(mdev)) { + except->timer.expires = jiffies + expires; + add_timer(&except->timer); + dev_info(mdev->dev, "Add timer to monitor PCI link\n"); + } +} + +int mtk_except_report_evt(struct mtk_md_dev *mdev, enum mtk_except_evt evt) +{ + struct mtk_md_except *except = &mdev->except; + int err, val; + + if (atomic_read(&except->flag) != 1) + return -EFAULT; + + switch (evt) { + case EXCEPT_LINK_ERR: + err = mtk_hw_mmio_check(mdev); + if (err) + mtk_fsm_evt_submit(mdev, FSM_EVT_LINKDOWN, FSM_F_DFLT, NULL, 0, 0); + break; + case EXCEPT_RGU: + /* delay 20ms to make sure device ready for reset */ + msleep(20); + + val = mtk_hw_get_dev_state(mdev); + dev_info(mdev->dev, "dev_state:0x%x, hw_ver:0x%x, fsm state:%d\n", + val, mdev->hw_ver, mdev->fsm->state); + + /* Invalid dev state will trigger PLDR */ + if (val & MTK_EXCEPT_RESET_TYPE_PLDR) { + except->type = RESET_PLDR; + } else if (val & MTK_EXCEPT_RESET_TYPE_FLDR) { + except->type = RESET_FLDR; + } else if (mdev->fsm->state >= FSM_STATE_READY) { + dev_info(mdev->dev, "HW reboot\n"); + except->type = RESET_NONE; + } else { + dev_info(mdev->dev, "RGU ignored\n"); + break; + } + mtk_fsm_evt_submit(mdev, FSM_EVT_DEV_RESET_REQ, FSM_F_DFLT, NULL, 0, 0); + break; + case EXCEPT_AER_DETECTED: + mtk_fsm_evt_submit(mdev, FSM_EVT_AER, FSM_F_DFLT, NULL, 0, EVT_MODE_BLOCKING); + break; + case EXCEPT_AER_RESET: + err = mtk_hw_reset(mdev, RESET_FLDR); + if (err) + mtk_hw_reset(mdev, RESET_RGU); + break; + case EXCEPT_AER_RESUME: + mtk_except_start_monitor(mdev, HZ); + break; + default: + break; + } + + return 0; +} + +void mtk_except_start(struct mtk_md_dev *mdev) +{ + struct mtk_md_except *except = &mdev->except; + + mtk_hw_unmask_irq(mdev, except->pci_ext_irq_id); +} + +void mtk_except_stop(struct mtk_md_dev *mdev) +{ + struct mtk_md_except *except = &mdev->except; + + mtk_hw_mask_irq(mdev, except->pci_ext_irq_id); +} + +static void mtk_except_fsm_handler(struct mtk_fsm_param *param, void *data) +{ + struct mtk_md_except *except = data; + enum mtk_reset_type reset_type; + struct mtk_md_dev *mdev; + unsigned long expires; + int err; + + mdev = container_of(except, struct mtk_md_dev, except); + + switch (param->to) { + case FSM_STATE_POSTDUMP: + mtk_hw_mask_irq(mdev, except->pci_ext_irq_id); + mtk_hw_clear_irq(mdev, except->pci_ext_irq_id); + mtk_hw_unmask_irq(mdev, except->pci_ext_irq_id); + break; + case FSM_STATE_OFF: + if (param->evt_id == FSM_EVT_DEV_RESET_REQ) + reset_type = except->type; + else if (param->evt_id == FSM_EVT_LINKDOWN) + reset_type = RESET_FLDR; + else + break; + + if (reset_type == RESET_NONE) { + expires = MTK_EXCEPT_SELF_RESET_TIME * HZ; + } else { + err = mtk_hw_reset(mdev, reset_type); + if (err) + expires = MTK_EXCEPT_SELF_RESET_TIME * HZ; + else + expires = MTK_EXCEPT_HOST_RESET_TIME * HZ; + } + + mtk_except_start_monitor(mdev, expires); + break; + default: + break; + } +} + +static void mtk_except_link_monitor(struct timer_list *timer) +{ + struct mtk_md_except *except = container_of(timer, struct mtk_md_except, timer); + struct mtk_md_dev *mdev = container_of(except, struct mtk_md_dev, except); + int err; + + err = mtk_hw_link_check(mdev); + if (!err) { + mtk_fsm_evt_submit(mdev, FSM_EVT_REINIT, FSM_F_FULL_REINIT, NULL, 0, 0); + del_timer(&except->timer); + } else { + mod_timer(timer, jiffies + HZ); + } +} + +int mtk_except_init(struct mtk_md_dev *mdev) +{ + struct mtk_md_except *except = &mdev->except; + + except->pci_ext_irq_id = mtk_hw_get_irq_id(mdev, MTK_IRQ_SRC_SAP_RGU); + + mtk_fsm_notifier_register(mdev, MTK_USER_EXCEPT, + mtk_except_fsm_handler, except, FSM_PRIO_1, false); + timer_setup(&except->timer, mtk_except_link_monitor, 0); + atomic_set(&except->flag, 1); + + return 0; +} + +int mtk_except_exit(struct mtk_md_dev *mdev) +{ + struct mtk_md_except *except = &mdev->except; + + atomic_set(&except->flag, 0); + del_timer(&except->timer); + mtk_fsm_notifier_unregister(mdev, MTK_USER_EXCEPT); + + return 0; +} diff --git a/drivers/net/wwan/mediatek/mtk_fsm.c b/drivers/net/wwan/mediatek/mtk_fsm.c index d754a34ade6c..4ba83134a149 100644 --- a/drivers/net/wwan/mediatek/mtk_fsm.c +++ b/drivers/net/wwan/mediatek/mtk_fsm.c @@ -516,6 +516,8 @@ static int mtk_fsm_early_bootup_handler(u32 status, void *__fsm) dev_stage = dev_state & REGION_BITMASK; if (dev_stage >= DEV_STAGE_MAX) { dev_err(mdev->dev, "Invalid dev state 0x%x\n", dev_state); + if (mtk_hw_link_check(mdev)) + mtk_except_report_evt(mdev, EXCEPT_LINK_ERR); return -ENXIO; } diff --git a/drivers/net/wwan/mediatek/pcie/mtk_cldma_drv_t800.c b/drivers/net/wwan/mediatek/pcie/mtk_cldma_drv_t800.c index 42b4358f2653..c58ec64a59bf 100644 --- a/drivers/net/wwan/mediatek/pcie/mtk_cldma_drv_t800.c +++ b/drivers/net/wwan/mediatek/pcie/mtk_cldma_drv_t800.c @@ -364,8 +364,10 @@ static void mtk_cldma_tx_done_work(struct work_struct *work) state = mtk_cldma_check_intr_status(mdev, txq->hw->base_addr, DIR_TX, txq->txqno, QUEUE_XFER_DONE); if (state) { - if (unlikely(state == LINK_ERROR_VAL)) + if (unlikely(state == LINK_ERROR_VAL)) { + mtk_except_report_evt(mdev, EXCEPT_LINK_ERR); return; + } mtk_cldma_clr_intr_status(mdev, txq->hw->base_addr, DIR_TX, txq->txqno, QUEUE_XFER_DONE); @@ -452,6 +454,11 @@ static void mtk_cldma_rx_done_work(struct work_struct *work) if (!state) break; + if (unlikely(state == LINK_ERROR_VAL)) { + mtk_except_report_evt(mdev, EXCEPT_LINK_ERR); + return; + } + mtk_cldma_clr_intr_status(mdev, rxq->hw->base_addr, DIR_RX, rxq->rxqno, QUEUE_XFER_DONE); @@ -750,6 +757,9 @@ int mtk_cldma_txq_free_t800(struct cldma_hw *hw, int vqno) devm_kfree(hw->mdev->dev, txq); hw->txq[txqno] = NULL; + if (active == LINK_ERROR_VAL) + mtk_except_report_evt(hw->mdev, EXCEPT_LINK_ERR); + return 0; } @@ -915,6 +925,9 @@ int mtk_cldma_rxq_free_t800(struct cldma_hw *hw, int vqno) devm_kfree(mdev->dev, rxq); hw->rxq[rxqno] = NULL; + if (active == LINK_ERROR_VAL) + mtk_except_report_evt(mdev, EXCEPT_LINK_ERR); + return 0; } diff --git a/drivers/net/wwan/mediatek/pcie/mtk_pci.c b/drivers/net/wwan/mediatek/pcie/mtk_pci.c index 7c7cb1f733de..47727567b0c5 100644 --- a/drivers/net/wwan/mediatek/pcie/mtk_pci.c +++ b/drivers/net/wwan/mediatek/pcie/mtk_pci.c @@ -536,6 +536,8 @@ static int mtk_pci_reset(struct mtk_md_dev *mdev, enum mtk_reset_type type) return mtk_pci_fldr(mdev); case RESET_PLDR: return mtk_pci_pldr(mdev); + default: + break; } return -EINVAL; @@ -547,6 +549,12 @@ static int mtk_pci_reinit(struct mtk_md_dev *mdev, enum mtk_reinit_type type) struct mtk_pci_priv *priv = mdev->hw_priv; int ret, ltr, l1ss; + if (type == REINIT_TYPE_EXP) { + /* We have saved it in probe() */ + pci_load_saved_state(pdev, priv->saved_state); + pci_restore_state(pdev); + } + /* restore ltr */ ltr = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_LTR); if (ltr) { @@ -571,6 +579,9 @@ static int mtk_pci_reinit(struct mtk_md_dev *mdev, enum mtk_reinit_type type) mtk_pci_set_msix_merged(priv, priv->irq_cnt); } + if (type == REINIT_TYPE_EXP) + mtk_pci_clear_irq(mdev, priv->rgu_irq_id); + mtk_pci_unmask_irq(mdev, priv->rgu_irq_id); mtk_pci_unmask_irq(mdev, priv->mhccif_irq_id); @@ -634,6 +645,7 @@ static const struct mtk_hw_ops mtk_pci_ops = { .get_ext_evt_status = mtk_mhccif_get_evt_status, .reset = mtk_pci_reset, .reinit = mtk_pci_reinit, + .link_check = mtk_pci_link_check, .mmio_check = mtk_pci_mmio_check, .get_hp_status = mtk_pci_get_hp_status, }; @@ -654,6 +666,7 @@ static void mtk_mhccif_isr_work(struct work_struct *work) if (unlikely(stat == U32_MAX && mtk_pci_link_check(mdev))) { /* When link failed, we don't need to unmask/clear. */ dev_err(mdev->dev, "Failed to check link in MHCCIF handler.\n"); + mtk_except_report_evt(mdev, EXCEPT_LINK_ERR); return; } @@ -778,6 +791,7 @@ static void mtk_rgu_work(struct work_struct *work) struct mtk_pci_priv *priv; struct mtk_md_dev *mdev; struct pci_dev *pdev; + int ret; priv = container_of(to_delayed_work(work), struct mtk_pci_priv, rgu_work); mdev = priv->mdev; @@ -788,6 +802,10 @@ static void mtk_rgu_work(struct work_struct *work) mtk_pci_mask_irq(mdev, priv->rgu_irq_id); mtk_pci_clear_irq(mdev, priv->rgu_irq_id); + ret = mtk_except_report_evt(mdev, EXCEPT_RGU); + if (ret) + dev_err(mdev->dev, "Failed to report exception with EXCEPT_RGU\n"); + if (!pdev->msix_enabled) return; @@ -800,8 +818,14 @@ static int mtk_rgu_irq_cb(int irq_id, void *data) struct mtk_pci_priv *priv; priv = mdev->hw_priv; + + if (delayed_work_pending(&priv->rgu_work)) + goto exit; + schedule_delayed_work(&priv->rgu_work, msecs_to_jiffies(1)); + dev_info(mdev->dev, "RGU IRQ arrived\n"); +exit: return 0; } @@ -1129,16 +1153,39 @@ static void mtk_pci_remove(struct pci_dev *pdev) static pci_ers_result_t mtk_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) { + struct mtk_md_dev *mdev = pci_get_drvdata(pdev); + int ret; + + ret = mtk_except_report_evt(mdev, EXCEPT_AER_DETECTED); + if (ret) + dev_err(mdev->dev, "Failed to call excpetion report API with EXCEPT_AER_DETECTED!\n"); + dev_info(mdev->dev, "AER detected: pci_channel_state_t=%d\n", state); + return PCI_ERS_RESULT_NEED_RESET; } static pci_ers_result_t mtk_pci_slot_reset(struct pci_dev *pdev) { + struct mtk_md_dev *mdev = pci_get_drvdata(pdev); + int ret; + + ret = mtk_except_report_evt(mdev, EXCEPT_AER_RESET); + if (ret) + dev_err(mdev->dev, "Failed to call excpetion report API with EXCEPT_AER_RESET!\n"); + dev_info(mdev->dev, "Slot reset!\n"); + return PCI_ERS_RESULT_RECOVERED; } static void mtk_pci_io_resume(struct pci_dev *pdev) { + struct mtk_md_dev *mdev = pci_get_drvdata(pdev); + int ret; + + ret = mtk_except_report_evt(mdev, EXCEPT_AER_RESUME); + if (ret) + dev_err(mdev->dev, "Failed to call excpetion report API with EXCEPT_AER_RESUME!\n"); + dev_info(mdev->dev, "IO resume!\n"); } static const struct pci_error_handlers mtk_pci_err_handler = {