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 33 kB image not shown  

Quelle  pci-keystone.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 * PCIe host controller driver for Texas Instruments Keystone SoCs
 *
 * Copyright (C) 2013-2014 Texas Instruments., Ltd.
 * https://www.ti.com
 *
 * Author: Murali Karicheri <m-karicheri2@ti.com>
 * Implementation based on pci-exynos.c and pcie-designware.c
 */


#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
#include <linux/mfd/syscon.h>
#include <linux/msi.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_pci.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/resource.h>
#include <linux/signal.h>

#include "../../pci.h"
#include "pcie-designware.h"

#define PCIE_VENDORID_MASK 0xffff
#define PCIE_DEVICEID_SHIFT 16

/* Application registers */
#define PID    0x000
#define RTL    GENMASK(15, 11)
#define RTL_SHIFT   11
#define AM6_PCI_PG1_RTL_VER  0x15

#define CMD_STATUS   0x004
#define LTSSM_EN_VAL          BIT(0)
#define OB_XLAT_EN_VAL          BIT(1)
#define DBI_CS2    BIT(5)

#define CFG_SETUP   0x008
#define CFG_BUS(x)   (((x) & 0xff) << 16)
#define CFG_DEVICE(x)   (((x) & 0x1f) << 8)
#define CFG_FUNC(x)   ((x) & 0x7)
#define CFG_TYPE1   BIT(24)

#define OB_SIZE    0x030
#define OB_OFFSET_INDEX(n)  (0x200 + (8 * (n)))
#define OB_OFFSET_HI(n)   (0x204 + (8 * (n)))
#define OB_ENABLEN   BIT(0)
#define OB_WIN_SIZE   8 /* 8MB */

#define PCIE_LEGACY_IRQ_ENABLE_SET(n) (0x188 + (0x10 * ((n) - 1)))
#define PCIE_LEGACY_IRQ_ENABLE_CLR(n) (0x18c + (0x10 * ((n) - 1)))
#define PCIE_EP_IRQ_SET   0x64
#define PCIE_EP_IRQ_CLR   0x68
#define INT_ENABLE   BIT(0)

/* IRQ register defines */
#define IRQ_EOI    0x050

#define MSI_IRQ    0x054
#define MSI_IRQ_STATUS(n)  (0x104 + ((n) << 4))
#define MSI_IRQ_ENABLE_SET(n)  (0x108 + ((n) << 4))
#define MSI_IRQ_ENABLE_CLR(n)  (0x10c + ((n) << 4))
#define MSI_IRQ_OFFSET   4

#define IRQ_STATUS(n)   (0x184 + ((n) << 4))
#define IRQ_ENABLE_SET(n)  (0x188 + ((n) << 4))
#define INTx_EN    BIT(0)

#define ERR_IRQ_STATUS   0x1c4
#define ERR_IRQ_ENABLE_SET  0x1c8
#define ERR_AER    BIT(5) /* ECRC error */
#define AM6_ERR_AER   BIT(4) /* AM6 ECRC error */
#define ERR_AXI    BIT(4) /* AXI tag lookup fatal error */
#define ERR_CORR   BIT(3) /* Correctable error */
#define ERR_NONFATAL   BIT(2) /* Non-fatal error */
#define ERR_FATAL   BIT(1) /* Fatal error */
#define ERR_SYS    BIT(0) /* System error */
#define ERR_IRQ_ALL   (ERR_AER | ERR_AXI | ERR_CORR | \
      ERR_NONFATAL | ERR_FATAL | ERR_SYS)

/* PCIE controller device IDs */
#define PCIE_RC_K2HK   0xb008
#define PCIE_RC_K2E   0xb009
#define PCIE_RC_K2L   0xb00a
#define PCIE_RC_K2G   0xb00b

#define KS_PCIE_DEV_TYPE_MASK  (0x3 << 1)
#define KS_PCIE_DEV_TYPE(mode)  ((mode) << 1)

#define EP    0x0
#define LEG_EP    0x1
#define RC    0x2

#define KS_PCIE_SYSCLOCKOUTEN  BIT(0)

#define AM654_PCIE_DEV_TYPE_MASK 0x3
#define AM654_WIN_SIZE   SZ_64K

#define APP_ADDR_SPACE_0  (16 * SZ_1K)

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

#define PCI_DEVICE_ID_TI_AM654X  0xb00c

struct ks_pcie_of_data {
 enum dw_pcie_device_mode mode;
 const struct dw_pcie_host_ops *host_ops;
 const struct dw_pcie_ep_ops *ep_ops;
 u32 version;
};

struct keystone_pcie {
 struct dw_pcie  *pci;
 /* PCI Device ID */
 u32   device_id;
 int   intx_host_irqs[PCI_NUM_INTX];

 int   msi_host_irq;
 int   num_lanes;
 u32   num_viewport;
 struct phy  **phy;
 struct device_link **link;
 struct   device_node *msi_intc_np;
 struct irq_domain *intx_irq_domain;
 struct device_node *np;

 /* Application register space */
 void __iomem  *va_app_base; /* DT 1st resource */
 struct resource  app;
 bool   is_am6;
};

static u32 ks_pcie_app_readl(struct keystone_pcie *ks_pcie, u32 offset)
{
 return readl(ks_pcie->va_app_base + offset);
}

static void ks_pcie_app_writel(struct keystone_pcie *ks_pcie, u32 offset,
          u32 val)
{
 writel(val, ks_pcie->va_app_base + offset);
}

static void ks_pcie_msi_irq_ack(struct irq_data *data)
{
 struct dw_pcie_rp *pp  = irq_data_get_irq_chip_data(data);
 struct keystone_pcie *ks_pcie;
 u32 irq = data->hwirq;
 struct dw_pcie *pci;
 u32 reg_offset;
 u32 bit_pos;

 pci = to_dw_pcie_from_pp(pp);
 ks_pcie = to_keystone_pcie(pci);

 reg_offset = irq % 8;
 bit_pos = irq >> 3;

 ks_pcie_app_writel(ks_pcie, MSI_IRQ_STATUS(reg_offset),
      BIT(bit_pos));
 ks_pcie_app_writel(ks_pcie, IRQ_EOI, reg_offset + MSI_IRQ_OFFSET);
}

static void ks_pcie_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
{
 struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(data);
 struct keystone_pcie *ks_pcie;
 struct dw_pcie *pci;
 u64 msi_target;

 pci = to_dw_pcie_from_pp(pp);
 ks_pcie = to_keystone_pcie(pci);

 msi_target = ks_pcie->app.start + MSI_IRQ;
 msg->address_lo = lower_32_bits(msi_target);
 msg->address_hi = upper_32_bits(msi_target);
 msg->data = data->hwirq;

 dev_dbg(pci->dev, "msi#%d address_hi %#x address_lo %#x\n",
  (int)data->hwirq, msg->address_hi, msg->address_lo);
}

static void ks_pcie_msi_mask(struct irq_data *data)
{
 struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(data);
 struct keystone_pcie *ks_pcie;
 u32 irq = data->hwirq;
 struct dw_pcie *pci;
 unsigned long flags;
 u32 reg_offset;
 u32 bit_pos;

 raw_spin_lock_irqsave(&pp->lock, flags);

 pci = to_dw_pcie_from_pp(pp);
 ks_pcie = to_keystone_pcie(pci);

 reg_offset = irq % 8;
 bit_pos = irq >> 3;

 ks_pcie_app_writel(ks_pcie, MSI_IRQ_ENABLE_CLR(reg_offset),
      BIT(bit_pos));

 raw_spin_unlock_irqrestore(&pp->lock, flags);
}

static void ks_pcie_msi_unmask(struct irq_data *data)
{
 struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(data);
 struct keystone_pcie *ks_pcie;
 u32 irq = data->hwirq;
 struct dw_pcie *pci;
 unsigned long flags;
 u32 reg_offset;
 u32 bit_pos;

 raw_spin_lock_irqsave(&pp->lock, flags);

 pci = to_dw_pcie_from_pp(pp);
 ks_pcie = to_keystone_pcie(pci);

 reg_offset = irq % 8;
 bit_pos = irq >> 3;

 ks_pcie_app_writel(ks_pcie, MSI_IRQ_ENABLE_SET(reg_offset),
      BIT(bit_pos));

 raw_spin_unlock_irqrestore(&pp->lock, flags);
}

static struct irq_chip ks_pcie_msi_irq_chip = {
 .name = "KEYSTONE-PCI-MSI",
 .irq_ack = ks_pcie_msi_irq_ack,
 .irq_compose_msi_msg = ks_pcie_compose_msi_msg,
 .irq_mask = ks_pcie_msi_mask,
 .irq_unmask = ks_pcie_msi_unmask,
};

/**
 * ks_pcie_set_dbi_mode() - Set DBI mode to access overlaid BAR mask registers
 * @ks_pcie: A pointer to the keystone_pcie structure which holds the KeyStone
 *      PCIe host controller driver information.
 *
 * Since modification of dbi_cs2 involves different clock domain, read the
 * status back to ensure the transition is complete.
 */

static void ks_pcie_set_dbi_mode(struct keystone_pcie *ks_pcie)
{
 u32 val;

 val = ks_pcie_app_readl(ks_pcie, CMD_STATUS);
 val |= DBI_CS2;
 ks_pcie_app_writel(ks_pcie, CMD_STATUS, val);

 do {
  val = ks_pcie_app_readl(ks_pcie, CMD_STATUS);
 } while (!(val & DBI_CS2));
}

/**
 * ks_pcie_clear_dbi_mode() - Disable DBI mode
 * @ks_pcie: A pointer to the keystone_pcie structure which holds the KeyStone
 *      PCIe host controller driver information.
 *
 * Since modification of dbi_cs2 involves different clock domain, read the
 * status back to ensure the transition is complete.
 */

static void ks_pcie_clear_dbi_mode(struct keystone_pcie *ks_pcie)
{
 u32 val;

 val = ks_pcie_app_readl(ks_pcie, CMD_STATUS);
 val &= ~DBI_CS2;
 ks_pcie_app_writel(ks_pcie, CMD_STATUS, val);

 do {
  val = ks_pcie_app_readl(ks_pcie, CMD_STATUS);
 } while (val & DBI_CS2);
}

static int ks_pcie_msi_host_init(struct dw_pcie_rp *pp)
{
 struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
 struct keystone_pcie *ks_pcie = to_keystone_pcie(pci);

 /* Configure and set up BAR0 */
 ks_pcie_set_dbi_mode(ks_pcie);

 /* Enable BAR0 */
 dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 1);
 dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, SZ_4K - 1);

 ks_pcie_clear_dbi_mode(ks_pcie);

 /*
 * For BAR0, just setting bus address for inbound writes (MSI) should
 * be sufficient.  Use physical address to avoid any conflicts.
 */

 dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, ks_pcie->app.start);

 pp->msi_irq_chip = &ks_pcie_msi_irq_chip;
 return dw_pcie_allocate_domains(pp);
}

static void ks_pcie_handle_intx_irq(struct keystone_pcie *ks_pcie,
        int offset)
{
 struct dw_pcie *pci = ks_pcie->pci;
 struct device *dev = pci->dev;
 u32 pending;

 pending = ks_pcie_app_readl(ks_pcie, IRQ_STATUS(offset));

 if (BIT(0) & pending) {
  dev_dbg(dev, ": irq: irq_offset %d", offset);
  generic_handle_domain_irq(ks_pcie->intx_irq_domain, offset);
 }

 /* EOI the INTx interrupt */
 ks_pcie_app_writel(ks_pcie, IRQ_EOI, offset);
}

static void ks_pcie_enable_error_irq(struct keystone_pcie *ks_pcie)
{
 ks_pcie_app_writel(ks_pcie, ERR_IRQ_ENABLE_SET, ERR_IRQ_ALL);
}

static irqreturn_t ks_pcie_handle_error_irq(struct keystone_pcie *ks_pcie)
{
 u32 reg;
 struct device *dev = ks_pcie->pci->dev;

 reg = ks_pcie_app_readl(ks_pcie, ERR_IRQ_STATUS);
 if (!reg)
  return IRQ_NONE;

 if (reg & ERR_SYS)
  dev_err(dev, "System Error\n");

 if (reg & ERR_FATAL)
  dev_err(dev, "Fatal Error\n");

 if (reg & ERR_NONFATAL)
  dev_dbg(dev, "Non Fatal Error\n");

 if (reg & ERR_CORR)
  dev_dbg(dev, "Correctable Error\n");

 if (!ks_pcie->is_am6 && (reg & ERR_AXI))
  dev_err(dev, "AXI tag lookup fatal Error\n");

 if (reg & ERR_AER || (ks_pcie->is_am6 && (reg & AM6_ERR_AER)))
  dev_err(dev, "ECRC Error\n");

 ks_pcie_app_writel(ks_pcie, ERR_IRQ_STATUS, reg);

 return IRQ_HANDLED;
}

static void ks_pcie_ack_intx_irq(struct irq_data *d)
{
}

static void ks_pcie_mask_intx_irq(struct irq_data *d)
{
}

static void ks_pcie_unmask_intx_irq(struct irq_data *d)
{
}

static struct irq_chip ks_pcie_intx_irq_chip = {
 .name = "Keystone-PCI-INTX-IRQ",
 .irq_ack = ks_pcie_ack_intx_irq,
 .irq_mask = ks_pcie_mask_intx_irq,
 .irq_unmask = ks_pcie_unmask_intx_irq,
};

static int ks_pcie_init_intx_irq_map(struct irq_domain *d,
         unsigned int irq, irq_hw_number_t hw_irq)
{
 irq_set_chip_and_handler(irq, &ks_pcie_intx_irq_chip,
     handle_level_irq);
 irq_set_chip_data(irq, d->host_data);

 return 0;
}

static const struct irq_domain_ops ks_pcie_intx_irq_domain_ops = {
 .map = ks_pcie_init_intx_irq_map,
 .xlate = irq_domain_xlate_onetwocell,
};

static int ks_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie)
{
 u32 val;
 u32 num_viewport = ks_pcie->num_viewport;
 struct dw_pcie *pci = ks_pcie->pci;
 struct dw_pcie_rp *pp = &pci->pp;
 struct resource_entry *entry;
 struct resource *mem;
 u64 start, end;
 int i;

 entry = resource_list_first_type(&pp->bridge->windows, IORESOURCE_MEM);
 if (!entry)
  return -ENODEV;

 mem = entry->res;
 start = mem->start;
 end = mem->end;

 /* Disable BARs for inbound access */
 ks_pcie_set_dbi_mode(ks_pcie);
 dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0);
 dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0);
 ks_pcie_clear_dbi_mode(ks_pcie);

 if (ks_pcie->is_am6)
  return 0;

 val = ilog2(OB_WIN_SIZE);
 ks_pcie_app_writel(ks_pcie, OB_SIZE, val);

 /* Using Direct 1:1 mapping of RC <-> PCI memory space */
 for (i = 0; i < num_viewport && (start < end); i++) {
  ks_pcie_app_writel(ks_pcie, OB_OFFSET_INDEX(i),
       lower_32_bits(start) | OB_ENABLEN);
  ks_pcie_app_writel(ks_pcie, OB_OFFSET_HI(i),
       upper_32_bits(start));
  start += OB_WIN_SIZE * SZ_1M;
 }

 val = ks_pcie_app_readl(ks_pcie, CMD_STATUS);
 val |= OB_XLAT_EN_VAL;
 ks_pcie_app_writel(ks_pcie, CMD_STATUS, val);

 return 0;
}

static void __iomem *ks_pcie_other_map_bus(struct pci_bus *bus,
        unsigned int devfn, int where)
{
 struct dw_pcie_rp *pp = bus->sysdata;
 struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
 struct keystone_pcie *ks_pcie = to_keystone_pcie(pci);
 u32 reg;

 /*
 * Checking whether the link is up here is a last line of defense
 * against platforms that forward errors on the system bus as
 * SError upon PCI configuration transactions issued when the link
 * is down. This check is racy by definition and does not stop
 * the system from triggering an SError if the link goes down
 * after this check is performed.
 */

 if (!dw_pcie_link_up(pci))
  return NULL;

 reg = CFG_BUS(bus->number) | CFG_DEVICE(PCI_SLOT(devfn)) |
  CFG_FUNC(PCI_FUNC(devfn));
 if (!pci_is_root_bus(bus->parent))
  reg |= CFG_TYPE1;
 ks_pcie_app_writel(ks_pcie, CFG_SETUP, reg);

 return pp->va_cfg0_base + where;
}

static struct pci_ops ks_child_pcie_ops = {
 .map_bus = ks_pcie_other_map_bus,
 .read = pci_generic_config_read,
 .write = pci_generic_config_write,
};

static struct pci_ops ks_pcie_ops = {
 .map_bus = dw_pcie_own_conf_map_bus,
 .read = pci_generic_config_read,
 .write = pci_generic_config_write,
};

/**
 * ks_pcie_link_up() - Check if link up
 * @pci: A pointer to the dw_pcie structure which holds the DesignWare PCIe host
 *  controller driver information.
 */

static bool ks_pcie_link_up(struct dw_pcie *pci)
{
 u32 val;

 val = dw_pcie_readl_dbi(pci, PCIE_PORT_DEBUG0);
 return (val & PORT_LOGIC_LTSSM_STATE_MASK) == PORT_LOGIC_LTSSM_STATE_L0;
}

static void ks_pcie_stop_link(struct dw_pcie *pci)
{
 struct keystone_pcie *ks_pcie = to_keystone_pcie(pci);
 u32 val;

 /* Disable Link training */
 val = ks_pcie_app_readl(ks_pcie, CMD_STATUS);
 val &= ~LTSSM_EN_VAL;
 ks_pcie_app_writel(ks_pcie, CMD_STATUS, val);
}

static int ks_pcie_start_link(struct dw_pcie *pci)
{
 struct keystone_pcie *ks_pcie = to_keystone_pcie(pci);
 u32 val;

 /* Initiate Link Training */
 val = ks_pcie_app_readl(ks_pcie, CMD_STATUS);
 ks_pcie_app_writel(ks_pcie, CMD_STATUS, LTSSM_EN_VAL | val);

 return 0;
}

static void ks_pcie_quirk(struct pci_dev *dev)
{
 struct pci_bus *bus = dev->bus;
 struct keystone_pcie *ks_pcie;
 struct device *bridge_dev;
 struct pci_dev *bridge;
 u32 val;

 static const struct pci_device_id rc_pci_devids[] = {
  { PCI_DEVICE(PCI_VENDOR_ID_TI, PCIE_RC_K2HK),
   .class = PCI_CLASS_BRIDGE_PCI_NORMAL, .class_mask = ~0, },
  { PCI_DEVICE(PCI_VENDOR_ID_TI, PCIE_RC_K2E),
   .class = PCI_CLASS_BRIDGE_PCI_NORMAL, .class_mask = ~0, },
  { PCI_DEVICE(PCI_VENDOR_ID_TI, PCIE_RC_K2L),
   .class = PCI_CLASS_BRIDGE_PCI_NORMAL, .class_mask = ~0, },
  { PCI_DEVICE(PCI_VENDOR_ID_TI, PCIE_RC_K2G),
   .class = PCI_CLASS_BRIDGE_PCI_NORMAL, .class_mask = ~0, },
  { 0, },
 };
 static const struct pci_device_id am6_pci_devids[] = {
  { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_AM654X),
   .class = PCI_CLASS_BRIDGE_PCI << 8, .class_mask = ~0, },
  { 0, },
 };

 if (pci_is_root_bus(bus))
  bridge = dev;

 /* look for the host bridge */
 while (!pci_is_root_bus(bus)) {
  bridge = bus->self;
  bus = bus->parent;
 }

 if (!bridge)
  return;

 /*
 * Keystone PCI controller has a h/w limitation of
 * 256 bytes maximum read request size.  It can't handle
 * anything higher than this.  So force this limit on
 * all downstream devices.
 */

 if (pci_match_id(rc_pci_devids, bridge)) {
  if (pcie_get_readrq(dev) > 256) {
   dev_info(&dev->dev, "limiting MRRS to 256 bytes\n");
   pcie_set_readrq(dev, 256);
  }
 }

 /*
 * Memory transactions fail with PCI controller in AM654 PG1.0
 * when MRRS is set to more than 128 bytes. Force the MRRS to
 * 128 bytes in all downstream devices.
 */

 if (pci_match_id(am6_pci_devids, bridge)) {
  bridge_dev = pci_get_host_bridge_device(dev);
  if (!bridge_dev || !bridge_dev->parent)
   return;

  ks_pcie = dev_get_drvdata(bridge_dev->parent);
  if (!ks_pcie)
   return;

  val = ks_pcie_app_readl(ks_pcie, PID);
  val &= RTL;
  val >>= RTL_SHIFT;
  if (val != AM6_PCI_PG1_RTL_VER)
   return;

  if (pcie_get_readrq(dev) > 128) {
   dev_info(&dev->dev, "limiting MRRS to 128 bytes\n");
   pcie_set_readrq(dev, 128);
  }
 }
}
DECLARE_PCI_FIXUP_ENABLE(PCI_ANY_ID, PCI_ANY_ID, ks_pcie_quirk);

static void ks_pcie_msi_irq_handler(struct irq_desc *desc)
{
 unsigned int irq = desc->irq_data.hwirq;
 struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc);
 u32 offset = irq - ks_pcie->msi_host_irq;
 struct dw_pcie *pci = ks_pcie->pci;
 struct dw_pcie_rp *pp = &pci->pp;
 struct device *dev = pci->dev;
 struct irq_chip *chip = irq_desc_get_chip(desc);
 u32 vector, reg, pos;

 dev_dbg(dev, "%s, irq %d\n", __func__, 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);

 reg = ks_pcie_app_readl(ks_pcie, MSI_IRQ_STATUS(offset));
 /*
 * MSI0 status bit 0-3 shows vectors 0, 8, 16, 24, MSI1 status bit
 * shows 1, 9, 17, 25 and so forth
 */

 for (pos = 0; pos < 4; pos++) {
  if (!(reg & BIT(pos)))
   continue;

  vector = offset + (pos << 3);
  dev_dbg(dev, "irq: bit %d, vector %d\n", pos, vector);
  generic_handle_domain_irq(pp->irq_domain, vector);
 }

 chained_irq_exit(chip, desc);
}

/**
 * ks_pcie_intx_irq_handler() - Handle INTX interrupt
 * @desc: Pointer to irq descriptor
 *
 * Traverse through pending INTX interrupts and invoke handler for each. Also
 * takes care of interrupt controller level mask/ack operation.
 */

static void ks_pcie_intx_irq_handler(struct irq_desc *desc)
{
 unsigned int irq = irq_desc_get_irq(desc);
 struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc);
 struct dw_pcie *pci = ks_pcie->pci;
 struct device *dev = pci->dev;
 u32 irq_offset = irq - ks_pcie->intx_host_irqs[0];
 struct irq_chip *chip = irq_desc_get_chip(desc);

 dev_dbg(dev, ": Handling INTX 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);
 ks_pcie_handle_intx_irq(ks_pcie, irq_offset);
 chained_irq_exit(chip, desc);
}

static int ks_pcie_config_msi_irq(struct keystone_pcie *ks_pcie)
{
 struct device *dev = ks_pcie->pci->dev;
 struct device_node *np = ks_pcie->np;
 struct device_node *intc_np;
 struct irq_data *irq_data;
 int irq_count, irq, ret, i;

 if (!IS_ENABLED(CONFIG_PCI_MSI))
  return 0;

 intc_np = of_get_child_by_name(np, "msi-interrupt-controller");
 if (!intc_np) {
  if (ks_pcie->is_am6)
   return 0;
  dev_warn(dev, "msi-interrupt-controller node is absent\n");
  return -EINVAL;
 }

 irq_count = of_irq_count(intc_np);
 if (!irq_count) {
  dev_err(dev, "No IRQ entries in msi-interrupt-controller\n");
  ret = -EINVAL;
  goto err;
 }

 for (i = 0; i < irq_count; i++) {
  irq = irq_of_parse_and_map(intc_np, i);
  if (!irq) {
   ret = -EINVAL;
   goto err;
  }

  if (!ks_pcie->msi_host_irq) {
   irq_data = irq_get_irq_data(irq);
   if (!irq_data) {
    ret = -EINVAL;
    goto err;
   }
   ks_pcie->msi_host_irq = irq_data->hwirq;
  }

  irq_set_chained_handler_and_data(irq, ks_pcie_msi_irq_handler,
       ks_pcie);
 }

 of_node_put(intc_np);
 return 0;

err:
 of_node_put(intc_np);
 return ret;
}

static int ks_pcie_config_intx_irq(struct keystone_pcie *ks_pcie)
{
 struct device *dev = ks_pcie->pci->dev;
 struct irq_domain *intx_irq_domain;
 struct device_node *np = ks_pcie->np;
 struct device_node *intc_np;
 int irq_count, irq, ret = 0, i;

 intc_np = of_get_child_by_name(np, "legacy-interrupt-controller");
 if (!intc_np) {
  /*
 * Since INTX interrupts are modeled as edge-interrupts in
 * AM6, keep it disabled for now.
 */

  if (ks_pcie->is_am6)
   return 0;
  dev_warn(dev, "legacy-interrupt-controller node is absent\n");
  return -EINVAL;
 }

 irq_count = of_irq_count(intc_np);
 if (!irq_count) {
  dev_err(dev, "No IRQ entries in legacy-interrupt-controller\n");
  ret = -EINVAL;
  goto err;
 }

 for (i = 0; i < irq_count; i++) {
  irq = irq_of_parse_and_map(intc_np, i);
  if (!irq) {
   ret = -EINVAL;
   goto err;
  }
  ks_pcie->intx_host_irqs[i] = irq;

  irq_set_chained_handler_and_data(irq,
       ks_pcie_intx_irq_handler,
       ks_pcie);
 }

 intx_irq_domain = irq_domain_create_linear(of_fwnode_handle(intc_np), PCI_NUM_INTX,
     &ks_pcie_intx_irq_domain_ops, NULL);
 if (!intx_irq_domain) {
  dev_err(dev, "Failed to add irq domain for INTX irqs\n");
  ret = -EINVAL;
  goto err;
 }
 ks_pcie->intx_irq_domain = intx_irq_domain;

 for (i = 0; i < PCI_NUM_INTX; i++)
  ks_pcie_app_writel(ks_pcie, IRQ_ENABLE_SET(i), INTx_EN);

err:
 of_node_put(intc_np);
 return ret;
}

#ifdef CONFIG_ARM
/*
 * When a PCI device does not exist during config cycles, keystone host
 * gets a bus error instead of returning 0xffffffff (PCI_ERROR_RESPONSE).
 * This handler always returns 0 for this kind of fault.
 */

static int ks_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;
}
#endif

static int __init ks_pcie_init_id(struct keystone_pcie *ks_pcie)
{
 int ret;
 unsigned int id;
 struct regmap *devctrl_regs;
 struct dw_pcie *pci = ks_pcie->pci;
 struct device *dev = pci->dev;
 struct device_node *np = dev->of_node;
 struct of_phandle_args args;
 unsigned int offset = 0;

 devctrl_regs = syscon_regmap_lookup_by_phandle(np, "ti,syscon-pcie-id");
 if (IS_ERR(devctrl_regs))
  return PTR_ERR(devctrl_regs);

 /* Do not error out to maintain old DT compatibility */
 ret = of_parse_phandle_with_fixed_args(np, "ti,syscon-pcie-id", 1, 0, &args);
 if (!ret)
  offset = args.args[0];

 ret = regmap_read(devctrl_regs, offset, &id);
 if (ret)
  return ret;

 dw_pcie_dbi_ro_wr_en(pci);
 dw_pcie_writew_dbi(pci, PCI_VENDOR_ID, id & PCIE_VENDORID_MASK);
 dw_pcie_writew_dbi(pci, PCI_DEVICE_ID, id >> PCIE_DEVICEID_SHIFT);
 dw_pcie_dbi_ro_wr_dis(pci);

 return 0;
}

static int __init ks_pcie_host_init(struct dw_pcie_rp *pp)
{
 struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
 struct keystone_pcie *ks_pcie = to_keystone_pcie(pci);
 int ret;

 pp->bridge->ops = &ks_pcie_ops;
 if (!ks_pcie->is_am6)
  pp->bridge->child_ops = &ks_child_pcie_ops;

 ret = ks_pcie_config_intx_irq(ks_pcie);
 if (ret)
  return ret;

 ret = ks_pcie_config_msi_irq(ks_pcie);
 if (ret)
  return ret;

 ks_pcie_stop_link(pci);
 ret = ks_pcie_setup_rc_app_regs(ks_pcie);
 if (ret)
  return ret;

 writew(PCI_IO_RANGE_TYPE_32 | (PCI_IO_RANGE_TYPE_32 << 8),
   pci->dbi_base + PCI_IO_BASE);

 ret = ks_pcie_init_id(ks_pcie);
 if (ret < 0)
  return ret;

#ifdef CONFIG_ARM
 /*
 * PCIe access errors that result into OCP errors are caught by ARM as
 * "External aborts"
 */

 hook_fault_code(17, ks_pcie_fault, SIGBUS, 0,
   "Asynchronous external abort");
#endif

 return 0;
}

static const struct dw_pcie_host_ops ks_pcie_host_ops = {
 .init = ks_pcie_host_init,
 .msi_init = ks_pcie_msi_host_init,
};

static const struct dw_pcie_host_ops ks_pcie_am654_host_ops = {
 .init = ks_pcie_host_init,
};

static irqreturn_t ks_pcie_err_irq_handler(int irq, void *priv)
{
 struct keystone_pcie *ks_pcie = priv;

 return ks_pcie_handle_error_irq(ks_pcie);
}

static void ks_pcie_am654_write_dbi2(struct dw_pcie *pci, void __iomem *base,
         u32 reg, size_t size, u32 val)
{
 struct keystone_pcie *ks_pcie = to_keystone_pcie(pci);

 ks_pcie_set_dbi_mode(ks_pcie);
 dw_pcie_write(base + reg, size, val);
 ks_pcie_clear_dbi_mode(ks_pcie);
}

static const struct dw_pcie_ops ks_pcie_dw_pcie_ops = {
 .start_link = ks_pcie_start_link,
 .stop_link = ks_pcie_stop_link,
 .link_up = ks_pcie_link_up,
 .write_dbi2 = ks_pcie_am654_write_dbi2,
};

static void ks_pcie_am654_ep_init(struct dw_pcie_ep *ep)
{
 struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
 int flags;

 ep->page_size = AM654_WIN_SIZE;
 flags = PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_32;
 dw_pcie_writel_dbi2(pci, PCI_BASE_ADDRESS_0, APP_ADDR_SPACE_0 - 1);
 dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, flags);
}

static void ks_pcie_am654_raise_intx_irq(struct keystone_pcie *ks_pcie)
{
 struct dw_pcie *pci = ks_pcie->pci;
 u8 int_pin;

 int_pin = dw_pcie_readb_dbi(pci, PCI_INTERRUPT_PIN);
 if (int_pin == 0 || int_pin > 4)
  return;

 ks_pcie_app_writel(ks_pcie, PCIE_LEGACY_IRQ_ENABLE_SET(int_pin),
      INT_ENABLE);
 ks_pcie_app_writel(ks_pcie, PCIE_EP_IRQ_SET, INT_ENABLE);
 mdelay(1);
 ks_pcie_app_writel(ks_pcie, PCIE_EP_IRQ_CLR, INT_ENABLE);
 ks_pcie_app_writel(ks_pcie, PCIE_LEGACY_IRQ_ENABLE_CLR(int_pin),
      INT_ENABLE);
}

static int ks_pcie_am654_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);
 struct keystone_pcie *ks_pcie = to_keystone_pcie(pci);

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

 return 0;
}

static const struct pci_epc_features ks_pcie_am654_epc_features = {
 .linkup_notifier = false,
 .msi_capable = true,
 .msix_capable = true,
 .bar[BAR_0] = { .type = BAR_RESERVED, },
 .bar[BAR_1] = { .type = BAR_RESERVED, },
 .bar[BAR_2] = { .type = BAR_RESIZABLE, },
 .bar[BAR_3] = { .type = BAR_FIXED, .fixed_size = SZ_64K, },
 .bar[BAR_4] = { .type = BAR_FIXED, .fixed_size = 256, },
 .bar[BAR_5] = { .type = BAR_RESIZABLE, },
 .align = SZ_64K,
};

static const struct pci_epc_features*
ks_pcie_am654_get_features(struct dw_pcie_ep *ep)
{
 return &ks_pcie_am654_epc_features;
}

static const struct dw_pcie_ep_ops ks_pcie_am654_ep_ops = {
 .init = ks_pcie_am654_ep_init,
 .raise_irq = ks_pcie_am654_raise_irq,
 .get_features = &ks_pcie_am654_get_features,
};

static void ks_pcie_disable_phy(struct keystone_pcie *ks_pcie)
{
 int num_lanes = ks_pcie->num_lanes;

 while (num_lanes--) {
  phy_power_off(ks_pcie->phy[num_lanes]);
  phy_exit(ks_pcie->phy[num_lanes]);
 }
}

static int ks_pcie_enable_phy(struct keystone_pcie *ks_pcie)
{
 int i;
 int ret;
 int num_lanes = ks_pcie->num_lanes;

 for (i = 0; i < num_lanes; i++) {
  ret = phy_reset(ks_pcie->phy[i]);
  if (ret < 0)
   goto err_phy;

  ret = phy_init(ks_pcie->phy[i]);
  if (ret < 0)
   goto err_phy;

  ret = phy_power_on(ks_pcie->phy[i]);
  if (ret < 0) {
   phy_exit(ks_pcie->phy[i]);
   goto err_phy;
  }
 }

 return 0;

err_phy:
 while (--i >= 0) {
  phy_power_off(ks_pcie->phy[i]);
  phy_exit(ks_pcie->phy[i]);
 }

 return ret;
}

static int ks_pcie_set_mode(struct device *dev)
{
 struct device_node *np = dev->of_node;
 struct of_phandle_args args;
 unsigned int offset = 0;
 struct regmap *syscon;
 u32 val;
 u32 mask;
 int ret = 0;

 syscon = syscon_regmap_lookup_by_phandle(np, "ti,syscon-pcie-mode");
 if (IS_ERR(syscon))
  return 0;

 /* Do not error out to maintain old DT compatibility */
 ret = of_parse_phandle_with_fixed_args(np, "ti,syscon-pcie-mode", 1, 0, &args);
 if (!ret)
  offset = args.args[0];

 mask = KS_PCIE_DEV_TYPE_MASK | KS_PCIE_SYSCLOCKOUTEN;
 val = KS_PCIE_DEV_TYPE(RC) | KS_PCIE_SYSCLOCKOUTEN;

 ret = regmap_update_bits(syscon, offset, mask, val);
 if (ret) {
  dev_err(dev, "failed to set pcie mode\n");
  return ret;
 }

 return 0;
}

static int ks_pcie_am654_set_mode(struct device *dev,
      enum dw_pcie_device_mode mode)
{
 struct device_node *np = dev->of_node;
 struct of_phandle_args args;
 unsigned int offset = 0;
 struct regmap *syscon;
 u32 val;
 u32 mask;
 int ret = 0;

 syscon = syscon_regmap_lookup_by_phandle(np, "ti,syscon-pcie-mode");
 if (IS_ERR(syscon))
  return 0;

 /* Do not error out to maintain old DT compatibility */
 ret = of_parse_phandle_with_fixed_args(np, "ti,syscon-pcie-mode", 1, 0, &args);
 if (!ret)
  offset = args.args[0];

 mask = AM654_PCIE_DEV_TYPE_MASK;

 switch (mode) {
 case DW_PCIE_RC_TYPE:
  val = RC;
  break;
 case DW_PCIE_EP_TYPE:
  val = EP;
  break;
 default:
  dev_err(dev, "INVALID device type %d\n", mode);
  return -EINVAL;
 }

 ret = regmap_update_bits(syscon, offset, mask, val);
 if (ret) {
  dev_err(dev, "failed to set pcie mode\n");
  return ret;
 }

 return 0;
}

static const struct ks_pcie_of_data ks_pcie_rc_of_data = {
 .host_ops = &ks_pcie_host_ops,
 .mode = DW_PCIE_RC_TYPE,
 .version = DW_PCIE_VER_365A,
};

static const struct ks_pcie_of_data ks_pcie_am654_rc_of_data = {
 .host_ops = &ks_pcie_am654_host_ops,
 .mode = DW_PCIE_RC_TYPE,
 .version = DW_PCIE_VER_490A,
};

static const struct ks_pcie_of_data ks_pcie_am654_ep_of_data = {
 .ep_ops = &ks_pcie_am654_ep_ops,
 .mode = DW_PCIE_EP_TYPE,
 .version = DW_PCIE_VER_490A,
};

static const struct of_device_id ks_pcie_of_match[] = {
 {
  .type = "pci",
  .data = &ks_pcie_rc_of_data,
  .compatible = "ti,keystone-pcie",
 },
 {
  .data = &ks_pcie_am654_rc_of_data,
  .compatible = "ti,am654-pcie-rc",
 },
 {
  .data = &ks_pcie_am654_ep_of_data,
  .compatible = "ti,am654-pcie-ep",
 },
 { },
};

static int ks_pcie_probe(struct platform_device *pdev)
{
 const struct dw_pcie_host_ops *host_ops;
 const struct dw_pcie_ep_ops *ep_ops;
 struct device *dev = &pdev->dev;
 struct device_node *np = dev->of_node;
 const struct ks_pcie_of_data *data;
 enum dw_pcie_device_mode mode;
 struct dw_pcie *pci;
 struct keystone_pcie *ks_pcie;
 struct device_link **link;
 struct gpio_desc *gpiod;
 struct resource *res;
 void __iomem *base;
 u32 num_viewport;
 struct phy **phy;
 u32 num_lanes;
 char name[10];
 u32 version;
 int ret;
 int irq;
 int i;

 data = of_device_get_match_data(dev);
 if (!data)
  return -EINVAL;

 version = data->version;
 host_ops = data->host_ops;
 ep_ops = data->ep_ops;
 mode = data->mode;

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

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

 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "app");
 ks_pcie->va_app_base = devm_ioremap_resource(dev, res);
 if (IS_ERR(ks_pcie->va_app_base))
  return PTR_ERR(ks_pcie->va_app_base);

 ks_pcie->app = *res;

 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbics");
 base = devm_pci_remap_cfg_resource(dev, res);
 if (IS_ERR(base))
  return PTR_ERR(base);

 if (of_device_is_compatible(np, "ti,am654-pcie-rc"))
  ks_pcie->is_am6 = true;

 pci->dbi_base = base;
 pci->dbi_base2 = base;
 pci->dev = dev;
 pci->ops = &ks_pcie_dw_pcie_ops;
 pci->version = version;

 irq = platform_get_irq(pdev, 0);
 if (irq < 0)
  return irq;

 ret = devm_request_irq(dev, irq, ks_pcie_err_irq_handler, IRQF_SHARED,
          "ks-pcie-error-irq", ks_pcie);
 if (ret < 0) {
  dev_err(dev, "failed to request error IRQ %d\n",
   irq);
  return ret;
 }

 ret = of_property_read_u32(np, "num-lanes", &num_lanes);
 if (ret)
  num_lanes = 1;

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

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

 for (i = 0; i < num_lanes; i++) {
  snprintf(name, sizeof(name), "pcie-phy%d", i);
  phy[i] = devm_phy_optional_get(dev, name);
  if (IS_ERR(phy[i])) {
   ret = PTR_ERR(phy[i]);
   goto err_link;
  }

  if (!phy[i])
   continue;

  link[i] = device_link_add(dev, &phy[i]->dev, DL_FLAG_STATELESS);
  if (!link[i]) {
   ret = -EINVAL;
   goto err_link;
  }
 }

 ks_pcie->np = np;
 ks_pcie->pci = pci;
 ks_pcie->link = link;
 ks_pcie->num_lanes = num_lanes;
 ks_pcie->phy = phy;

 gpiod = devm_gpiod_get_optional(dev, "reset",
     GPIOD_OUT_LOW);
 if (IS_ERR(gpiod)) {
  ret = PTR_ERR(gpiod);
  if (ret != -EPROBE_DEFER)
   dev_err(dev, "Failed to get reset GPIO\n");
  goto err_link;
 }

 /* Obtain references to the PHYs */
 for (i = 0; i < num_lanes; i++)
  phy_pm_runtime_get_sync(ks_pcie->phy[i]);

 ret = ks_pcie_enable_phy(ks_pcie);

 /* Release references to the PHYs */
 for (i = 0; i < num_lanes; i++)
  phy_pm_runtime_put_sync(ks_pcie->phy[i]);

 if (ret) {
  dev_err(dev, "failed to enable phy\n");
  goto err_link;
 }

 platform_set_drvdata(pdev, ks_pcie);
 pm_runtime_enable(dev);
 ret = pm_runtime_get_sync(dev);
 if (ret < 0) {
  dev_err(dev, "pm_runtime_get_sync failed\n");
  goto err_get_sync;
 }

 if (dw_pcie_ver_is_ge(pci, 480A))
  ret = ks_pcie_am654_set_mode(dev, mode);
 else
  ret = ks_pcie_set_mode(dev);
 if (ret < 0)
  goto err_get_sync;

 switch (mode) {
 case DW_PCIE_RC_TYPE:
  if (!IS_ENABLED(CONFIG_PCI_KEYSTONE_HOST)) {
   ret = -ENODEV;
   goto err_get_sync;
  }

  ret = of_property_read_u32(np, "num-viewport", &num_viewport);
  if (ret < 0) {
   dev_err(dev, "unable to read *num-viewport* property\n");
   goto err_get_sync;
  }

  /*
 * "Power Sequencing and Reset Signal Timings" table in
 * PCI EXPRESS CARD ELECTROMECHANICAL SPECIFICATION, REV. 2.0
 * indicates PERST# should be deasserted after minimum of 100us
 * once REFCLK is stable. The REFCLK to the connector in RC
 * mode is selected while enabling the PHY. So deassert PERST#
 * after 100 us.
 */

  if (gpiod) {
   usleep_range(100, 200);
   gpiod_set_value_cansleep(gpiod, 1);
  }

  ks_pcie->num_viewport = num_viewport;
  pci->pp.ops = host_ops;
  ret = dw_pcie_host_init(&pci->pp);
  if (ret < 0)
   goto err_get_sync;
  break;
 case DW_PCIE_EP_TYPE:
  if (!IS_ENABLED(CONFIG_PCI_KEYSTONE_EP)) {
   ret = -ENODEV;
   goto err_get_sync;
  }

  pci->ep.ops = ep_ops;
  ret = dw_pcie_ep_init(&pci->ep);
  if (ret < 0)
   goto err_get_sync;

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

  pci_epc_init_notify(pci->ep.epc);

  break;
 default:
  dev_err(dev, "INVALID device type %d\n", mode);
 }

 ks_pcie_enable_error_irq(ks_pcie);

 return 0;

err_ep_init:
 dw_pcie_ep_deinit(&pci->ep);
err_get_sync:
 pm_runtime_put(dev);
 pm_runtime_disable(dev);
 ks_pcie_disable_phy(ks_pcie);

err_link:
 while (--i >= 0 && link[i])
  device_link_del(link[i]);

 return ret;
}

static void ks_pcie_remove(struct platform_device *pdev)
{
 struct keystone_pcie *ks_pcie = platform_get_drvdata(pdev);
 struct device_link **link = ks_pcie->link;
 int num_lanes = ks_pcie->num_lanes;
 struct device *dev = &pdev->dev;

 pm_runtime_put(dev);
 pm_runtime_disable(dev);
 ks_pcie_disable_phy(ks_pcie);
 while (num_lanes--)
  device_link_del(link[num_lanes]);
}

static struct platform_driver ks_pcie_driver = {
 .probe  = ks_pcie_probe,
 .remove = ks_pcie_remove,
 .driver = {
  .name = "keystone-pcie",
  .of_match_table = ks_pcie_of_match,
 },
};
builtin_platform_driver(ks_pcie_driver);

Messung V0.5
C=97 H=89 G=93

¤ Dauer der Verarbeitung: 0.5 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.