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

Quelle  irq-mvebu-odmi.c   Sprache: C

 
/*
 * Copyright (C) 2016 Marvell
 *
 * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
 *
 * This file is licensed under the terms of the GNU General Public
 * License version 2.  This program is licensed "as is" without any
 * warranty of any kind, whether express or implied.
 */


#define pr_fmt(fmt) "GIC-ODMI: " fmt

#include <linux/irq.h>
#include <linux/irqchip.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/msi.h>
#include <linux/of_address.h>
#include <linux/slab.h>

#include <linux/irqchip/irq-msi-lib.h>

#include <dt-bindings/interrupt-controller/arm-gic.h>

#define GICP_ODMIN_SET   0x40
#define   GICP_ODMI_INT_NUM_SHIFT 12
#define GICP_ODMIN_GM_EP_R0  0x110
#define GICP_ODMIN_GM_EP_R1  0x114
#define GICP_ODMIN_GM_EA_R0  0x108
#define GICP_ODMIN_GM_EA_R1  0x118

/*
 * We don't support the group events, so we simply have 8 interrupts
 * per frame.
 */

#define NODMIS_SHIFT  3
#define NODMIS_PER_FRAME (1 << NODMIS_SHIFT)
#define NODMIS_MASK  (NODMIS_PER_FRAME - 1)

struct odmi_data {
 struct resource res;
 void __iomem *base;
 unsigned int spi_base;
};

static struct odmi_data *odmis;
static unsigned long *odmis_bm;
static unsigned int odmis_count;

/* Protects odmis_bm */
static DEFINE_SPINLOCK(odmis_bm_lock);

static void odmi_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
{
 struct odmi_data *odmi;
 phys_addr_t addr;
 unsigned int odmin;

 if (WARN_ON(d->hwirq >= odmis_count * NODMIS_PER_FRAME))
  return;

 odmi = &odmis[d->hwirq >> NODMIS_SHIFT];
 odmin = d->hwirq & NODMIS_MASK;

 addr = odmi->res.start + GICP_ODMIN_SET;

 msg->address_hi = upper_32_bits(addr);
 msg->address_lo = lower_32_bits(addr);
 msg->data = odmin << GICP_ODMI_INT_NUM_SHIFT;
}

static struct irq_chip odmi_irq_chip = {
 .name   = "ODMI",
 .irq_mask  = irq_chip_mask_parent,
 .irq_unmask  = irq_chip_unmask_parent,
 .irq_eoi  = irq_chip_eoi_parent,
 .irq_set_affinity = irq_chip_set_affinity_parent,
 .irq_compose_msi_msg = odmi_compose_msi_msg,
};

static int odmi_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
     unsigned int nr_irqs, void *args)
{
 struct odmi_data *odmi = NULL;
 struct irq_fwspec fwspec;
 struct irq_data *d;
 unsigned int hwirq, odmin;
 int ret;

 spin_lock(&odmis_bm_lock);
 hwirq = find_first_zero_bit(odmis_bm, NODMIS_PER_FRAME * odmis_count);
 if (hwirq >= NODMIS_PER_FRAME * odmis_count) {
  spin_unlock(&odmis_bm_lock);
  return -ENOSPC;
 }

 __set_bit(hwirq, odmis_bm);
 spin_unlock(&odmis_bm_lock);

 odmi = &odmis[hwirq >> NODMIS_SHIFT];
 odmin = hwirq & NODMIS_MASK;

 fwspec.fwnode = domain->parent->fwnode;
 fwspec.param_count = 3;
 fwspec.param[0] = GIC_SPI;
 fwspec.param[1] = odmi->spi_base - 32 + odmin;
 fwspec.param[2] = IRQ_TYPE_EDGE_RISING;

 ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
 if (ret) {
  pr_err("Cannot allocate parent IRQ\n");
  spin_lock(&odmis_bm_lock);
  __clear_bit(odmin, odmis_bm);
  spin_unlock(&odmis_bm_lock);
  return ret;
 }

 /* Configure the interrupt line to be edge */
 d = irq_domain_get_irq_data(domain->parent, virq);
 d->chip->irq_set_type(d, IRQ_TYPE_EDGE_RISING);

 irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
          &odmi_irq_chip, NULL);

 return 0;
}

static void odmi_irq_domain_free(struct irq_domain *domain,
     unsigned int virq, unsigned int nr_irqs)
{
 struct irq_data *d = irq_domain_get_irq_data(domain, virq);

 if (d->hwirq >= odmis_count * NODMIS_PER_FRAME) {
  pr_err("Failed to teardown msi. Invalid hwirq %lu\n", d->hwirq);
  return;
 }

 irq_domain_free_irqs_parent(domain, virq, nr_irqs);

 /* Actually free the MSI */
 spin_lock(&odmis_bm_lock);
 __clear_bit(d->hwirq, odmis_bm);
 spin_unlock(&odmis_bm_lock);
}

static const struct irq_domain_ops odmi_domain_ops = {
 .select = msi_lib_irq_domain_select,
 .alloc = odmi_irq_domain_alloc,
 .free = odmi_irq_domain_free,
};

#define ODMI_MSI_FLAGS_REQUIRED  (MSI_FLAG_USE_DEF_DOM_OPS | \
      MSI_FLAG_USE_DEF_CHIP_OPS)

#define ODMI_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK)

static const struct msi_parent_ops odmi_msi_parent_ops = {
 .supported_flags = ODMI_MSI_FLAGS_SUPPORTED,
 .required_flags  = ODMI_MSI_FLAGS_REQUIRED,
 .chip_flags  = MSI_CHIP_FLAG_SET_EOI,
 .bus_select_token = DOMAIN_BUS_GENERIC_MSI,
 .bus_select_mask = MATCH_PLATFORM_MSI,
 .prefix   = "ODMI-",
 .init_dev_msi_info = msi_lib_init_dev_msi_info,
};

static int __init mvebu_odmi_init(struct device_node *node,
      struct device_node *parent)
{
 struct irq_domain_info info = {
  .fwnode = of_fwnode_handle(node),
  .ops = &odmi_domain_ops,
  .size = odmis_count * NODMIS_PER_FRAME,
  .parent = irq_find_host(parent),
 };
 int ret, i;

 if (of_property_read_u32(node, "marvell,odmi-frames", &odmis_count))
  return -EINVAL;

 odmis = kcalloc(odmis_count, sizeof(struct odmi_data), GFP_KERNEL);
 if (!odmis)
  return -ENOMEM;

 odmis_bm = bitmap_zalloc(odmis_count * NODMIS_PER_FRAME, GFP_KERNEL);
 if (!odmis_bm) {
  ret = -ENOMEM;
  goto err_alloc;
 }

 for (i = 0; i < odmis_count; i++) {
  struct odmi_data *odmi = &odmis[i];

  ret = of_address_to_resource(node, i, &odmi->res);
  if (ret)
   goto err_unmap;

  odmi->base = of_io_request_and_map(node, i, "odmi");
  if (IS_ERR(odmi->base)) {
   ret = PTR_ERR(odmi->base);
   goto err_unmap;
  }

  if (of_property_read_u32_index(node, "marvell,spi-base",
            i, &odmi->spi_base)) {
   ret = -EINVAL;
   goto err_unmap;
  }
 }

 if (msi_create_parent_irq_domain(&info, &odmi_msi_parent_ops))
  return 0;

 ret = -ENOMEM;

err_unmap:
 for (i = 0; i < odmis_count; i++) {
  struct odmi_data *odmi = &odmis[i];

  if (odmi->base && !IS_ERR(odmi->base))
   iounmap(odmis[i].base);
 }
 bitmap_free(odmis_bm);
err_alloc:
 kfree(odmis);
 return ret;
}

IRQCHIP_DECLARE(mvebu_odmi, "marvell,odmi-controller", mvebu_odmi_init);

Messung V0.5
C=96 H=96 G=95

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