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

Quelle  pcie-plda-host.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 * PLDA PCIe XpressRich host controller driver
 *
 * Copyright (C) 2023 Microchip Co. Ltd
 *       StarFive Co. Ltd
 *
 * Author: Daire McNamara <daire.mcnamara@microchip.com>
 */


#include <linux/align.h>
#include <linux/bitfield.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqchip/irq-msi-lib.h>
#include <linux/irqdomain.h>
#include <linux/msi.h>
#include <linux/pci_regs.h>
#include <linux/pci-ecam.h>
#include <linux/wordpart.h>

#include "pcie-plda.h"

void __iomem *plda_pcie_map_bus(struct pci_bus *bus, unsigned int devfn,
    int where)
{
 struct plda_pcie_rp *pcie = bus->sysdata;

 return pcie->config_base + PCIE_ECAM_OFFSET(bus->number, devfn, where);
}
EXPORT_SYMBOL_GPL(plda_pcie_map_bus);

static void plda_handle_msi(struct irq_desc *desc)
{
 struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
 struct irq_chip *chip = irq_desc_get_chip(desc);
 struct device *dev = port->dev;
 struct plda_msi *msi = &port->msi;
 void __iomem *bridge_base_addr = port->bridge_addr;
 unsigned long status;
 u32 bit;
 int ret;

 chained_irq_enter(chip, desc);

 status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
 if (status & PM_MSI_INT_MSI_MASK) {
  writel_relaxed(status & PM_MSI_INT_MSI_MASK,
          bridge_base_addr + ISTATUS_LOCAL);
  status = readl_relaxed(bridge_base_addr + ISTATUS_MSI);
  for_each_set_bit(bit, &status, msi->num_vectors) {
   ret = generic_handle_domain_irq(msi->dev_domain, bit);
   if (ret)
    dev_err_ratelimited(dev, "bad MSI IRQ %d\n",
          bit);
  }
 }

 chained_irq_exit(chip, desc);
}

static void plda_msi_bottom_irq_ack(struct irq_data *data)
{
 struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
 void __iomem *bridge_base_addr = port->bridge_addr;
 u32 bitpos = data->hwirq;

 writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI);
}

static void plda_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
{
 struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
 phys_addr_t addr = port->msi.vector_phy;

 msg->address_lo = lower_32_bits(addr);
 msg->address_hi = upper_32_bits(addr);
 msg->data = data->hwirq;

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

static struct irq_chip plda_msi_bottom_irq_chip = {
 .name = "PLDA MSI",
 .irq_ack = plda_msi_bottom_irq_ack,
 .irq_compose_msi_msg = plda_compose_msi_msg,
};

static int plda_irq_msi_domain_alloc(struct irq_domain *domain,
         unsigned int virq,
         unsigned int nr_irqs,
         void *args)
{
 struct plda_pcie_rp *port = domain->host_data;
 struct plda_msi *msi = &port->msi;
 unsigned long bit;

 mutex_lock(&msi->lock);
 bit = find_first_zero_bit(msi->used, msi->num_vectors);
 if (bit >= msi->num_vectors) {
  mutex_unlock(&msi->lock);
  return -ENOSPC;
 }

 set_bit(bit, msi->used);

 irq_domain_set_info(domain, virq, bit, &plda_msi_bottom_irq_chip,
       domain->host_data, handle_edge_irq, NULL, NULL);

 mutex_unlock(&msi->lock);

 return 0;
}

static void plda_irq_msi_domain_free(struct irq_domain *domain,
         unsigned int virq,
         unsigned int nr_irqs)
{
 struct irq_data *d = irq_domain_get_irq_data(domain, virq);
 struct plda_pcie_rp *port = irq_data_get_irq_chip_data(d);
 struct plda_msi *msi = &port->msi;

 mutex_lock(&msi->lock);

 if (test_bit(d->hwirq, msi->used))
  __clear_bit(d->hwirq, msi->used);
 else
  dev_err(port->dev, "trying to free unused MSI%lu\n", d->hwirq);

 mutex_unlock(&msi->lock);
}

static const struct irq_domain_ops msi_domain_ops = {
 .alloc = plda_irq_msi_domain_alloc,
 .free = plda_irq_msi_domain_free,
};

#define PLDA_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \
     MSI_FLAG_USE_DEF_CHIP_OPS | \
     MSI_FLAG_NO_AFFINITY)
#define PLDA_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \
      MSI_FLAG_PCI_MSIX)

static const struct msi_parent_ops plda_msi_parent_ops = {
 .required_flags  = PLDA_MSI_FLAGS_REQUIRED,
 .supported_flags = PLDA_MSI_FLAGS_SUPPORTED,
 .chip_flags  = MSI_CHIP_FLAG_SET_ACK,
 .bus_select_token = DOMAIN_BUS_PCI_MSI,
 .prefix   = "PLDA-",
 .init_dev_msi_info = msi_lib_init_dev_msi_info,
};

static int plda_allocate_msi_domains(struct plda_pcie_rp *port)
{
 struct device *dev = port->dev;
 struct plda_msi *msi = &port->msi;

 mutex_init(&port->msi.lock);

 struct irq_domain_info info = {
  .fwnode  = dev_fwnode(dev),
  .ops  = &msi_domain_ops,
  .host_data = port,
  .size  = msi->num_vectors,
 };

 msi->dev_domain = msi_create_parent_irq_domain(&info, &plda_msi_parent_ops);
 if (!msi->dev_domain) {
  dev_err(dev, "failed to create IRQ domain\n");
  return -ENOMEM;
 }

 return 0;
}

static void plda_handle_intx(struct irq_desc *desc)
{
 struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
 struct irq_chip *chip = irq_desc_get_chip(desc);
 struct device *dev = port->dev;
 void __iomem *bridge_base_addr = port->bridge_addr;
 unsigned long status;
 u32 bit;
 int ret;

 chained_irq_enter(chip, desc);

 status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
 if (status & PM_MSI_INT_INTX_MASK) {
  status &= PM_MSI_INT_INTX_MASK;
  status >>= PM_MSI_INT_INTX_SHIFT;
  for_each_set_bit(bit, &status, PCI_NUM_INTX) {
   ret = generic_handle_domain_irq(port->intx_domain, bit);
   if (ret)
    dev_err_ratelimited(dev, "bad INTx IRQ %d\n",
          bit);
  }
 }

 chained_irq_exit(chip, desc);
}

static void plda_ack_intx_irq(struct irq_data *data)
{
 struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
 void __iomem *bridge_base_addr = port->bridge_addr;
 u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);

 writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL);
}

static void plda_mask_intx_irq(struct irq_data *data)
{
 struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
 void __iomem *bridge_base_addr = port->bridge_addr;
 unsigned long flags;
 u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
 u32 val;

 raw_spin_lock_irqsave(&port->lock, flags);
 val = readl_relaxed(bridge_base_addr + IMASK_LOCAL);
 val &= ~mask;
 writel_relaxed(val, bridge_base_addr + IMASK_LOCAL);
 raw_spin_unlock_irqrestore(&port->lock, flags);
}

static void plda_unmask_intx_irq(struct irq_data *data)
{
 struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
 void __iomem *bridge_base_addr = port->bridge_addr;
 unsigned long flags;
 u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT);
 u32 val;

 raw_spin_lock_irqsave(&port->lock, flags);
 val = readl_relaxed(bridge_base_addr + IMASK_LOCAL);
 val |= mask;
 writel_relaxed(val, bridge_base_addr + IMASK_LOCAL);
 raw_spin_unlock_irqrestore(&port->lock, flags);
}

static struct irq_chip plda_intx_irq_chip = {
 .name = "PLDA PCIe INTx",
 .irq_ack = plda_ack_intx_irq,
 .irq_mask = plda_mask_intx_irq,
 .irq_unmask = plda_unmask_intx_irq,
};

static int plda_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
         irq_hw_number_t hwirq)
{
 irq_set_chip_and_handler(irq, &plda_intx_irq_chip, handle_level_irq);
 irq_set_chip_data(irq, domain->host_data);

 return 0;
}

static const struct irq_domain_ops intx_domain_ops = {
 .map = plda_pcie_intx_map,
};

static u32 plda_get_events(struct plda_pcie_rp *port)
{
 u32 events, val, origin;

 origin = readl_relaxed(port->bridge_addr + ISTATUS_LOCAL);

 /* MSI event and sys events */
 val = (origin & SYS_AND_MSI_MASK) >> PM_MSI_INT_MSI_SHIFT;
 events = val << (PM_MSI_INT_MSI_SHIFT - PCI_NUM_INTX + 1);

 /* INTx events */
 if (origin & PM_MSI_INT_INTX_MASK)
  events |= BIT(PM_MSI_INT_INTX_SHIFT);

 /* remains are same with register */
 events |= origin & GENMASK(P_ATR_EVT_DOORBELL_SHIFT, 0);

 return events;
}

static irqreturn_t plda_event_handler(int irq, void *dev_id)
{
 return IRQ_HANDLED;
}

static void plda_handle_event(struct irq_desc *desc)
{
 struct plda_pcie_rp *port = irq_desc_get_handler_data(desc);
 unsigned long events;
 u32 bit;
 struct irq_chip *chip = irq_desc_get_chip(desc);

 chained_irq_enter(chip, desc);

 events = port->event_ops->get_events(port);

 events &= port->events_bitmap;
 for_each_set_bit(bit, &events, port->num_events)
  generic_handle_domain_irq(port->event_domain, bit);

 chained_irq_exit(chip, desc);
}

static u32 plda_hwirq_to_mask(int hwirq)
{
 u32 mask;

 /* hwirq 23 - 0 are the same with register */
 if (hwirq < EVENT_PM_MSI_INT_INTX)
  mask = BIT(hwirq);
 else if (hwirq == EVENT_PM_MSI_INT_INTX)
  mask = PM_MSI_INT_INTX_MASK;
 else
  mask = BIT(hwirq + PCI_NUM_INTX - 1);

 return mask;
}

static void plda_ack_event_irq(struct irq_data *data)
{
 struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);

 writel_relaxed(plda_hwirq_to_mask(data->hwirq),
         port->bridge_addr + ISTATUS_LOCAL);
}

static void plda_mask_event_irq(struct irq_data *data)
{
 struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
 u32 mask, val;

 mask = plda_hwirq_to_mask(data->hwirq);

 raw_spin_lock(&port->lock);
 val = readl_relaxed(port->bridge_addr + IMASK_LOCAL);
 val &= ~mask;
 writel_relaxed(val, port->bridge_addr + IMASK_LOCAL);
 raw_spin_unlock(&port->lock);
}

static void plda_unmask_event_irq(struct irq_data *data)
{
 struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data);
 u32 mask, val;

 mask = plda_hwirq_to_mask(data->hwirq);

 raw_spin_lock(&port->lock);
 val = readl_relaxed(port->bridge_addr + IMASK_LOCAL);
 val |= mask;
 writel_relaxed(val, port->bridge_addr + IMASK_LOCAL);
 raw_spin_unlock(&port->lock);
}

static struct irq_chip plda_event_irq_chip = {
 .name = "PLDA PCIe EVENT",
 .irq_ack = plda_ack_event_irq,
 .irq_mask = plda_mask_event_irq,
 .irq_unmask = plda_unmask_event_irq,
};

static const struct plda_event_ops plda_event_ops = {
 .get_events = plda_get_events,
};

static int plda_pcie_event_map(struct irq_domain *domain, unsigned int irq,
          irq_hw_number_t hwirq)
{
 struct plda_pcie_rp *port = (void *)domain->host_data;

 irq_set_chip_and_handler(irq, port->event_irq_chip, handle_level_irq);
 irq_set_chip_data(irq, domain->host_data);

 return 0;
}

static const struct irq_domain_ops plda_event_domain_ops = {
 .map = plda_pcie_event_map,
};

static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port)
{
 struct device *dev = port->dev;
 struct device_node *node = dev->of_node;
 struct device_node *pcie_intc_node;

 /* Setup INTx */
 pcie_intc_node = of_get_next_child(node, NULL);
 if (!pcie_intc_node) {
  dev_err(dev, "failed to find PCIe Intc node\n");
  return -EINVAL;
 }

 port->event_domain = irq_domain_create_linear(of_fwnode_handle(pcie_intc_node),
            port->num_events, &plda_event_domain_ops,
            port);
 if (!port->event_domain) {
  dev_err(dev, "failed to get event domain\n");
  of_node_put(pcie_intc_node);
  return -ENOMEM;
 }

 irq_domain_update_bus_token(port->event_domain, DOMAIN_BUS_NEXUS);

 port->intx_domain = irq_domain_create_linear(of_fwnode_handle(pcie_intc_node), PCI_NUM_INTX,
           &intx_domain_ops, port);
 if (!port->intx_domain) {
  dev_err(dev, "failed to get an INTx IRQ domain\n");
  of_node_put(pcie_intc_node);
  return -ENOMEM;
 }

 irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED);

 of_node_put(pcie_intc_node);
 raw_spin_lock_init(&port->lock);

 return plda_allocate_msi_domains(port);
}

int plda_init_interrupts(struct platform_device *pdev,
    struct plda_pcie_rp *port,
    const struct plda_event *event)
{
 struct device *dev = &pdev->dev;
 int event_irq, ret;
 u32 i;

 if (!port->event_ops)
  port->event_ops = &plda_event_ops;

 if (!port->event_irq_chip)
  port->event_irq_chip = &plda_event_irq_chip;

 ret = plda_pcie_init_irq_domains(port);
 if (ret) {
  dev_err(dev, "failed creating IRQ domains\n");
  return ret;
 }

 port->irq = platform_get_irq(pdev, 0);
 if (port->irq < 0)
  return -ENODEV;

 for_each_set_bit(i, &port->events_bitmap, port->num_events) {
  event_irq = irq_create_mapping(port->event_domain, i);
  if (!event_irq) {
   dev_err(dev, "failed to map hwirq %d\n", i);
   return -ENXIO;
  }

  if (event->request_event_irq)
   ret = event->request_event_irq(port, event_irq, i);
  else
   ret = devm_request_irq(dev, event_irq,
            plda_event_handler,
            0, NULL, port);

  if (ret) {
   dev_err(dev, "failed to request IRQ %d\n", event_irq);
   return ret;
  }
 }

 port->intx_irq = irq_create_mapping(port->event_domain,
         event->intx_event);
 if (!port->intx_irq) {
  dev_err(dev, "failed to map INTx interrupt\n");
  return -ENXIO;
 }

 /* Plug the INTx chained handler */
 irq_set_chained_handler_and_data(port->intx_irq, plda_handle_intx, port);

 port->msi_irq = irq_create_mapping(port->event_domain,
        event->msi_event);
 if (!port->msi_irq)
  return -ENXIO;

 /* Plug the MSI chained handler */
 irq_set_chained_handler_and_data(port->msi_irq, plda_handle_msi, port);

 /* Plug the main event chained handler */
 irq_set_chained_handler_and_data(port->irq, plda_handle_event, port);

 return 0;
}
EXPORT_SYMBOL_GPL(plda_init_interrupts);

void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index,
       phys_addr_t axi_addr, phys_addr_t pci_addr,
       size_t size)
{
 u32 atr_sz = ilog2(size) - 1;
 u32 val;

 if (index == 0)
  val = PCIE_CONFIG_INTERFACE;
 else
  val = PCIE_TX_RX_INTERFACE;

 writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
        ATR0_AXI4_SLV0_TRSL_PARAM);

 val = ALIGN_DOWN(lower_32_bits(axi_addr), SZ_4K);
 val |= FIELD_PREP(ATR_SIZE_MASK, atr_sz);
 val |= ATR_IMPL_ENABLE;
 writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
        ATR0_AXI4_SLV0_SRCADDR_PARAM);

 val = upper_32_bits(axi_addr);
 writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
        ATR0_AXI4_SLV0_SRC_ADDR);

 val = lower_32_bits(pci_addr);
 writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
        ATR0_AXI4_SLV0_TRSL_ADDR_LSB);

 val = upper_32_bits(pci_addr);
 writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) +
        ATR0_AXI4_SLV0_TRSL_ADDR_UDW);
}
EXPORT_SYMBOL_GPL(plda_pcie_setup_window);

void plda_pcie_setup_inbound_address_translation(struct plda_pcie_rp *port)
{
 void __iomem *bridge_base_addr = port->bridge_addr;
 u32 val;

 val = readl(bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM);
 val |= (ATR0_PCIE_ATR_SIZE << ATR0_PCIE_ATR_SIZE_SHIFT);
 writel(val, bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM);
 writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR);
}
EXPORT_SYMBOL_GPL(plda_pcie_setup_inbound_address_translation);

int plda_pcie_setup_iomems(struct pci_host_bridge *bridge,
      struct plda_pcie_rp *port)
{
 void __iomem *bridge_base_addr = port->bridge_addr;
 struct resource_entry *entry;
 u64 pci_addr;
 u32 index = 1;

 resource_list_for_each_entry(entry, &bridge->windows) {
  if (resource_type(entry->res) == IORESOURCE_MEM) {
   pci_addr = entry->res->start - entry->offset;
   plda_pcie_setup_window(bridge_base_addr, index,
            entry->res->start, pci_addr,
            resource_size(entry->res));
   index++;
  }
 }

 return 0;
}
EXPORT_SYMBOL_GPL(plda_pcie_setup_iomems);

static void plda_pcie_irq_domain_deinit(struct plda_pcie_rp *pcie)
{
 irq_set_chained_handler_and_data(pcie->irq, NULL, NULL);
 irq_set_chained_handler_and_data(pcie->msi_irq, NULL, NULL);
 irq_set_chained_handler_and_data(pcie->intx_irq, NULL, NULL);

 irq_domain_remove(pcie->msi.dev_domain);

 irq_domain_remove(pcie->intx_domain);
 irq_domain_remove(pcie->event_domain);
}

int plda_pcie_host_init(struct plda_pcie_rp *port, struct pci_ops *ops,
   const struct plda_event *plda_event)
{
 struct device *dev = port->dev;
 struct pci_host_bridge *bridge;
 struct platform_device *pdev = to_platform_device(dev);
 struct resource *cfg_res;
 int ret;

 pdev = to_platform_device(dev);

 port->bridge_addr =
  devm_platform_ioremap_resource_byname(pdev, "apb");

 if (IS_ERR(port->bridge_addr))
  return dev_err_probe(dev, PTR_ERR(port->bridge_addr),
         "failed to map reg memory\n");

 cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg");
 if (!cfg_res)
  return dev_err_probe(dev, -ENODEV,
         "failed to get config memory\n");

 port->config_base = devm_ioremap_resource(dev, cfg_res);
 if (IS_ERR(port->config_base))
  return dev_err_probe(dev, PTR_ERR(port->config_base),
         "failed to map config memory\n");

 bridge = devm_pci_alloc_host_bridge(dev, 0);
 if (!bridge)
  return dev_err_probe(dev, -ENOMEM,
         "failed to alloc bridge\n");

 if (port->host_ops && port->host_ops->host_init) {
  ret = port->host_ops->host_init(port);
  if (ret)
   return ret;
 }

 port->bridge = bridge;
 plda_pcie_setup_window(port->bridge_addr, 0, cfg_res->start, 0,
          resource_size(cfg_res));
 plda_pcie_setup_iomems(bridge, port);
 plda_set_default_msi(&port->msi);
 ret = plda_init_interrupts(pdev, port, plda_event);
 if (ret)
  goto err_host;

 /* Set default bus ops */
 bridge->ops = ops;
 bridge->sysdata = port;

 ret = pci_host_probe(bridge);
 if (ret < 0) {
  dev_err_probe(dev, ret, "failed to probe pci host\n");
  goto err_probe;
 }

 return ret;

err_probe:
 plda_pcie_irq_domain_deinit(port);
err_host:
 if (port->host_ops && port->host_ops->host_deinit)
  port->host_ops->host_deinit(port);

 return ret;
}
EXPORT_SYMBOL_GPL(plda_pcie_host_init);

void plda_pcie_host_deinit(struct plda_pcie_rp *port)
{
 pci_stop_root_bus(port->bridge->bus);
 pci_remove_root_bus(port->bridge->bus);

 plda_pcie_irq_domain_deinit(port);

 if (port->host_ops && port->host_ops->host_deinit)
  port->host_ops->host_deinit(port);
}
EXPORT_SYMBOL_GPL(plda_pcie_host_deinit);

Messung V0.5
C=99 H=86 G=92

¤ Dauer der Verarbeitung: 0.4 Sekunden  ¤

*© 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.