Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/interrupt-controller/loongson,pch-lpc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

title: Loongson PCH LPC Controller

maintainers:
- Jiaxun Yang <jiaxun.yang@flygoat.com>

description:
This interrupt controller is found in the Loongson LS7A family of PCH for
accepting interrupts sent by LPC-connected peripherals and signalling PIC
via a single interrupt line when interrupts are available.

properties:
compatible:
const: loongson,pch-lpc-1.0

reg:
maxItems: 1

interrupt-controller: true

interrupts:
maxItems: 1

'#interrupt-cells':
const: 2

required:
- compatible
- reg
- interrupt-controller
- interrupts
- '#interrupt-cells'

additionalProperties: false

examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
lpc: interrupt-controller@10002000 {
compatible = "loongson,pch-lpc-1.0";
reg = <0x10002000 0x400>;
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&pic>;
interrupts = <19 IRQ_TYPE_LEVEL_HIGH>;
};
...
23 changes: 17 additions & 6 deletions arch/mips/boot/dts/loongson/ls7a-pch.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@
#interrupt-cells = <2>;
};

rtc0: rtc@100d0100 {
compatible = "loongson,ls7a-rtc";
reg = <0 0x100d0100 0 0x78>;
lpc: interrupt-controller@10002000 {
compatible = "loongson,pch-lpc-1.0";
reg = <0 0x10002000 0 0x1000>;
interrupt-controller;
interrupt-parent = <&pic>;
interrupts = <52 IRQ_TYPE_LEVEL_HIGH>;
interrupts = <19 IRQ_TYPE_LEVEL_HIGH>;
#interrupt-cells = <2>;
};

ls7a_uart0: serial@10080000 {
Expand Down Expand Up @@ -65,6 +67,13 @@
no-loopback-test;
};

rtc0: rtc@100d0100 {
compatible = "loongson,ls7a-rtc";
reg = <0 0x100d0100 0 0x78>;
interrupt-parent = <&pic>;
interrupts = <52 IRQ_TYPE_LEVEL_HIGH>;
};

pci@1a000000 {
compatible = "loongson,ls7a-pci";
device_type = "pci";
Expand Down Expand Up @@ -199,7 +208,8 @@
<13 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "macirq", "eth_lpi";
interrupt-parent = <&pic>;
phy-mode = "rgmii";
phy-mode = "rgmii-id";
phy-handle = <&phy0>;
mdio {
#address-cells = <1>;
#size-cells = <0>;
Expand All @@ -222,7 +232,8 @@
<15 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "macirq", "eth_lpi";
interrupt-parent = <&pic>;
phy-mode = "rgmii";
phy-mode = "rgmii-id";
phy-handle = <&phy1>;
mdio {
#address-cells = <1>;
#size-cells = <0>;
Expand Down
1 change: 0 additions & 1 deletion drivers/irqchip/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,6 @@ config LOONGSON_PCH_MSI

config LOONGSON_PCH_LPC
bool "Loongson PCH LPC Controller"
depends on LOONGARCH
depends on MACH_LOONGSON64
default MACH_LOONGSON64
select IRQ_DOMAIN_HIERARCHY
Expand Down
86 changes: 66 additions & 20 deletions drivers/irqchip/irq-loongson-pch-lpc.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/syscore_ops.h>

#include "irq-loongson.h"
Expand Down Expand Up @@ -171,21 +173,19 @@ static struct syscore_ops pch_lpc_syscore_ops = {
.resume = pch_lpc_resume,
};

int __init pch_lpc_acpi_init(struct irq_domain *parent,
struct acpi_madt_lpc_pic *acpi_pchlpc)
static int __init pch_lpc_init(phys_addr_t addr, unsigned long size,
struct fwnode_handle *irq_handle,
int parent_irq)
{
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);
priv->base = ioremap(addr, size);
if (!priv->base)
goto free_priv;

Expand All @@ -194,12 +194,6 @@ int __init pch_lpc_acpi_init(struct irq_domain *parent,
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.
Expand All @@ -209,15 +203,10 @@ int __init pch_lpc_acpi_init(struct irq_domain *parent,
&pch_lpc_domain_ops, priv);
if (!priv->lpc_domain) {
pr_err("Failed to create IRQ domain\n");
goto free_irq_handle;
goto iounmap_base;
}
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;
Expand All @@ -226,12 +215,69 @@ int __init pch_lpc_acpi_init(struct irq_domain *parent,

return 0;

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

return -ENOMEM;
}

#ifdef CONFIG_ACPI
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;
int ret;

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

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);

ret = pch_lpc_init(acpi_pchlpc->address, acpi_pchlpc->size,
irq_handle, parent_irq);
if (ret) {
irq_domain_free_fwnode(irq_handle);
return ret;
}

return 0;
}
#endif /* CONFIG_ACPI */

#ifdef CONFIG_OF
static int pch_lpc_of_init(struct device_node *node,
struct device_node *parent)
{
int parent_irq;
struct fwnode_handle *irq_handle;
struct resource res;

if (of_address_to_resource(node, 0, &res))
return -EINVAL;

parent_irq = irq_of_parse_and_map(node, 0);
if (!parent_irq) {
pr_err("Failed to get the parent IRQ for LPC IRQs\n");
return -EINVAL;
}

irq_handle = of_fwnode_handle(node);

return pch_lpc_init(res.start, resource_size(&res), irq_handle,
parent_irq);
}

IRQCHIP_DECLARE(pch_lpc, "loongson,pch-lpc-1.0", pch_lpc_of_init);
#endif /* CONFIG_OF */
2 changes: 1 addition & 1 deletion kernel/softirq.c
Original file line number Diff line number Diff line change
Expand Up @@ -1184,5 +1184,5 @@ int __init __weak arch_early_irq_init(void)

unsigned int __weak arch_dynirq_lower_bound(unsigned int from)
{
return from;
return MAX(from, NR_IRQS_LEGACY);
}