From patchwork Thu May 15 16:01:32 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Murali Karicheri X-Patchwork-Id: 30269 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-qg0-f71.google.com (mail-qg0-f71.google.com [209.85.192.71]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id 8147620068 for ; Thu, 15 May 2014 16:02:30 +0000 (UTC) Received: by mail-qg0-f71.google.com with SMTP id a108sf2869557qge.2 for ; Thu, 15 May 2014 09:02:30 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:delivered-to:from:to:cc:subject:date:message-id :in-reply-to:references:mime-version:sender:precedence:list-id :x-original-sender:x-original-authentication-results:mailing-list :list-post:list-help:list-archive:list-unsubscribe:content-type; bh=SPV/4ZF3M0BbABHGBMcXQR9yDyWUx3SOLX4LILtPEpE=; b=Vefadmr8jh7qYqkQ4z8CDs55N9PPF86CAObhl3WpB1wX6TnRHx3zmUU/C/0VYEZXVp DpCyFpazecKwjqx1xs+bLVLmHP7zH9snWFQXdPvbTx9ASto2kBgyevgsQyuG9rUfBQG6 rg07+E1b3L3UbBUcNiTNr4vdIL+M6Svvk6KPLuRelBXbLWiJxh3Ov70ilBjeQ1X9iBO2 nTg4ciV7Z29jm1Skqn0/vRj/rz5sA3FEDnq5Mc3eEUMeexvdPU6cTXwCSvsKYpGQpVqt t4TqmSQCaCIvvzpqE3MEC0imruqUcH27z0iIZULKdZg6VfE75qhdx6imgYNAbhkA3EZK dreQ== X-Gm-Message-State: ALoCoQkqO1Cfjl5eYCoGZEFMWLkEPcno+Ks5E94FUo7zbdEtHyy+G0zD2HIq4nM3GuCQDwle2Liu X-Received: by 10.236.117.80 with SMTP id i56mr4497496yhh.48.1400169750365; Thu, 15 May 2014 09:02:30 -0700 (PDT) X-BeenThere: patchwork-forward@linaro.org Received: by 10.140.92.247 with SMTP id b110ls319501qge.16.gmail; Thu, 15 May 2014 09:02:30 -0700 (PDT) X-Received: by 10.52.164.175 with SMTP id yr15mr758493vdb.59.1400169750272; Thu, 15 May 2014 09:02:30 -0700 (PDT) Received: from mail-vc0-f179.google.com (mail-vc0-f179.google.com [209.85.220.179]) by mx.google.com with ESMTPS id iz10si1014894vec.60.2014.05.15.09.02.30 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Thu, 15 May 2014 09:02:30 -0700 (PDT) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.220.179 as permitted sender) client-ip=209.85.220.179; Received: by mail-vc0-f179.google.com with SMTP id im17so4603527vcb.24 for ; Thu, 15 May 2014 09:02:30 -0700 (PDT) X-Received: by 10.220.191.134 with SMTP id dm6mr9409093vcb.16.1400169749949; Thu, 15 May 2014 09:02:29 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.220.221.72 with SMTP id ib8csp334243vcb; Thu, 15 May 2014 09:02:29 -0700 (PDT) X-Received: by 10.68.202.74 with SMTP id kg10mr13327486pbc.163.1400169748990; Thu, 15 May 2014 09:02:28 -0700 (PDT) Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id wh4si2898578pbc.176.2014.05.15.09.02.28; Thu, 15 May 2014 09:02:28 -0700 (PDT) Received-SPF: none (google.com: linux-kernel-owner@vger.kernel.org does not designate permitted sender hosts) client-ip=209.132.180.67; Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755551AbaEOQCR (ORCPT + 27 others); Thu, 15 May 2014 12:02:17 -0400 Received: from arroyo.ext.ti.com ([192.94.94.40]:41354 "EHLO arroyo.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755054AbaEOQCI (ORCPT ); Thu, 15 May 2014 12:02:08 -0400 Received: from dflxv15.itg.ti.com ([128.247.5.124]) by arroyo.ext.ti.com (8.13.7/8.13.7) with ESMTP id s4FG1b2L022138; Thu, 15 May 2014 11:01:37 -0500 Received: from DFLE72.ent.ti.com (dfle72.ent.ti.com [128.247.5.109]) by dflxv15.itg.ti.com (8.14.3/8.13.8) with ESMTP id s4FG1bbV006652; Thu, 15 May 2014 11:01:37 -0500 Received: from dflp32.itg.ti.com (10.64.6.15) by DFLE72.ent.ti.com (128.247.5.109) with Microsoft SMTP Server id 14.3.174.1; Thu, 15 May 2014 11:01:37 -0500 Received: from localhost.localdomain (ileax41-snat.itg.ti.com [10.172.224.153]) by dflp32.itg.ti.com (8.14.3/8.13.8) with ESMTP id s4FG1XJN013438; Thu, 15 May 2014 11:01:37 -0500 From: Murali Karicheri To: , , CC: Murali Karicheri , Santosh Shilimkar , Mohit Kumar , Jingoo Han , Bjorn Helgaas , Grygorii Strashko Subject: [PATCH v1 5/5] pci: keystone: add pcie driver based on designware core driver Date: Thu, 15 May 2014 12:01:32 -0400 Message-ID: <1400169692-9677-6-git-send-email-m-karicheri2@ti.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1400169692-9677-1-git-send-email-m-karicheri2@ti.com> References: <1400169692-9677-1-git-send-email-m-karicheri2@ti.com> MIME-Version: 1.0 Sender: linux-kernel-owner@vger.kernel.org Precedence: list List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: m-karicheri2@ti.com X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.220.179 as permitted sender) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , keystone pcie hardware is based on designware version 3.65. This driver make use of the functions from pci-dw-old.c and pci-dw-old-msi.c to implement the driver. Driver mainly handle the platform specific part of the PCI driver and depends on DW Old driver to configure application specific registers. Also routes the irq events and ack the interrupt after the same is acked by the end point device driver. This requires irqchip implementation for legacy and MSI irq handling. This patch adds a quirks to override the max read request size as PCI controller has a limit of 256 bytes. CC: Santosh Shilimkar CC: Mohit Kumar CC: Jingoo Han CC: Bjorn Helgaas Signed-off-by: Murali Karicheri Signed-off-by: Grygorii Strashko --- .../devicetree/bindings/pci/pcie-keystone.txt | 68 ++++ drivers/pci/host/Kconfig | 8 + drivers/pci/host/Makefile | 1 + drivers/pci/host/pci-keystone.c | 400 ++++++++++++++++++++ drivers/pci/quirks.c | 13 + 5 files changed, 490 insertions(+) create mode 100644 Documentation/devicetree/bindings/pci/pcie-keystone.txt create mode 100644 drivers/pci/host/pci-keystone.c diff --git a/Documentation/devicetree/bindings/pci/pcie-keystone.txt b/Documentation/devicetree/bindings/pci/pcie-keystone.txt new file mode 100644 index 0000000..17cf261 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/pcie-keystone.txt @@ -0,0 +1,68 @@ +Keystone PCIE Root complex device tree bindings +----------------------------------------------- + +Sample bindings shown below:- + + - Remove ti,enable-linktrain if boot loader already does Link training and do EP + configuration. + - Remove ti,init-phy if boot loader already initialize the phy and sets up pcie + link + + pcie0_phy: pciephy@2320000 { + #address-cells = <1>; + #size-cells = <1>; + #phy-cells = <0>; + compatible = "ti,keystone-phy"; + reg = <0x02320000 0x4000>; + reg-names = "reg_serdes"; + }; + + pcie@21800000 { + compatible = "ti,keystone-pcie"; + device_type = "pci"; + clocks = <&clkpcie>; + clock-names = "pcie"; + #address-cells = <3>; + #size-cells = <2>; + reg = <0x21800000 0x1000>, <0x0262014c 4>; + reg-names = "reg_rc_app", "reg_devcfg"; + interrupts = , + , + , + , + , + , + , + , + ; /* Error IRQ */ + + ranges = <0x00000800 0 0x21801000 0x21801000 0 0x0002000 /* Configuration space */ + 0x81000000 0 0 0x24000000 0 0x4000 /* downstream I/O */ + 0x82000000 0 0x50000000 0x50000000 0 0x10000000>; /* non-prefetchable memory */ + + num-lanes = <2>; + ti,enable-linktrain; + ti,init-phy; + + /* PCIE phy */ + phys = <&pcie0_phy>; + phy-names = "pcie-phy"; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 1 &pcie_intc 1>, // INT A + <0 0 0 2 &pcie_intc 2>, // INT B + <0 0 0 3 &pcie_intc 3>, // INT C + <0 0 0 4 &pcie_intc 4>; // INT D + + pcie_intc: legacy-interrupt-controller { + interrupt-controller; + #interrupt-cells = <1>; + interrupt-parent = <&gic>; + interrupts = , + , + , + ; + }; + }; + diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index c4f4732..066d611 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -37,4 +37,12 @@ config PCI_RCAR_GEN2 Say Y here if you want internal PCI support on R-Car Gen2 SoC. There are 3 internal PCI controllers available with a single built-in EHCI/OHCI host controller present on each one. + +config PCI_KEYSTONE + bool "TI Keystone PCIe controller" + depends on ARCH_KEYSTONE + select PCIE_DW + select PCI_DW_OLD + select PCIEPORTBUS + select PHY_TI_KEYSTONE endmenu diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index be5d939..10e02f9 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o obj-$(CONFIG_PCI_DW_OLD) += pci-dw-old-msi.o pci-dw-old.o +obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone.o diff --git a/drivers/pci/host/pci-keystone.c b/drivers/pci/host/pci-keystone.c new file mode 100644 index 0000000..2636717 --- /dev/null +++ b/drivers/pci/host/pci-keystone.c @@ -0,0 +1,400 @@ +/* + * PCIe host controller driver for Texas Instruments Keystone SoCs + * + * Copyright (C) 2013-2014 Texas Instruments., Ltd. + * http://www.ti.com + * + * Author: Murali Karicheri + * Implementation based on pci-exynos.c and pcie-designware.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-designware.h" +#include "pci-dw-old.h" + +#define DRIVER_NAME "keystone-pcie" + +/* driver specific constants */ +#define MAX_MSI_HOST_IRQS 8 +#define MAX_LEGACY_HOST_IRQS 4 + +/* RC mode settings */ +#define PCIE_RC_MODE (BIT(2)) +#define PCIE_MODE_MASK (BIT(1) | BIT(2)) + +static inline struct pcie_port *sys_to_pcie(struct pci_sys_data *sys) +{ + return sys->private_data; +} + +struct keystone_pcie { + struct clk *clk; + int en_link_train; + struct pcie_port pp; + + int num_legacy_host_irqs; + int legacy_host_irqs[MAX_LEGACY_HOST_IRQS]; + struct device_node *np_intc; + + int num_msi_host_irqs; + int msi_host_irqs[MAX_MSI_HOST_IRQS]; +}; + +#define to_keystone_pcie(x) container_of(x, struct keystone_pcie, pp) + +static int ks_pcie_establish_link(struct keystone_pcie *ks_pcie) +{ + struct pcie_port *pp = &ks_pcie->pp; + int count = 200; + + dw_pcie_setup_rc(pp); + + /* check if the link is up or not */ + while (!dw_pcie_link_up(pp)) { + usleep_range(100, 1000); + if (--count) + continue; + dev_err(pp->dev, "phy link never came up\n"); + return -EINVAL; + } + + return 0; +} + +static void ks_pcie_msi_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc); + u32 offset = irq - ks_pcie->msi_host_irqs[0]; + struct pcie_port *pp = &ks_pcie->pp; + struct irq_chip *chip = irq_desc_get_chip(desc); + + dev_dbg(pp->dev, "ks_pcie_msi_irq_handler, irq %d\n", irq); + + /* + * The chained irq handler installation would have replaced normal + * interrupt driver handler so we need to take care of mask/unmask and + * ack operation. + */ + chained_irq_enter(chip, desc); + dw_old_handle_msi_irq(pp, offset); + chained_irq_exit(chip, desc); +} + +/** + * ks_pcie_legacy_irq_handler() - Handle legacy interrupt + * @irq: IRQ line for legacy interrupts + * @desc: Pointer to irq descriptor + * + * Traverse through pending legacy interrupts and invoke handler for each. Also + * takes care of interrupt controller level mask/ack operation. + */ +static void ks_pcie_legacy_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc); + u32 irq_offset = irq - ks_pcie->legacy_host_irqs[0]; + struct irq_chip *chip = irq_desc_get_chip(desc); + struct pcie_port *pp = &ks_pcie->pp; + + dev_dbg(ks_pcie->pp.dev, ": Handling legacy irq %d\n", irq); + + /* + * The chained irq handler installation would have replaced normal + * interrupt driver handler so we need to take care of mask/unmask and + * ack operation. + */ + chained_irq_enter(chip, desc); + dw_old_handle_legacy_irq(pp, irq_offset); + chained_irq_exit(chip, desc); +} + +static int ks_pcie_init_legacy_irqs(struct keystone_pcie *ks_pcie) +{ + struct device *dev = ks_pcie->pp.dev; + struct device_node *np_pcie = dev->of_node; + int num_legacy_irqs; + int ret = 0; + int i; + + /* get node */ + ks_pcie->np_intc = of_find_node_by_name(np_pcie, + "legacy-interrupt-controller"); + if (!ks_pcie->np_intc) { + dev_err(dev, + "Node for legacy-interrupt-controller is absent\n"); + goto out; + } + + /* get number of IRQs */ + num_legacy_irqs = of_irq_count(ks_pcie->np_intc); + if (!num_legacy_irqs) + goto out; + + if (num_legacy_irqs > MAX_LEGACY_HOST_IRQS) { + dev_err(dev, "Too many legacy interrupts defined %u\n", + num_legacy_irqs); + num_legacy_irqs = MAX_LEGACY_HOST_IRQS; + } + /* + * support upto MAX_LEGACY_HOST_IRQS host and legacy IRQs. + * In dt from index 0 to 3 + */ + for (i = 0; i < num_legacy_irqs; i++) { + ks_pcie->legacy_host_irqs[i] = + irq_of_parse_and_map(ks_pcie->np_intc, i); + if (ks_pcie->legacy_host_irqs[i] < 0) + break; + ks_pcie->num_legacy_host_irqs++; + } + + if (!ks_pcie->num_legacy_host_irqs) { + dev_err(dev, "Failed to get legacy interrupts\n"); + goto out; + } + +out: + return ret; +} + +static void ks_pcie_enable_interrupts(struct keystone_pcie *ks_pcie) +{ + struct pcie_port *pp = &ks_pcie->pp; + int i; + + /* Legacy IRQ */ + for (i = 0; i < ks_pcie->num_legacy_host_irqs; i++) { + irq_set_handler_data(ks_pcie->legacy_host_irqs[i], ks_pcie); + irq_set_chained_handler(ks_pcie->legacy_host_irqs[i], + ks_pcie_legacy_irq_handler); + } + dw_old_enable_legacy_irqs(pp); + + /* MSI IRQ */ + if (IS_ENABLED(CONFIG_PCI_MSI)) { + for (i = 0; i < ks_pcie->num_msi_host_irqs; i++) { + irq_set_chained_handler(ks_pcie->msi_host_irqs[i], + ks_pcie_msi_irq_handler); + irq_set_handler_data(ks_pcie->msi_host_irqs[i], + ks_pcie); + + } + } + + return; +} + +static int +keystone_pcie_fault(unsigned long addr, unsigned int fsr, + struct pt_regs *regs) +{ + unsigned long instr = *(unsigned long *) instruction_pointer(regs); + + if ((instr & 0x0e100090) == 0x00100090) { + int reg = (instr >> 12) & 15; + + regs->uregs[reg] = -1; + regs->ARM_pc += 4; + } + + return 0; +} + +static void __init ks_pcie_host_init(struct pcie_port *pp) +{ + struct keystone_pcie *ks_pcie = to_keystone_pcie(pp); + + ks_pcie_establish_link(ks_pcie); + dw_old_disable_bars(pp); + dw_old_setup_ob_regs(pp); + ks_pcie_enable_interrupts(ks_pcie); + writew(PCI_IO_RANGE_TYPE_32 | (PCI_IO_RANGE_TYPE_32 << 8), + pp->dbi_base + PCI_IO_BASE); + /* + * PCIe access errors that result into OCP errors are caught by ARM as + * "External aborts" (Precise). + */ + hook_fault_code(17, keystone_pcie_fault, SIGBUS, 0, + "external abort on linefetch"); +} + +int ks_pcie_link_up(struct pcie_port *pp) +{ + struct keystone_pcie *ks_pcie = to_keystone_pcie(pp); + + return dw_old_pcie_link_up(pp, ks_pcie->en_link_train); +} + +static struct pcie_host_ops keystone_pcie_host_ops = { + .rd_other_conf = dw_old_rd_other_conf, + .wr_other_conf = dw_old_wr_other_conf, + .link_up = ks_pcie_link_up, + .host_init = ks_pcie_host_init, + .get_msi_data = dw_old_get_msi_data, +}; + +static int add_pcie_port(struct keystone_pcie *ks_pcie, + struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct pcie_port *pp = &ks_pcie->pp; + struct resource *res; + int ret, i; + + ret = ks_pcie_init_legacy_irqs(ks_pcie); + if (ret) + return ret; + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + /* + * support upto 32 MSI irqs mapped to 8 host IRQs. + * In dt from index 4 to 11 + */ + for (i = 0; i < MAX_MSI_HOST_IRQS; i++) { + ks_pcie->msi_host_irqs[i] = irq_of_parse_and_map(np, i); + if (ks_pcie->msi_host_irqs[i] < 0) + break; + ks_pcie->num_msi_host_irqs++; + } + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "reg_rc_app"); + pp->va_app_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pp->va_app_base)) + return PTR_ERR(pp->va_app_base); + pp->app_base = res->start; + + pp->root_bus_nr = -1; + pp->version = DW_VERSION_OLD; + pp->ops = &keystone_pcie_host_ops; + spin_lock_init(&pp->conf_lock); + ret = dw_old_pcie_host_init(pp, ks_pcie->np_intc); + if (ret) { + dev_err(&pdev->dev, "failed to initialize host\n"); + return ret; + } + + return ret; +} + +static const struct of_device_id ks_pcie_of_match[] = { + { + .type = "pci", + .compatible = "ti,keystone-pcie", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, ks_pcie_of_match); + +static int __exit ks_pcie_remove(struct platform_device *pdev) +{ + struct keystone_pcie *ks_pcie = platform_get_drvdata(pdev); + + clk_disable_unprepare(ks_pcie->clk); + + return 0; +} + +static int __init ks_pcie_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct keystone_pcie *ks_pcie; + void __iomem *devstat; + struct pcie_port *pp; + struct resource *res; + struct phy *phy; + int ret = 0; + u32 val; + + ks_pcie = devm_kzalloc(&pdev->dev, sizeof(*ks_pcie), + GFP_KERNEL); + if (!ks_pcie) { + dev_err(dev, "no memory for keystone pcie\n"); + return -ENOMEM; + } + + /* check if serdes phy needs to be enabled */ + if (of_get_property(np, "ti,init-phy", NULL) != NULL) { + phy = devm_phy_get(dev, "pcie-phy"); + if (IS_ERR(phy)) + return PTR_ERR(phy); + + ret = phy_init(phy); + if (ret < 0) + return ret; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "reg_devcfg"); + devstat = devm_ioremap_resource(dev, res); + if (IS_ERR(devstat)) + return PTR_ERR(devstat); + + /* enable RC mode in devcfg */ + val = readl(devstat); + val &= ~PCIE_MODE_MASK; + val |= PCIE_RC_MODE; + writel(val, devstat); + + /* check if we need to enable link training */ + ks_pcie->en_link_train = + (of_get_property(np, "ti,enable-linktrain", NULL) != NULL); + + pp = &ks_pcie->pp; + pp->dev = dev; + + ks_pcie->clk = devm_clk_get(dev, "pcie"); + if (IS_ERR(ks_pcie->clk)) { + dev_err(dev, "Failed to get pcie rc clock\n"); + return PTR_ERR(ks_pcie->clk); + } + ret = clk_prepare_enable(ks_pcie->clk); + if (ret) + return ret; + + ret = add_pcie_port(ks_pcie, pdev); + if (ret < 0) + goto fail_clk; + + platform_set_drvdata(pdev, ks_pcie); + dev_info(dev, "pcie rc probe success\n"); + + return 0; + +fail_clk: + clk_disable_unprepare(ks_pcie->clk); + + return ret; +} + +static struct platform_driver ks_pcie_driver __refdata = { + .probe = ks_pcie_probe, + .remove = __exit_p(ks_pcie_remove), + .driver = { + .name = "keystone-pcie", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ks_pcie_of_match), + }, +}; + +module_platform_driver(ks_pcie_driver); + +MODULE_AUTHOR("Murali Karicheri "); +MODULE_DESCRIPTION("Keystone PCIe host controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index e729206..b3e12c2 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3651,3 +3651,16 @@ void pci_dev_specific_enable_acs(struct pci_dev *dev) } } } +#ifdef CONFIG_PCI_KEYSTONE +/* + * The KeyStone PCIe controller has maximum read request size of 256 bytes. + */ +static void quirk_limit_readrequest(struct pci_dev *dev) +{ + int readrq = pcie_get_readrq(dev); + + if (readrq > 256) + pcie_set_readrq(dev, 256); +} +DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, quirk_limit_readrequest); +#endif /* CONFIG_PCI_KEYSTONE */