From patchwork Tue Aug 15 05:55:45 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oza Pawandeep X-Patchwork-Id: 9900923 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 61CE4603FB for ; Tue, 15 Aug 2017 06:03:42 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 541C5286F8 for ; Tue, 15 Aug 2017 06:03:42 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 48ECA28745; Tue, 15 Aug 2017 06:03:42 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.6 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,RCVD_IN_DNSWL_LOW autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [65.50.211.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 7039428757 for ; Tue, 15 Aug 2017 06:03:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:List-Subscribe:List-Help: List-Post:List-Archive:List-Unsubscribe:List-Id:References:In-Reply-To: Message-Id:Date:Subject:To:From:Reply-To:Cc:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=/jqCN/IrwXIaAraRMzBwkl6jBUc8UyzypcSfmkWq7Sk=; b=ZfCnovz7kz+H/R lDfn/d0pWoDWnmEPx8ag0DuszGnJMzJK86JKfhnDFUjEYSGn4SBU5UFvpGbf22rcHNfq/Nt8zFQK3 At1O6eSfB1LUP0TSLHGj6LGzQaCOQDM7wlNOXxtjhMGzAKT4KJUh7Hyg5oQDGRUOqQAJnK2a+t20O FzfP/c6emgHqmt8Jx/t10uKLNHY96Y03CIn/YYUqPt+zTukYpBHDJL07dFd0NU5JFp9OEFUuMRYQh yNl0eRU/mCfPhEu0MRlFE+O0HykhA3zH7QqkrKDGdXQLa6SL+2SroMsG0KoK8Oj2nEMegpVL8W6xU Kn6rg8jPxTV5hC1lVeXQ==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.87 #1 (Red Hat Linux)) id 1dhUws-0008Q4-83; Tue, 15 Aug 2017 06:03:30 +0000 Received: from mail-wm0-x22b.google.com ([2a00:1450:400c:c09::22b]) by bombadil.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1dhUq0-0004t4-TV for linux-arm-kernel@lists.infradead.org; Tue, 15 Aug 2017 05:56:39 +0000 Received: by mail-wm0-x22b.google.com with SMTP id t201so2161793wmt.1 for ; Mon, 14 Aug 2017 22:56:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=broadcom.com; s=google; h=from:to:subject:date:message-id:in-reply-to:references; bh=0MOtzprAiodqyKAoYcgRR6X/DCvi1AwSL8DjXHnyIdg=; b=ModT97/qajCCHcd5IDfx3TVMBi8u7uICvU4KK+2sCL/Otj9TN52gX7vJ8tSHQLe1cd tSoj/3l72/epMfa0BmfsCjgSJCLz5963iM1HuOHlngp8kchzL8ZSnbqlsTNYdDhstJmc 973bb/tDY8UyHw8TzEvvfB+Kp4xl/UWPFhs68= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=0MOtzprAiodqyKAoYcgRR6X/DCvi1AwSL8DjXHnyIdg=; b=EZIDipyKDlobeu7xLgF848QSGdEapKU5Av5sLWY5sTxY83Pm+uoOHfyCCtt3bHhnkN iX3Ofi7P/2+nteXlQQJpvuo0+Ya+ryODq+5ROPGCtSflcorZ7LwSqh5Bdqj8Xbmc2vYA /VSMBtyDvWC5Vt/9RxB5jN/axyNURBEk5/rf3dX/XQDtcMLWEqJvCFmruEOJPz+wg97r VtWoj3lkt64y5Z+gdnd1c7HbTARvBmGRx531FfzsUaOZrZi7lHMZck6fu4LaamX10rux X48MaH6ESVWybP2rBgiZZsp1JDLmUSA/bqGt2MA714wTCjODiSPwKJUnRTAwYWiqH2RT Y91g== X-Gm-Message-State: AHYfb5hz0Ly7cr8hmbJ9eH/i1cYUIIFTDL4eaxePE6AFLTFq5L1Z+JR+ LgCZWRQ8uOYuPO7R X-Received: by 10.28.193.203 with SMTP id r194mr674302wmf.85.1502776562372; Mon, 14 Aug 2017 22:56:02 -0700 (PDT) Received: from anjanavk-OptiPlex-7010.dhcp.avagotech.net ([192.19.237.250]) by smtp.gmail.com with ESMTPSA id 9sm7597374wrl.57.2017.08.14.22.55.55 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 14 Aug 2017 22:56:01 -0700 (PDT) From: Oza Pawandeep To: Bjorn Helgaas , Rob Herring , Mark Rutland , Ray Jui , Scott Branden , Jon Mason , bcm-kernel-feedback-list@broadcom.com, Oza Pawandeep , Andy Gospodarek , linux-pci@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Oza Pawandeep Subject: [PATCH v2 1/3] PCI: iproc: Implement PCI hotplug support Date: Tue, 15 Aug 2017 11:25:45 +0530 Message-Id: <1502776547-30542-2-git-send-email-oza.oza@broadcom.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1502776547-30542-1-git-send-email-oza.oza@broadcom.com> References: <1502776547-30542-1-git-send-email-oza.oza@broadcom.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20170814_225625_539791_83B8B24D X-CRM114-Status: GOOD ( 19.81 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP This patch implements PCI hotplug support for iproc family chipsets. iproc based SOC (e.g. Stingray) does not have hotplug controller integrated. Hence, standard PCI hotplug framework hooks can-not be used. e.g. controlled power up/down of slot. The mechanism, for e.g. Stingray has adopted for PCI hotplug is as follows: PCI present lines are input to GPIOs depending on the type of connector (x2, x4, x8). The implementation essentially takes care of following: > Initializing hotplug irq thread. > Detecting the endpoint device based on link state. > Handling PERST and detecting the plugged devices. > Ordered Hot plug-out, where User is expected to write 1 to /sys/bus/pci/devices//remove > Handling spurious interrupt > Handling multiple interrupts and makes sure that card is enumerated only once. Signed-off-by: Oza Pawandeep Reviewed-by: Ray Jui diff --git a/drivers/pci/host/pcie-iproc-platform.c b/drivers/pci/host/pcie-iproc-platform.c index 9512960..e1eb141 100644 --- a/drivers/pci/host/pcie-iproc-platform.c +++ b/drivers/pci/host/pcie-iproc-platform.c @@ -89,6 +89,9 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev) pcie->need_ob_cfg = true; } + if (of_property_read_bool(np, "slot-pluggable")) + pcie->enable_hotplug = true; + /* PHY use is optional */ pcie->phy = devm_phy_get(dev, "pcie-phy"); if (IS_ERR(pcie->phy)) { diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c index ee40651..eb919f7 100644 --- a/drivers/pci/host/pcie-iproc.c +++ b/drivers/pci/host/pcie-iproc.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "pcie-iproc.h" @@ -65,6 +66,17 @@ #define PCIE_DL_ACTIVE_SHIFT 2 #define PCIE_DL_ACTIVE BIT(PCIE_DL_ACTIVE_SHIFT) +#define CFG_RC_LTSSM 0x1cf8 +#define CFG_RC_PHY_CTL 0x1804 +#define CFG_RC_LTSSM_TIMEOUT 1000 +#define CFG_RC_LTSSM_STATE_MASK 0xff +#define CFG_RC_LTSSM_STATE_L1 0x1 + +#define CFG_RC_CLR_LTSSM_HIST_SHIFT 29 +#define CFG_RC_CLR_LTSSM_HIST_MASK BIT(CFG_RC_CLR_LTSSM_HIST_SHIFT) +#define CFG_RC_CLR_RECOV_HIST_SHIFT 31 +#define CFG_RC_CLR_RECOV_HIST_MASK BIT(CFG_RC_CLR_RECOV_HIST_SHIFT) + #define APB_ERR_EN_SHIFT 0 #define APB_ERR_EN BIT(APB_ERR_EN_SHIFT) @@ -1306,12 +1318,106 @@ static int iproc_pcie_rev_init(struct iproc_pcie *pcie) return 0; } +static bool iproc_pci_hp_check_ltssm(struct iproc_pcie *pcie) +{ + struct pci_bus *bus = pcie->root_bus; + u32 val, timeout = CFG_RC_LTSSM_TIMEOUT; + + /* Clear LTSSM history. */ + pci_bus_read_config_dword(pcie->root_bus, 0, + CFG_RC_PHY_CTL, &val); + pci_bus_write_config_dword(bus, 0, CFG_RC_PHY_CTL, + val | CFG_RC_CLR_RECOV_HIST_MASK | + CFG_RC_CLR_LTSSM_HIST_MASK); + /* write back the origional value. */ + pci_bus_write_config_dword(bus, 0, CFG_RC_PHY_CTL, val); + + do { + pci_bus_read_config_dword(pcie->root_bus, 0, + CFG_RC_LTSSM, &val); + /* check link state to see if link moved to L1 state. */ + if ((val & CFG_RC_LTSSM_STATE_MASK) == + CFG_RC_LTSSM_STATE_L1) + return true; + timeout--; + usleep_range(500, 1000); + } while (timeout); + + return false; +} + +static irqreturn_t iproc_pci_hotplug_thread(int irq, void *data) +{ + struct iproc_pcie *pcie = data; + struct pci_bus *bus = pcie->root_bus, *child; + bool link_status; + + iproc_pcie_perst_ctrl(pcie, true); + iproc_pcie_perst_ctrl(pcie, false); + + link_status = iproc_pci_hp_check_ltssm(pcie); + + if (link_status && + !iproc_pcie_check_link(pcie, bus) && + !pcie->ep_is_present) { + pci_rescan_bus(bus); + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); + pcie->ep_is_present = true; + dev_info(pcie->dev, + "PCI Hotplug: \n"); + } else if (link_status && pcie->ep_is_present) + /* + * ep_is_present makes sure, enumuration done only once. + * So it can handle spurious intrrupts, and also if we + * get multiple interrupts for all the implemented pins, + * we handle it only once. + */ + dev_info(pcie->dev, + "PCI Hotplug: \n"); + else { + iproc_pcie_perst_ctrl(pcie, true); + pcie->ep_is_present = false; + dev_info(pcie->dev, + "PCI Hotplug: \n"); + } + return IRQ_HANDLED; +} + +static int iproc_pci_hp_gpio_irq_get(struct iproc_pcie *pcie) +{ + struct gpio_descs *hp_gpiod; + struct device *dev = pcie->dev; + int i; + + hp_gpiod = devm_gpiod_get_array(dev, "prsnt", GPIOD_IN); + if (PTR_ERR(hp_gpiod) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + if (!IS_ERR(hp_gpiod) && (hp_gpiod->ndescs > 0)) { + for (i = 0; i < hp_gpiod->ndescs; ++i) { + gpiod_direction_input(hp_gpiod->desc[i]); + if (request_threaded_irq(gpiod_to_irq + (hp_gpiod->desc[i]), + NULL, iproc_pci_hotplug_thread, + IRQF_TRIGGER_FALLING, + "PCI-hotplug", pcie)) + dev_err(dev, + "PCI hotplug prsnt: request irq failed\n"); + } + } + pcie->ep_is_present = false; + + return 0; +} + int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) { struct device *dev; int ret; void *sysdata; struct pci_bus *bus, *child; + bool is_link_active; dev = pcie->dev; @@ -1337,6 +1443,12 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) goto err_exit_phy; } + if (pcie->enable_hotplug) { + ret = iproc_pci_hp_gpio_irq_get(pcie); + if (ret < 0) + return ret; + } + iproc_pcie_perst_ctrl(pcie, true); iproc_pcie_perst_ctrl(pcie, false); @@ -1367,8 +1479,11 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) } pcie->root_bus = bus; - ret = iproc_pcie_check_link(pcie, bus); - if (ret) { + is_link_active = iproc_pcie_check_link(pcie, bus); + if (is_link_active && pcie->enable_hotplug) { + dev_err(dev, "no PCIe EP device detected\n"); + iproc_pcie_perst_ctrl(pcie, true); + } else if (is_link_active) { dev_err(dev, "no PCIe EP device detected\n"); goto err_rm_root_bus; } @@ -1379,14 +1494,17 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) if (iproc_pcie_msi_enable(pcie)) dev_info(dev, "not using iProc MSI\n"); - pci_scan_child_bus(bus); - pci_assign_unassigned_bus_resources(bus); + if (!is_link_active) { + pci_scan_child_bus(bus); + pci_assign_unassigned_bus_resources(bus); + } if (pcie->map_irq) pci_fixup_irqs(pci_common_swizzle, pcie->map_irq); - list_for_each_entry(child, &bus->children, node) - pcie_bus_configure_settings(child); + if (!is_link_active) + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); pci_bus_add_devices(bus); diff --git a/drivers/pci/host/pcie-iproc.h b/drivers/pci/host/pcie-iproc.h index a6b55ce..e5d0cd4 100644 --- a/drivers/pci/host/pcie-iproc.h +++ b/drivers/pci/host/pcie-iproc.h @@ -77,6 +77,10 @@ struct iproc_pcie_ib { * @ib: inbound mapping related parameters * @ib_map: outbound mapping region related parameters * + * @enable_hotplug: indicates PCI hotplug feature is enabled + * @ep_is_present: when PCIe hotplug is enabled, this flag is used to + * indicate whether or not the endpoint device is present + * * @need_msi_steer: indicates additional configuration of the iProc PCIe * controller is required to steer MSI writes to external interrupt controller * @msi: MSI data @@ -104,6 +108,9 @@ struct iproc_pcie { struct iproc_pcie_ib ib; const struct iproc_pcie_ib_map *ib_map; + bool enable_hotplug; + bool ep_is_present; + bool need_msi_steer; struct iproc_msi *msi; };