mirror of
https://github.com/torvalds/linux.git
synced 2026-05-30 10:04:04 +02:00
Merge branch irq/loongarch-acpi into irq/irqchip-next
* irq/loongarch-acpi: : . : More APCI fixes and improvements for the LoongArch architecture: : : - Work around trigger type for INTx interrupts described : via ACPI (Jianmin Lv). : : - ACPI support got the HTVEC controller (Huacai Chen) : : - Suspend/resume across the board (Huacai Chen) : : - Fixes and random cleanups : . irqchip/loongarch: Adjust acpi_cascade_irqdomain_init() and sub-routines irqchip/loongson-pch-lpc: Add suspend/resume support irqchip/loongson-pch-pic: Add suspend/resume support irqchip/loongson-eiointc: Add suspend/resume support irqchip/loongson-htvec: Add suspend/resume support irqchip/loongson-htvec: Add ACPI init support irqchip/loongson-liointc: Support to set IRQ type for ACPI path irqchip/loongson-pch-pic: Support to set IRQ type for ACPI path irqchip/loongson-pch-pic: Fix translate callback for DT path ACPI / PCI: fix LPIC IRQ model default PCI IRQ polarity Signed-off-by: Marc Zyngier <maz@kernel.org>
This commit is contained in:
commit
63ab33c08c
|
|
@ -93,7 +93,7 @@ int liointc_acpi_init(struct irq_domain *parent,
|
|||
int eiointc_acpi_init(struct irq_domain *parent,
|
||||
struct acpi_madt_eio_pic *acpi_eiointc);
|
||||
|
||||
struct irq_domain *htvec_acpi_init(struct irq_domain *parent,
|
||||
int htvec_acpi_init(struct irq_domain *parent,
|
||||
struct acpi_madt_ht_pic *acpi_htvec);
|
||||
int pch_lpc_acpi_init(struct irq_domain *parent,
|
||||
struct acpi_madt_lpc_pic *acpi_pchlpc);
|
||||
|
|
|
|||
|
|
@ -387,13 +387,15 @@ int acpi_pci_irq_enable(struct pci_dev *dev)
|
|||
u8 pin;
|
||||
int triggering = ACPI_LEVEL_SENSITIVE;
|
||||
/*
|
||||
* On ARM systems with the GIC interrupt model, level interrupts
|
||||
* On ARM systems with the GIC interrupt model, or LoongArch
|
||||
* systems with the LPIC interrupt model, level interrupts
|
||||
* are always polarity high by specification; PCI legacy
|
||||
* IRQs lines are inverted before reaching the interrupt
|
||||
* controller and must therefore be considered active high
|
||||
* as default.
|
||||
*/
|
||||
int polarity = acpi_irq_model == ACPI_IRQ_MODEL_GIC ?
|
||||
int polarity = acpi_irq_model == ACPI_IRQ_MODEL_GIC ||
|
||||
acpi_irq_model == ACPI_IRQ_MODEL_LPIC ?
|
||||
ACPI_ACTIVE_HIGH : ACPI_ACTIVE_LOW;
|
||||
char *link = NULL;
|
||||
char link_desc[16];
|
||||
|
|
|
|||
|
|
@ -576,6 +576,7 @@ config IRQ_LOONGARCH_CPU
|
|||
select GENERIC_IRQ_CHIP
|
||||
select IRQ_DOMAIN
|
||||
select GENERIC_IRQ_EFFECTIVE_AFF_MASK
|
||||
select LOONGSON_HTVEC
|
||||
select LOONGSON_LIOINTC
|
||||
select LOONGSON_EIOINTC
|
||||
select LOONGSON_PCH_PIC
|
||||
|
|
|
|||
|
|
@ -92,18 +92,16 @@ static const struct irq_domain_ops loongarch_cpu_intc_irq_domain_ops = {
|
|||
.xlate = irq_domain_xlate_onecell,
|
||||
};
|
||||
|
||||
static int __init
|
||||
liointc_parse_madt(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
static int __init liointc_parse_madt(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_madt_lio_pic *liointc_entry = (struct acpi_madt_lio_pic *)header;
|
||||
|
||||
return liointc_acpi_init(irq_domain, liointc_entry);
|
||||
}
|
||||
|
||||
static int __init
|
||||
eiointc_parse_madt(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
static int __init eiointc_parse_madt(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_madt_eio_pic *eiointc_entry = (struct acpi_madt_eio_pic *)header;
|
||||
|
||||
|
|
@ -112,16 +110,24 @@ eiointc_parse_madt(union acpi_subtable_headers *header,
|
|||
|
||||
static int __init acpi_cascade_irqdomain_init(void)
|
||||
{
|
||||
acpi_table_parse_madt(ACPI_MADT_TYPE_LIO_PIC,
|
||||
liointc_parse_madt, 0);
|
||||
acpi_table_parse_madt(ACPI_MADT_TYPE_EIO_PIC,
|
||||
eiointc_parse_madt, 0);
|
||||
int r;
|
||||
|
||||
r = acpi_table_parse_madt(ACPI_MADT_TYPE_LIO_PIC, liointc_parse_madt, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = acpi_table_parse_madt(ACPI_MADT_TYPE_EIO_PIC, eiointc_parse_madt, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init cpuintc_acpi_init(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (irq_domain)
|
||||
return 0;
|
||||
|
||||
|
|
@ -139,9 +145,9 @@ static int __init cpuintc_acpi_init(union acpi_subtable_headers *header,
|
|||
set_handle_irq(&handle_cpu_irq);
|
||||
acpi_set_irq_model(ACPI_IRQ_MODEL_LPIC, lpic_get_gsi_domain_id);
|
||||
acpi_set_gsi_to_irq_fallback(lpic_gsi_to_irq);
|
||||
acpi_cascade_irqdomain_init();
|
||||
ret = acpi_cascade_irqdomain_init();
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
IRQCHIP_ACPI_DECLARE(cpuintc_v1, ACPI_MADT_TYPE_CORE_PIC,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
|
||||
#define EIOINTC_REG_NODEMAP 0x14a0
|
||||
#define EIOINTC_REG_IPMAP 0x14c0
|
||||
|
|
@ -301,9 +302,39 @@ static struct irq_domain *acpi_get_vec_parent(int node, struct acpi_vector_group
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int __init
|
||||
pch_pic_parse_madt(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
static int eiointc_suspend(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void eiointc_resume(void)
|
||||
{
|
||||
int i, j;
|
||||
struct irq_desc *desc;
|
||||
struct irq_data *irq_data;
|
||||
|
||||
eiointc_router_init(0);
|
||||
|
||||
for (i = 0; i < nr_pics; i++) {
|
||||
for (j = 0; j < VEC_COUNT; j++) {
|
||||
desc = irq_resolve_mapping(eiointc_priv[i]->eiointc_domain, j);
|
||||
if (desc && desc->handle_irq && desc->handle_irq != handle_bad_irq) {
|
||||
raw_spin_lock(&desc->lock);
|
||||
irq_data = &desc->irq_data;
|
||||
eiointc_set_irq_affinity(irq_data, irq_data->common->affinity, 0);
|
||||
raw_spin_unlock(&desc->lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct syscore_ops eiointc_syscore_ops = {
|
||||
.suspend = eiointc_suspend,
|
||||
.resume = eiointc_resume,
|
||||
};
|
||||
|
||||
static int __init pch_pic_parse_madt(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_madt_bio_pic *pchpic_entry = (struct acpi_madt_bio_pic *)header;
|
||||
unsigned int node = (pchpic_entry->address >> 44) & 0xf;
|
||||
|
|
@ -315,9 +346,8 @@ pch_pic_parse_madt(union acpi_subtable_headers *header,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int __init
|
||||
pch_msi_parse_madt(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
static int __init pch_msi_parse_madt(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_madt_msi_pic *pchmsi_entry = (struct acpi_madt_msi_pic *)header;
|
||||
struct irq_domain *parent = acpi_get_vec_parent(eiointc_priv[nr_pics - 1]->node, msi_group);
|
||||
|
|
@ -330,17 +360,23 @@ pch_msi_parse_madt(union acpi_subtable_headers *header,
|
|||
|
||||
static int __init acpi_cascade_irqdomain_init(void)
|
||||
{
|
||||
acpi_table_parse_madt(ACPI_MADT_TYPE_BIO_PIC,
|
||||
pch_pic_parse_madt, 0);
|
||||
acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC,
|
||||
pch_msi_parse_madt, 1);
|
||||
int r;
|
||||
|
||||
r = acpi_table_parse_madt(ACPI_MADT_TYPE_BIO_PIC, pch_pic_parse_madt, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 1);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init eiointc_acpi_init(struct irq_domain *parent,
|
||||
struct acpi_madt_eio_pic *acpi_eiointc)
|
||||
{
|
||||
int i, parent_irq;
|
||||
int i, ret, parent_irq;
|
||||
unsigned long node_map;
|
||||
struct eiointc_priv *priv;
|
||||
|
||||
|
|
@ -380,15 +416,16 @@ int __init eiointc_acpi_init(struct irq_domain *parent,
|
|||
parent_irq = irq_create_mapping(parent, acpi_eiointc->cascade);
|
||||
irq_set_chained_handler_and_data(parent_irq, eiointc_irq_dispatch, priv);
|
||||
|
||||
register_syscore_ops(&eiointc_syscore_ops);
|
||||
cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_LOONGARCH_STARTING,
|
||||
"irqchip/loongarch/intc:starting",
|
||||
eiointc_router_init, NULL);
|
||||
|
||||
acpi_set_vec_parent(acpi_eiointc->node, priv->eiointc_domain, pch_group);
|
||||
acpi_set_vec_parent(acpi_eiointc->node, priv->eiointc_domain, msi_group);
|
||||
acpi_cascade_irqdomain_init();
|
||||
ret = acpi_cascade_irqdomain_init();
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
|
||||
out_free_handle:
|
||||
irq_domain_free_fwnode(priv->domain_handle);
|
||||
|
|
|
|||
|
|
@ -16,11 +16,11 @@
|
|||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
|
||||
/* Registers */
|
||||
#define HTVEC_EN_OFF 0x20
|
||||
#define HTVEC_MAX_PARENT_IRQ 8
|
||||
|
||||
#define VEC_COUNT_PER_REG 32
|
||||
#define VEC_REG_IDX(irq_id) ((irq_id) / VEC_COUNT_PER_REG)
|
||||
#define VEC_REG_BIT(irq_id) ((irq_id) % VEC_COUNT_PER_REG)
|
||||
|
|
@ -30,8 +30,11 @@ struct htvec {
|
|||
void __iomem *base;
|
||||
struct irq_domain *htvec_domain;
|
||||
raw_spinlock_t htvec_lock;
|
||||
u32 saved_vec_en[HTVEC_MAX_PARENT_IRQ];
|
||||
};
|
||||
|
||||
static struct htvec *htvec_priv;
|
||||
|
||||
static void htvec_irq_dispatch(struct irq_desc *desc)
|
||||
{
|
||||
int i;
|
||||
|
|
@ -155,64 +158,169 @@ static void htvec_reset(struct htvec *priv)
|
|||
}
|
||||
}
|
||||
|
||||
static int htvec_of_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
static int htvec_suspend(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < htvec_priv->num_parents; i++)
|
||||
htvec_priv->saved_vec_en[i] = readl(htvec_priv->base + HTVEC_EN_OFF + 4 * i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void htvec_resume(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < htvec_priv->num_parents; i++)
|
||||
writel(htvec_priv->saved_vec_en[i], htvec_priv->base + HTVEC_EN_OFF + 4 * i);
|
||||
}
|
||||
|
||||
static struct syscore_ops htvec_syscore_ops = {
|
||||
.suspend = htvec_suspend,
|
||||
.resume = htvec_resume,
|
||||
};
|
||||
|
||||
static int htvec_init(phys_addr_t addr, unsigned long size,
|
||||
int num_parents, int parent_irq[], struct fwnode_handle *domain_handle)
|
||||
{
|
||||
int i;
|
||||
struct htvec *priv;
|
||||
int err, parent_irq[8], i;
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->num_parents = num_parents;
|
||||
priv->base = ioremap(addr, size);
|
||||
raw_spin_lock_init(&priv->htvec_lock);
|
||||
priv->base = of_iomap(node, 0);
|
||||
if (!priv->base) {
|
||||
err = -ENOMEM;
|
||||
goto free_priv;
|
||||
|
||||
/* Setup IRQ domain */
|
||||
priv->htvec_domain = irq_domain_create_linear(domain_handle,
|
||||
(VEC_COUNT_PER_REG * priv->num_parents),
|
||||
&htvec_domain_ops, priv);
|
||||
if (!priv->htvec_domain) {
|
||||
pr_err("loongson-htvec: cannot add IRQ domain\n");
|
||||
goto iounmap_base;
|
||||
}
|
||||
|
||||
htvec_reset(priv);
|
||||
|
||||
for (i = 0; i < priv->num_parents; i++) {
|
||||
irq_set_chained_handler_and_data(parent_irq[i],
|
||||
htvec_irq_dispatch, priv);
|
||||
}
|
||||
|
||||
htvec_priv = priv;
|
||||
|
||||
register_syscore_ops(&htvec_syscore_ops);
|
||||
|
||||
return 0;
|
||||
|
||||
iounmap_base:
|
||||
iounmap(priv->base);
|
||||
kfree(priv);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
||||
static int htvec_of_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
{
|
||||
int i, err;
|
||||
int parent_irq[8];
|
||||
int num_parents = 0;
|
||||
struct resource res;
|
||||
|
||||
if (of_address_to_resource(node, 0, &res))
|
||||
return -EINVAL;
|
||||
|
||||
/* Interrupt may come from any of the 8 interrupt lines */
|
||||
for (i = 0; i < HTVEC_MAX_PARENT_IRQ; i++) {
|
||||
parent_irq[i] = irq_of_parse_and_map(node, i);
|
||||
if (parent_irq[i] <= 0)
|
||||
break;
|
||||
|
||||
priv->num_parents++;
|
||||
num_parents++;
|
||||
}
|
||||
|
||||
if (!priv->num_parents) {
|
||||
pr_err("Failed to get parent irqs\n");
|
||||
err = -ENODEV;
|
||||
goto iounmap_base;
|
||||
}
|
||||
|
||||
priv->htvec_domain = irq_domain_create_linear(of_node_to_fwnode(node),
|
||||
(VEC_COUNT_PER_REG * priv->num_parents),
|
||||
&htvec_domain_ops, priv);
|
||||
if (!priv->htvec_domain) {
|
||||
pr_err("Failed to create IRQ domain\n");
|
||||
err = -ENOMEM;
|
||||
goto irq_dispose;
|
||||
}
|
||||
|
||||
htvec_reset(priv);
|
||||
|
||||
for (i = 0; i < priv->num_parents; i++)
|
||||
irq_set_chained_handler_and_data(parent_irq[i],
|
||||
htvec_irq_dispatch, priv);
|
||||
err = htvec_init(res.start, resource_size(&res),
|
||||
num_parents, parent_irq, of_node_to_fwnode(node));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
|
||||
irq_dispose:
|
||||
for (; i > 0; i--)
|
||||
irq_dispose_mapping(parent_irq[i - 1]);
|
||||
iounmap_base:
|
||||
iounmap(priv->base);
|
||||
free_priv:
|
||||
kfree(priv);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
IRQCHIP_DECLARE(htvec, "loongson,htvec-1.0", htvec_of_init);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static int __init pch_pic_parse_madt(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_madt_bio_pic *pchpic_entry = (struct acpi_madt_bio_pic *)header;
|
||||
|
||||
return pch_pic_acpi_init(htvec_priv->htvec_domain, pchpic_entry);
|
||||
}
|
||||
|
||||
static int __init pch_msi_parse_madt(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_madt_msi_pic *pchmsi_entry = (struct acpi_madt_msi_pic *)header;
|
||||
|
||||
return pch_msi_acpi_init(htvec_priv->htvec_domain, pchmsi_entry);
|
||||
}
|
||||
|
||||
static int __init acpi_cascade_irqdomain_init(void)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = acpi_table_parse_madt(ACPI_MADT_TYPE_BIO_PIC, pch_pic_parse_madt, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, pch_msi_parse_madt, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init htvec_acpi_init(struct irq_domain *parent,
|
||||
struct acpi_madt_ht_pic *acpi_htvec)
|
||||
{
|
||||
int i, ret;
|
||||
int num_parents, parent_irq[8];
|
||||
struct fwnode_handle *domain_handle;
|
||||
|
||||
if (!acpi_htvec)
|
||||
return -EINVAL;
|
||||
|
||||
num_parents = HTVEC_MAX_PARENT_IRQ;
|
||||
|
||||
domain_handle = irq_domain_alloc_fwnode(&acpi_htvec->address);
|
||||
if (!domain_handle) {
|
||||
pr_err("Unable to allocate domain handle\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Interrupt may come from any of the 8 interrupt lines */
|
||||
for (i = 0; i < HTVEC_MAX_PARENT_IRQ; i++)
|
||||
parent_irq[i] = irq_create_mapping(parent, acpi_htvec->cascade[i]);
|
||||
|
||||
ret = htvec_init(acpi_htvec->address, acpi_htvec->size,
|
||||
num_parents, parent_irq, domain_handle);
|
||||
|
||||
if (ret == 0)
|
||||
ret = acpi_cascade_irqdomain_init();
|
||||
else
|
||||
irq_domain_free_fwnode(domain_handle);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -167,7 +167,12 @@ static int liointc_domain_xlate(struct irq_domain *d, struct device_node *ctrlr,
|
|||
if (WARN_ON(intsize < 1))
|
||||
return -EINVAL;
|
||||
*out_hwirq = intspec[0] - GSI_MIN_CPU_IRQ;
|
||||
*out_type = IRQ_TYPE_NONE;
|
||||
|
||||
if (intsize > 1)
|
||||
*out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
|
||||
else
|
||||
*out_type = IRQ_TYPE_NONE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -349,6 +354,26 @@ IRQCHIP_DECLARE(loongson_liointc_2_0, "loongson,liointc-2.0", liointc_of_init);
|
|||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static int __init htintc_parse_madt(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_madt_ht_pic *htintc_entry = (struct acpi_madt_ht_pic *)header;
|
||||
struct irq_domain *parent = irq_find_matching_fwnode(liointc_handle, DOMAIN_BUS_ANY);
|
||||
|
||||
return htvec_acpi_init(parent, htintc_entry);
|
||||
}
|
||||
|
||||
static int __init acpi_cascade_irqdomain_init(void)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = acpi_table_parse_madt(ACPI_MADT_TYPE_HT_PIC, htintc_parse_madt, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init liointc_acpi_init(struct irq_domain *parent, struct acpi_madt_lio_pic *acpi_liointc)
|
||||
{
|
||||
int ret;
|
||||
|
|
@ -365,9 +390,12 @@ int __init liointc_acpi_init(struct irq_domain *parent, struct acpi_madt_lio_pic
|
|||
pr_err("Unable to allocate domain handle\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = liointc_init(acpi_liointc->address, acpi_liointc->size,
|
||||
1, domain_handle, NULL);
|
||||
if (ret)
|
||||
if (ret == 0)
|
||||
ret = acpi_cascade_irqdomain_init();
|
||||
else
|
||||
irq_domain_free_fwnode(domain_handle);
|
||||
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
|
||||
/* Registers */
|
||||
#define LPC_INT_CTL 0x00
|
||||
|
|
@ -34,6 +35,7 @@ struct pch_lpc {
|
|||
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)
|
||||
|
|
@ -147,6 +149,26 @@ static int pch_lpc_disabled(struct pch_lpc *priv)
|
|||
(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)
|
||||
{
|
||||
|
|
@ -191,7 +213,10 @@ int __init pch_lpc_acpi_init(struct irq_domain *parent,
|
|||
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:
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
|
||||
/* Registers */
|
||||
#define PCH_PIC_MASK 0x20
|
||||
|
|
@ -42,6 +43,9 @@ struct pch_pic {
|
|||
raw_spinlock_t pic_lock;
|
||||
u32 vec_count;
|
||||
u32 gsi_base;
|
||||
u32 saved_vec_en[PIC_REG_COUNT];
|
||||
u32 saved_vec_pol[PIC_REG_COUNT];
|
||||
u32 saved_vec_edge[PIC_REG_COUNT];
|
||||
};
|
||||
|
||||
static struct pch_pic *pch_pic_priv[MAX_IO_PICS];
|
||||
|
|
@ -145,6 +149,7 @@ static struct irq_chip pch_pic_irq_chip = {
|
|||
.irq_ack = pch_pic_ack_irq,
|
||||
.irq_set_affinity = irq_chip_set_affinity_parent,
|
||||
.irq_set_type = pch_pic_set_type,
|
||||
.flags = IRQCHIP_SKIP_SET_WAKE,
|
||||
};
|
||||
|
||||
static int pch_pic_domain_translate(struct irq_domain *d,
|
||||
|
|
@ -155,15 +160,21 @@ static int pch_pic_domain_translate(struct irq_domain *d,
|
|||
struct pch_pic *priv = d->host_data;
|
||||
struct device_node *of_node = to_of_node(fwspec->fwnode);
|
||||
|
||||
if (fwspec->param_count < 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (of_node) {
|
||||
if (fwspec->param_count < 2)
|
||||
return -EINVAL;
|
||||
|
||||
*hwirq = fwspec->param[0] + priv->ht_vec_base;
|
||||
*type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
|
||||
} else {
|
||||
if (fwspec->param_count < 1)
|
||||
return -EINVAL;
|
||||
|
||||
*hwirq = fwspec->param[0] - priv->gsi_base;
|
||||
*type = IRQ_TYPE_NONE;
|
||||
if (fwspec->param_count > 1)
|
||||
*type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
|
||||
else
|
||||
*type = IRQ_TYPE_NONE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -228,6 +239,46 @@ static void pch_pic_reset(struct pch_pic *priv)
|
|||
}
|
||||
}
|
||||
|
||||
static int pch_pic_suspend(void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < nr_pics; i++) {
|
||||
for (j = 0; j < PIC_REG_COUNT; j++) {
|
||||
pch_pic_priv[i]->saved_vec_pol[j] =
|
||||
readl(pch_pic_priv[i]->base + PCH_PIC_POL + 4 * j);
|
||||
pch_pic_priv[i]->saved_vec_edge[j] =
|
||||
readl(pch_pic_priv[i]->base + PCH_PIC_EDGE + 4 * j);
|
||||
pch_pic_priv[i]->saved_vec_en[j] =
|
||||
readl(pch_pic_priv[i]->base + PCH_PIC_MASK + 4 * j);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pch_pic_resume(void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < nr_pics; i++) {
|
||||
pch_pic_reset(pch_pic_priv[i]);
|
||||
for (j = 0; j < PIC_REG_COUNT; j++) {
|
||||
writel(pch_pic_priv[i]->saved_vec_pol[j],
|
||||
pch_pic_priv[i]->base + PCH_PIC_POL + 4 * j);
|
||||
writel(pch_pic_priv[i]->saved_vec_edge[j],
|
||||
pch_pic_priv[i]->base + PCH_PIC_EDGE + 4 * j);
|
||||
writel(pch_pic_priv[i]->saved_vec_en[j],
|
||||
pch_pic_priv[i]->base + PCH_PIC_MASK + 4 * j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct syscore_ops pch_pic_syscore_ops = {
|
||||
.suspend = pch_pic_suspend,
|
||||
.resume = pch_pic_resume,
|
||||
};
|
||||
|
||||
static int pch_pic_init(phys_addr_t addr, unsigned long size, int vec_base,
|
||||
struct irq_domain *parent_domain, struct fwnode_handle *domain_handle,
|
||||
u32 gsi_base)
|
||||
|
|
@ -260,6 +311,8 @@ static int pch_pic_init(phys_addr_t addr, unsigned long size, int vec_base,
|
|||
pch_pic_handle[nr_pics] = domain_handle;
|
||||
pch_pic_priv[nr_pics++] = priv;
|
||||
|
||||
register_syscore_ops(&pch_pic_syscore_ops);
|
||||
|
||||
return 0;
|
||||
|
||||
iounmap_base:
|
||||
|
|
@ -325,9 +378,8 @@ int find_pch_pic(u32 gsi)
|
|||
return -1;
|
||||
}
|
||||
|
||||
static int __init
|
||||
pch_lpc_parse_madt(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
static int __init pch_lpc_parse_madt(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_madt_lpc_pic *pchlpc_entry = (struct acpi_madt_lpc_pic *)header;
|
||||
|
||||
|
|
@ -336,8 +388,12 @@ pch_lpc_parse_madt(union acpi_subtable_headers *header,
|
|||
|
||||
static int __init acpi_cascade_irqdomain_init(void)
|
||||
{
|
||||
acpi_table_parse_madt(ACPI_MADT_TYPE_LPC_PIC,
|
||||
pch_lpc_parse_madt, 0);
|
||||
int r;
|
||||
|
||||
r = acpi_table_parse_madt(ACPI_MADT_TYPE_LPC_PIC, pch_lpc_parse_madt, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -364,7 +420,7 @@ int __init pch_pic_acpi_init(struct irq_domain *parent,
|
|||
}
|
||||
|
||||
if (acpi_pchpic->id == 0)
|
||||
acpi_cascade_irqdomain_init();
|
||||
ret = acpi_cascade_irqdomain_init();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user