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-loongson-pch-lpc.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/*
 * Loongson LPC Interrupt Controller support
 *
 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
 */


#define pr_fmt(fmt) "lpc: " fmt

#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/syscore_ops.h>

#include "irq-loongson.h"

/* Registers */
#define LPC_INT_CTL  0x00
#define LPC_INT_ENA  0x04
#define LPC_INT_STS  0x08
#define LPC_INT_CLR  0x0c
#define LPC_INT_POL  0x10
#define LPC_COUNT  16

/* LPC_INT_CTL */
#define LPC_INT_CTL_EN  BIT(31)

struct pch_lpc {
 void __iomem  *base;
 struct irq_domain *lpc_domain;
 raw_spinlock_t  lpc_lock;
 u32   saved_reg_ctl;
 u32   saved_reg_ena;
 u32   saved_reg_pol;
};

static struct pch_lpc *pch_lpc_priv;
struct fwnode_handle *pch_lpc_handle;

static void lpc_irq_ack(struct irq_data *d)
{
 unsigned long flags;
 struct pch_lpc *priv = d->domain->host_data;

 raw_spin_lock_irqsave(&priv->lpc_lock, flags);
 writel(0x1 << d->hwirq, priv->base + LPC_INT_CLR);
 raw_spin_unlock_irqrestore(&priv->lpc_lock, flags);
}

static void lpc_irq_mask(struct irq_data *d)
{
 unsigned long flags;
 struct pch_lpc *priv = d->domain->host_data;

 raw_spin_lock_irqsave(&priv->lpc_lock, flags);
 writel(readl(priv->base + LPC_INT_ENA) & (~(0x1 << (d->hwirq))),
   priv->base + LPC_INT_ENA);
 raw_spin_unlock_irqrestore(&priv->lpc_lock, flags);
}

static void lpc_irq_unmask(struct irq_data *d)
{
 unsigned long flags;
 struct pch_lpc *priv = d->domain->host_data;

 raw_spin_lock_irqsave(&priv->lpc_lock, flags);
 writel(readl(priv->base + LPC_INT_ENA) | (0x1 << (d->hwirq)),
   priv->base + LPC_INT_ENA);
 raw_spin_unlock_irqrestore(&priv->lpc_lock, flags);
}

static int lpc_irq_set_type(struct irq_data *d, unsigned int type)
{
 u32 val;
 u32 mask = 0x1 << (d->hwirq);
 struct pch_lpc *priv = d->domain->host_data;

 if (!(type & IRQ_TYPE_LEVEL_MASK))
  return 0;

 val = readl(priv->base + LPC_INT_POL);

 if (type == IRQ_TYPE_LEVEL_HIGH)
  val |= mask;
 else
  val &= ~mask;

 writel(val, priv->base + LPC_INT_POL);

 return 0;
}

static const struct irq_chip pch_lpc_irq_chip = {
 .name   = "PCH LPC",
 .irq_mask  = lpc_irq_mask,
 .irq_unmask  = lpc_irq_unmask,
 .irq_ack  = lpc_irq_ack,
 .irq_set_type  = lpc_irq_set_type,
 .flags   = IRQCHIP_SKIP_SET_WAKE,
};

static void lpc_irq_dispatch(struct irq_desc *desc)
{
 u32 pending, bit;
 struct irq_chip *chip = irq_desc_get_chip(desc);
 struct pch_lpc *priv = irq_desc_get_handler_data(desc);

 chained_irq_enter(chip, desc);

 pending = readl(priv->base + LPC_INT_ENA);
 pending &= readl(priv->base + LPC_INT_STS);
 if (!pending)
  spurious_interrupt();

 while (pending) {
  bit = __ffs(pending);

  generic_handle_domain_irq(priv->lpc_domain, bit);
  pending &= ~BIT(bit);
 }
 chained_irq_exit(chip, desc);
}

static int pch_lpc_map(struct irq_domain *d, unsigned int irq,
   irq_hw_number_t hw)
{
 irq_set_chip_and_handler(irq, &pch_lpc_irq_chip, handle_level_irq);
 return 0;
}

static const struct irq_domain_ops pch_lpc_domain_ops = {
 .map   = pch_lpc_map,
 .translate = irq_domain_translate_twocell,
};

static void pch_lpc_reset(struct pch_lpc *priv)
{
 /* Enable the LPC interrupt, bit31: en  bit30: edge */
 writel(LPC_INT_CTL_EN, priv->base + LPC_INT_CTL);
 writel(0, priv->base + LPC_INT_ENA);
 /* Clear all 18-bit interrpt bit */
 writel(GENMASK(17, 0), priv->base + LPC_INT_CLR);
}

static int pch_lpc_disabled(struct pch_lpc *priv)
{
 return (readl(priv->base + LPC_INT_ENA) == 0xffffffff) &&
   (readl(priv->base + LPC_INT_STS) == 0xffffffff);
}

static int pch_lpc_suspend(void)
{
 pch_lpc_priv->saved_reg_ctl = readl(pch_lpc_priv->base + LPC_INT_CTL);
 pch_lpc_priv->saved_reg_ena = readl(pch_lpc_priv->base + LPC_INT_ENA);
 pch_lpc_priv->saved_reg_pol = readl(pch_lpc_priv->base + LPC_INT_POL);
 return 0;
}

static void pch_lpc_resume(void)
{
 writel(pch_lpc_priv->saved_reg_ctl, pch_lpc_priv->base + LPC_INT_CTL);
 writel(pch_lpc_priv->saved_reg_ena, pch_lpc_priv->base + LPC_INT_ENA);
 writel(pch_lpc_priv->saved_reg_pol, pch_lpc_priv->base + LPC_INT_POL);
}

static struct syscore_ops pch_lpc_syscore_ops = {
 .suspend = pch_lpc_suspend,
 .resume = pch_lpc_resume,
};

int __init pch_lpc_acpi_init(struct irq_domain *parent,
     struct acpi_madt_lpc_pic *acpi_pchlpc)
{
 int parent_irq;
 struct pch_lpc *priv;
 struct irq_fwspec fwspec;
 struct fwnode_handle *irq_handle;

 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 if (!priv)
  return -ENOMEM;

 raw_spin_lock_init(&priv->lpc_lock);

 priv->base = ioremap(acpi_pchlpc->address, acpi_pchlpc->size);
 if (!priv->base)
  goto free_priv;

 if (pch_lpc_disabled(priv)) {
  pr_err("Failed to get LPC status\n");
  goto iounmap_base;
 }

 irq_handle = irq_domain_alloc_named_fwnode("lpcintc");
 if (!irq_handle) {
  pr_err("Unable to allocate domain handle\n");
  goto iounmap_base;
 }

 /*
 * The LPC interrupt controller is a legacy i8259-compatible device,
 * which requires a static 1:1 mapping for IRQs 0-15.
 * Use irq_domain_create_legacy to establish this static mapping early.
 */

 priv->lpc_domain = irq_domain_create_legacy(irq_handle, LPC_COUNT, 0, 0,
          &pch_lpc_domain_ops, priv);
 if (!priv->lpc_domain) {
  pr_err("Failed to create IRQ domain\n");
  goto free_irq_handle;
 }
 pch_lpc_reset(priv);

 fwspec.fwnode = parent->fwnode;
 fwspec.param[0] = acpi_pchlpc->cascade + GSI_MIN_PCH_IRQ;
 fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH;
 fwspec.param_count = 2;
 parent_irq = irq_create_fwspec_mapping(&fwspec);
 irq_set_chained_handler_and_data(parent_irq, lpc_irq_dispatch, priv);

 pch_lpc_priv = priv;
 pch_lpc_handle = irq_handle;
 register_syscore_ops(&pch_lpc_syscore_ops);

 return 0;

free_irq_handle:
 irq_domain_free_fwnode(irq_handle);
iounmap_base:
 iounmap(priv->base);
free_priv:
 kfree(priv);

 return -ENOMEM;
}

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

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