Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/pci/controller/dwc/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 7 kB image not shown  

Quelle  pci-layerscape-ep.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 * PCIe controller EP driver for Freescale Layerscape SoCs
 *
 * Copyright (C) 2018 NXP Semiconductor.
 *
 * Author: Xiaowei Bao <xiaowei.bao@nxp.com>
 */


#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of_pci.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/resource.h>

#include "pcie-designware.h"

#define PEX_PF0_CONFIG   0xC0014
#define PEX_PF0_CFG_READY  BIT(0)

/* PEX PFa PCIE PME and message interrupt registers*/
#define PEX_PF0_PME_MES_DR  0xC0020
#define PEX_PF0_PME_MES_DR_LUD  BIT(7)
#define PEX_PF0_PME_MES_DR_LDD  BIT(9)
#define PEX_PF0_PME_MES_DR_HRD  BIT(10)

#define PEX_PF0_PME_MES_IER  0xC0028
#define PEX_PF0_PME_MES_IER_LUDIE BIT(7)
#define PEX_PF0_PME_MES_IER_LDDIE BIT(9)
#define PEX_PF0_PME_MES_IER_HRDIE BIT(10)

#define to_ls_pcie_ep(x) dev_get_drvdata((x)->dev)

struct ls_pcie_ep_drvdata {
 u32    func_offset;
 const struct dw_pcie_ep_ops *ops;
 const struct dw_pcie_ops *dw_pcie_ops;
};

struct ls_pcie_ep {
 struct dw_pcie   *pci;
 struct pci_epc_features  *ls_epc;
 const struct ls_pcie_ep_drvdata *drvdata;
 int    irq;
 u32    lnkcap;
 bool    big_endian;
};

static u32 ls_pcie_pf_lut_readl(struct ls_pcie_ep *pcie, u32 offset)
{
 struct dw_pcie *pci = pcie->pci;

 if (pcie->big_endian)
  return ioread32be(pci->dbi_base + offset);
 else
  return ioread32(pci->dbi_base + offset);
}

static void ls_pcie_pf_lut_writel(struct ls_pcie_ep *pcie, u32 offset, u32 value)
{
 struct dw_pcie *pci = pcie->pci;

 if (pcie->big_endian)
  iowrite32be(value, pci->dbi_base + offset);
 else
  iowrite32(value, pci->dbi_base + offset);
}

static irqreturn_t ls_pcie_ep_event_handler(int irq, void *dev_id)
{
 struct ls_pcie_ep *pcie = dev_id;
 struct dw_pcie *pci = pcie->pci;
 u32 val, cfg;
 u8 offset;

 val = ls_pcie_pf_lut_readl(pcie, PEX_PF0_PME_MES_DR);
 ls_pcie_pf_lut_writel(pcie, PEX_PF0_PME_MES_DR, val);

 if (!val)
  return IRQ_NONE;

 if (val & PEX_PF0_PME_MES_DR_LUD) {

  offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);

  /*
 * The values of the Maximum Link Width and Supported Link
 * Speed from the Link Capabilities Register will be lost
 * during link down or hot reset. Restore initial value
 * that configured by the Reset Configuration Word (RCW).
 */

  dw_pcie_dbi_ro_wr_en(pci);
  dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCAP, pcie->lnkcap);
  dw_pcie_dbi_ro_wr_dis(pci);

  cfg = ls_pcie_pf_lut_readl(pcie, PEX_PF0_CONFIG);
  cfg |= PEX_PF0_CFG_READY;
  ls_pcie_pf_lut_writel(pcie, PEX_PF0_CONFIG, cfg);
  dw_pcie_ep_linkup(&pci->ep);

  dev_dbg(pci->dev, "Link up\n");
 } else if (val & PEX_PF0_PME_MES_DR_LDD) {
  dev_dbg(pci->dev, "Link down\n");
  dw_pcie_ep_linkdown(&pci->ep);
 } else if (val & PEX_PF0_PME_MES_DR_HRD) {
  dev_dbg(pci->dev, "Hot reset\n");
 }

 return IRQ_HANDLED;
}

static int ls_pcie_ep_interrupt_init(struct ls_pcie_ep *pcie,
         struct platform_device *pdev)
{
 u32 val;
 int ret;

 pcie->irq = platform_get_irq_byname(pdev, "pme");
 if (pcie->irq < 0)
  return pcie->irq;

 ret = devm_request_irq(&pdev->dev, pcie->irq, ls_pcie_ep_event_handler,
          IRQF_SHARED, pdev->name, pcie);
 if (ret) {
  dev_err(&pdev->dev, "Can't register PCIe IRQ\n");
  return ret;
 }

 /* Enable interrupts */
 val = ls_pcie_pf_lut_readl(pcie, PEX_PF0_PME_MES_IER);
 val |=  PEX_PF0_PME_MES_IER_LDDIE | PEX_PF0_PME_MES_IER_HRDIE |
  PEX_PF0_PME_MES_IER_LUDIE;
 ls_pcie_pf_lut_writel(pcie, PEX_PF0_PME_MES_IER, val);

 return 0;
}

static const struct pci_epc_features*
ls_pcie_ep_get_features(struct dw_pcie_ep *ep)
{
 struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
 struct ls_pcie_ep *pcie = to_ls_pcie_ep(pci);

 return pcie->ls_epc;
}

static void ls_pcie_ep_init(struct dw_pcie_ep *ep)
{
 struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
 struct ls_pcie_ep *pcie = to_ls_pcie_ep(pci);
 struct dw_pcie_ep_func *ep_func;
 enum pci_barno bar;

 ep_func = dw_pcie_ep_get_func_from_ep(ep, 0);
 if (!ep_func)
  return;

 for (bar = 0; bar < PCI_STD_NUM_BARS; bar++)
  dw_pcie_ep_reset_bar(pci, bar);

 pcie->ls_epc->msi_capable = ep_func->msi_cap ? true : false;
 pcie->ls_epc->msix_capable = ep_func->msix_cap ? true : false;
}

static int ls_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
    unsigned int type, u16 interrupt_num)
{
 struct dw_pcie *pci = to_dw_pcie_from_ep(ep);

 switch (type) {
 case PCI_IRQ_INTX:
  return dw_pcie_ep_raise_intx_irq(ep, func_no);
 case PCI_IRQ_MSI:
  return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num);
 case PCI_IRQ_MSIX:
  return dw_pcie_ep_raise_msix_irq_doorbell(ep, func_no,
         interrupt_num);
 default:
  dev_err(pci->dev, "UNKNOWN IRQ type\n");
  return -EINVAL;
 }
}

static unsigned int ls_pcie_ep_get_dbi_offset(struct dw_pcie_ep *ep, u8 func_no)
{
 struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
 struct ls_pcie_ep *pcie = to_ls_pcie_ep(pci);

 WARN_ON(func_no && !pcie->drvdata->func_offset);
 return pcie->drvdata->func_offset * func_no;
}

static const struct dw_pcie_ep_ops ls_pcie_ep_ops = {
 .init = ls_pcie_ep_init,
 .raise_irq = ls_pcie_ep_raise_irq,
 .get_features = ls_pcie_ep_get_features,
 .get_dbi_offset = ls_pcie_ep_get_dbi_offset,
};

static const struct ls_pcie_ep_drvdata ls1_ep_drvdata = {
 .ops = &ls_pcie_ep_ops,
};

static const struct ls_pcie_ep_drvdata ls2_ep_drvdata = {
 .func_offset = 0x20000,
 .ops = &ls_pcie_ep_ops,
};

static const struct ls_pcie_ep_drvdata lx2_ep_drvdata = {
 .func_offset = 0x8000,
 .ops = &ls_pcie_ep_ops,
};

static const struct of_device_id ls_pcie_ep_of_match[] = {
 { .compatible = "fsl,ls1028a-pcie-ep", .data = &ls1_ep_drvdata },
 { .compatible = "fsl,ls1046a-pcie-ep", .data = &ls1_ep_drvdata },
 { .compatible = "fsl,ls1088a-pcie-ep", .data = &ls2_ep_drvdata },
 { .compatible = "fsl,ls2088a-pcie-ep", .data = &ls2_ep_drvdata },
 { .compatible = "fsl,lx2160ar2-pcie-ep", .data = &lx2_ep_drvdata },
 { },
};

static int __init ls_pcie_ep_probe(struct platform_device *pdev)
{
 struct device *dev = &pdev->dev;
 struct dw_pcie *pci;
 struct ls_pcie_ep *pcie;
 struct pci_epc_features *ls_epc;
 struct resource *dbi_base;
 u8 offset;
 int ret;

 pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
 if (!pcie)
  return -ENOMEM;

 pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
 if (!pci)
  return -ENOMEM;

 ls_epc = devm_kzalloc(dev, sizeof(*ls_epc), GFP_KERNEL);
 if (!ls_epc)
  return -ENOMEM;

 pcie->drvdata = of_device_get_match_data(dev);

 pci->dev = dev;
 pci->ops = pcie->drvdata->dw_pcie_ops;

 ls_epc->bar[BAR_2].only_64bit = true;
 ls_epc->bar[BAR_3].type = BAR_RESERVED;
 ls_epc->bar[BAR_4].only_64bit = true;
 ls_epc->bar[BAR_5].type = BAR_RESERVED;
 ls_epc->linkup_notifier = true;

 pcie->pci = pci;
 pcie->ls_epc = ls_epc;

 dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
 pci->dbi_base = devm_pci_remap_cfg_resource(dev, dbi_base);
 if (IS_ERR(pci->dbi_base))
  return PTR_ERR(pci->dbi_base);

 pci->ep.ops = &ls_pcie_ep_ops;

 pcie->big_endian = of_property_read_bool(dev->of_node, "big-endian");

 dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));

 platform_set_drvdata(pdev, pcie);

 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
 pcie->lnkcap = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP);

 ret = dw_pcie_ep_init(&pci->ep);
 if (ret)
  return ret;

 ret = dw_pcie_ep_init_registers(&pci->ep);
 if (ret) {
  dev_err(dev, "Failed to initialize DWC endpoint registers\n");
  dw_pcie_ep_deinit(&pci->ep);
  return ret;
 }

 pci_epc_init_notify(pci->ep.epc);

 return ls_pcie_ep_interrupt_init(pcie, pdev);
}

static struct platform_driver ls_pcie_ep_driver = {
 .driver = {
  .name = "layerscape-pcie-ep",
  .of_match_table = ls_pcie_ep_of_match,
  .suppress_bind_attrs = true,
 },
};
builtin_platform_driver_probe(ls_pcie_ep_driver, ls_pcie_ep_probe);

Messung V0.5
C=92 H=93 G=92

¤ Dauer der Verarbeitung: 0.30 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.