mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-21 08:46:49 +07:00
d7c2878cfc
There are no current callers of these functions, and they use the ppc-specific virq_to_hw(). So removing them gets us one step closer to building QE support for ARM. If the functionality is ever actually needed, the code can be dug out of git and then adapted to work on all architectures, but for future reference please note that I believe qe_ic_set_priority is buggy: The "priority < 4" should be "priority <= 4", and in the else branch 24 should be replaced by 28, at least if I'm reading the data sheet right. Reviewed-by: Timur Tabi <timur@kernel.org> Signed-off-by: Rasmus Villemoes <linux@rasmusvillemoes.dk> Signed-off-by: Li Yang <leoyang.li@nxp.com>
448 lines
10 KiB
C
448 lines
10 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* arch/powerpc/sysdev/qe_lib/qe_ic.c
|
|
*
|
|
* Copyright (C) 2006 Freescale Semiconductor, Inc. All rights reserved.
|
|
*
|
|
* Author: Li Yang <leoli@freescale.com>
|
|
* Based on code from Shlomi Gridish <gridish@freescale.com>
|
|
*
|
|
* QUICC ENGINE Interrupt Controller
|
|
*/
|
|
|
|
#include <linux/of_irq.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/reboot.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/stddef.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/signal.h>
|
|
#include <linux/device.h>
|
|
#include <linux/spinlock.h>
|
|
#include <asm/irq.h>
|
|
#include <asm/io.h>
|
|
#include <soc/fsl/qe/qe.h>
|
|
#include <soc/fsl/qe/qe_ic.h>
|
|
|
|
#include "qe_ic.h"
|
|
|
|
static DEFINE_RAW_SPINLOCK(qe_ic_lock);
|
|
|
|
static struct qe_ic_info qe_ic_info[] = {
|
|
[1] = {
|
|
.mask = 0x00008000,
|
|
.mask_reg = QEIC_CIMR,
|
|
.pri_code = 0,
|
|
.pri_reg = QEIC_CIPWCC,
|
|
},
|
|
[2] = {
|
|
.mask = 0x00004000,
|
|
.mask_reg = QEIC_CIMR,
|
|
.pri_code = 1,
|
|
.pri_reg = QEIC_CIPWCC,
|
|
},
|
|
[3] = {
|
|
.mask = 0x00002000,
|
|
.mask_reg = QEIC_CIMR,
|
|
.pri_code = 2,
|
|
.pri_reg = QEIC_CIPWCC,
|
|
},
|
|
[10] = {
|
|
.mask = 0x00000040,
|
|
.mask_reg = QEIC_CIMR,
|
|
.pri_code = 1,
|
|
.pri_reg = QEIC_CIPZCC,
|
|
},
|
|
[11] = {
|
|
.mask = 0x00000020,
|
|
.mask_reg = QEIC_CIMR,
|
|
.pri_code = 2,
|
|
.pri_reg = QEIC_CIPZCC,
|
|
},
|
|
[12] = {
|
|
.mask = 0x00000010,
|
|
.mask_reg = QEIC_CIMR,
|
|
.pri_code = 3,
|
|
.pri_reg = QEIC_CIPZCC,
|
|
},
|
|
[13] = {
|
|
.mask = 0x00000008,
|
|
.mask_reg = QEIC_CIMR,
|
|
.pri_code = 4,
|
|
.pri_reg = QEIC_CIPZCC,
|
|
},
|
|
[14] = {
|
|
.mask = 0x00000004,
|
|
.mask_reg = QEIC_CIMR,
|
|
.pri_code = 5,
|
|
.pri_reg = QEIC_CIPZCC,
|
|
},
|
|
[15] = {
|
|
.mask = 0x00000002,
|
|
.mask_reg = QEIC_CIMR,
|
|
.pri_code = 6,
|
|
.pri_reg = QEIC_CIPZCC,
|
|
},
|
|
[20] = {
|
|
.mask = 0x10000000,
|
|
.mask_reg = QEIC_CRIMR,
|
|
.pri_code = 3,
|
|
.pri_reg = QEIC_CIPRTA,
|
|
},
|
|
[25] = {
|
|
.mask = 0x00800000,
|
|
.mask_reg = QEIC_CRIMR,
|
|
.pri_code = 0,
|
|
.pri_reg = QEIC_CIPRTB,
|
|
},
|
|
[26] = {
|
|
.mask = 0x00400000,
|
|
.mask_reg = QEIC_CRIMR,
|
|
.pri_code = 1,
|
|
.pri_reg = QEIC_CIPRTB,
|
|
},
|
|
[27] = {
|
|
.mask = 0x00200000,
|
|
.mask_reg = QEIC_CRIMR,
|
|
.pri_code = 2,
|
|
.pri_reg = QEIC_CIPRTB,
|
|
},
|
|
[28] = {
|
|
.mask = 0x00100000,
|
|
.mask_reg = QEIC_CRIMR,
|
|
.pri_code = 3,
|
|
.pri_reg = QEIC_CIPRTB,
|
|
},
|
|
[32] = {
|
|
.mask = 0x80000000,
|
|
.mask_reg = QEIC_CIMR,
|
|
.pri_code = 0,
|
|
.pri_reg = QEIC_CIPXCC,
|
|
},
|
|
[33] = {
|
|
.mask = 0x40000000,
|
|
.mask_reg = QEIC_CIMR,
|
|
.pri_code = 1,
|
|
.pri_reg = QEIC_CIPXCC,
|
|
},
|
|
[34] = {
|
|
.mask = 0x20000000,
|
|
.mask_reg = QEIC_CIMR,
|
|
.pri_code = 2,
|
|
.pri_reg = QEIC_CIPXCC,
|
|
},
|
|
[35] = {
|
|
.mask = 0x10000000,
|
|
.mask_reg = QEIC_CIMR,
|
|
.pri_code = 3,
|
|
.pri_reg = QEIC_CIPXCC,
|
|
},
|
|
[36] = {
|
|
.mask = 0x08000000,
|
|
.mask_reg = QEIC_CIMR,
|
|
.pri_code = 4,
|
|
.pri_reg = QEIC_CIPXCC,
|
|
},
|
|
[40] = {
|
|
.mask = 0x00800000,
|
|
.mask_reg = QEIC_CIMR,
|
|
.pri_code = 0,
|
|
.pri_reg = QEIC_CIPYCC,
|
|
},
|
|
[41] = {
|
|
.mask = 0x00400000,
|
|
.mask_reg = QEIC_CIMR,
|
|
.pri_code = 1,
|
|
.pri_reg = QEIC_CIPYCC,
|
|
},
|
|
[42] = {
|
|
.mask = 0x00200000,
|
|
.mask_reg = QEIC_CIMR,
|
|
.pri_code = 2,
|
|
.pri_reg = QEIC_CIPYCC,
|
|
},
|
|
[43] = {
|
|
.mask = 0x00100000,
|
|
.mask_reg = QEIC_CIMR,
|
|
.pri_code = 3,
|
|
.pri_reg = QEIC_CIPYCC,
|
|
},
|
|
};
|
|
|
|
static inline u32 qe_ic_read(__be32 __iomem *base, unsigned int reg)
|
|
{
|
|
return qe_ioread32be(base + (reg >> 2));
|
|
}
|
|
|
|
static inline void qe_ic_write(__be32 __iomem *base, unsigned int reg,
|
|
u32 value)
|
|
{
|
|
qe_iowrite32be(value, base + (reg >> 2));
|
|
}
|
|
|
|
static inline struct qe_ic *qe_ic_from_irq(unsigned int virq)
|
|
{
|
|
return irq_get_chip_data(virq);
|
|
}
|
|
|
|
static inline struct qe_ic *qe_ic_from_irq_data(struct irq_data *d)
|
|
{
|
|
return irq_data_get_irq_chip_data(d);
|
|
}
|
|
|
|
static void qe_ic_unmask_irq(struct irq_data *d)
|
|
{
|
|
struct qe_ic *qe_ic = qe_ic_from_irq_data(d);
|
|
unsigned int src = irqd_to_hwirq(d);
|
|
unsigned long flags;
|
|
u32 temp;
|
|
|
|
raw_spin_lock_irqsave(&qe_ic_lock, flags);
|
|
|
|
temp = qe_ic_read(qe_ic->regs, qe_ic_info[src].mask_reg);
|
|
qe_ic_write(qe_ic->regs, qe_ic_info[src].mask_reg,
|
|
temp | qe_ic_info[src].mask);
|
|
|
|
raw_spin_unlock_irqrestore(&qe_ic_lock, flags);
|
|
}
|
|
|
|
static void qe_ic_mask_irq(struct irq_data *d)
|
|
{
|
|
struct qe_ic *qe_ic = qe_ic_from_irq_data(d);
|
|
unsigned int src = irqd_to_hwirq(d);
|
|
unsigned long flags;
|
|
u32 temp;
|
|
|
|
raw_spin_lock_irqsave(&qe_ic_lock, flags);
|
|
|
|
temp = qe_ic_read(qe_ic->regs, qe_ic_info[src].mask_reg);
|
|
qe_ic_write(qe_ic->regs, qe_ic_info[src].mask_reg,
|
|
temp & ~qe_ic_info[src].mask);
|
|
|
|
/* Flush the above write before enabling interrupts; otherwise,
|
|
* spurious interrupts will sometimes happen. To be 100% sure
|
|
* that the write has reached the device before interrupts are
|
|
* enabled, the mask register would have to be read back; however,
|
|
* this is not required for correctness, only to avoid wasting
|
|
* time on a large number of spurious interrupts. In testing,
|
|
* a sync reduced the observed spurious interrupts to zero.
|
|
*/
|
|
mb();
|
|
|
|
raw_spin_unlock_irqrestore(&qe_ic_lock, flags);
|
|
}
|
|
|
|
static struct irq_chip qe_ic_irq_chip = {
|
|
.name = "QEIC",
|
|
.irq_unmask = qe_ic_unmask_irq,
|
|
.irq_mask = qe_ic_mask_irq,
|
|
.irq_mask_ack = qe_ic_mask_irq,
|
|
};
|
|
|
|
static int qe_ic_host_match(struct irq_domain *h, struct device_node *node,
|
|
enum irq_domain_bus_token bus_token)
|
|
{
|
|
/* Exact match, unless qe_ic node is NULL */
|
|
struct device_node *of_node = irq_domain_get_of_node(h);
|
|
return of_node == NULL || of_node == node;
|
|
}
|
|
|
|
static int qe_ic_host_map(struct irq_domain *h, unsigned int virq,
|
|
irq_hw_number_t hw)
|
|
{
|
|
struct qe_ic *qe_ic = h->host_data;
|
|
struct irq_chip *chip;
|
|
|
|
if (hw >= ARRAY_SIZE(qe_ic_info)) {
|
|
pr_err("%s: Invalid hw irq number for QEIC\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (qe_ic_info[hw].mask == 0) {
|
|
printk(KERN_ERR "Can't map reserved IRQ\n");
|
|
return -EINVAL;
|
|
}
|
|
/* Default chip */
|
|
chip = &qe_ic->hc_irq;
|
|
|
|
irq_set_chip_data(virq, qe_ic);
|
|
irq_set_status_flags(virq, IRQ_LEVEL);
|
|
|
|
irq_set_chip_and_handler(virq, chip, handle_level_irq);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct irq_domain_ops qe_ic_host_ops = {
|
|
.match = qe_ic_host_match,
|
|
.map = qe_ic_host_map,
|
|
.xlate = irq_domain_xlate_onetwocell,
|
|
};
|
|
|
|
/* Return an interrupt vector or NO_IRQ if no interrupt is pending. */
|
|
unsigned int qe_ic_get_low_irq(struct qe_ic *qe_ic)
|
|
{
|
|
int irq;
|
|
|
|
BUG_ON(qe_ic == NULL);
|
|
|
|
/* get the interrupt source vector. */
|
|
irq = qe_ic_read(qe_ic->regs, QEIC_CIVEC) >> 26;
|
|
|
|
if (irq == 0)
|
|
return NO_IRQ;
|
|
|
|
return irq_linear_revmap(qe_ic->irqhost, irq);
|
|
}
|
|
|
|
/* Return an interrupt vector or NO_IRQ if no interrupt is pending. */
|
|
unsigned int qe_ic_get_high_irq(struct qe_ic *qe_ic)
|
|
{
|
|
int irq;
|
|
|
|
BUG_ON(qe_ic == NULL);
|
|
|
|
/* get the interrupt source vector. */
|
|
irq = qe_ic_read(qe_ic->regs, QEIC_CHIVEC) >> 26;
|
|
|
|
if (irq == 0)
|
|
return NO_IRQ;
|
|
|
|
return irq_linear_revmap(qe_ic->irqhost, irq);
|
|
}
|
|
|
|
static void qe_ic_cascade_low(struct irq_desc *desc)
|
|
{
|
|
struct qe_ic *qe_ic = irq_desc_get_handler_data(desc);
|
|
unsigned int cascade_irq = qe_ic_get_low_irq(qe_ic);
|
|
struct irq_chip *chip = irq_desc_get_chip(desc);
|
|
|
|
if (cascade_irq != NO_IRQ)
|
|
generic_handle_irq(cascade_irq);
|
|
|
|
if (chip->irq_eoi)
|
|
chip->irq_eoi(&desc->irq_data);
|
|
}
|
|
|
|
static void qe_ic_cascade_high(struct irq_desc *desc)
|
|
{
|
|
struct qe_ic *qe_ic = irq_desc_get_handler_data(desc);
|
|
unsigned int cascade_irq = qe_ic_get_high_irq(qe_ic);
|
|
struct irq_chip *chip = irq_desc_get_chip(desc);
|
|
|
|
if (cascade_irq != NO_IRQ)
|
|
generic_handle_irq(cascade_irq);
|
|
|
|
if (chip->irq_eoi)
|
|
chip->irq_eoi(&desc->irq_data);
|
|
}
|
|
|
|
static void qe_ic_cascade_muxed_mpic(struct irq_desc *desc)
|
|
{
|
|
struct qe_ic *qe_ic = irq_desc_get_handler_data(desc);
|
|
unsigned int cascade_irq;
|
|
struct irq_chip *chip = irq_desc_get_chip(desc);
|
|
|
|
cascade_irq = qe_ic_get_high_irq(qe_ic);
|
|
if (cascade_irq == NO_IRQ)
|
|
cascade_irq = qe_ic_get_low_irq(qe_ic);
|
|
|
|
if (cascade_irq != NO_IRQ)
|
|
generic_handle_irq(cascade_irq);
|
|
|
|
chip->irq_eoi(&desc->irq_data);
|
|
}
|
|
|
|
static void __init qe_ic_init(struct device_node *node, unsigned int flags)
|
|
{
|
|
void (*low_handler)(struct irq_desc *desc);
|
|
void (*high_handler)(struct irq_desc *desc);
|
|
struct qe_ic *qe_ic;
|
|
struct resource res;
|
|
u32 temp = 0, ret;
|
|
|
|
ret = of_address_to_resource(node, 0, &res);
|
|
if (ret)
|
|
return;
|
|
|
|
qe_ic = kzalloc(sizeof(*qe_ic), GFP_KERNEL);
|
|
if (qe_ic == NULL)
|
|
return;
|
|
|
|
qe_ic->irqhost = irq_domain_add_linear(node, NR_QE_IC_INTS,
|
|
&qe_ic_host_ops, qe_ic);
|
|
if (qe_ic->irqhost == NULL) {
|
|
kfree(qe_ic);
|
|
return;
|
|
}
|
|
|
|
qe_ic->regs = ioremap(res.start, resource_size(&res));
|
|
|
|
qe_ic->hc_irq = qe_ic_irq_chip;
|
|
|
|
qe_ic->virq_high = irq_of_parse_and_map(node, 0);
|
|
qe_ic->virq_low = irq_of_parse_and_map(node, 1);
|
|
|
|
if (qe_ic->virq_low == NO_IRQ) {
|
|
printk(KERN_ERR "Failed to map QE_IC low IRQ\n");
|
|
kfree(qe_ic);
|
|
return;
|
|
}
|
|
if (qe_ic->virq_high != qe_ic->virq_low) {
|
|
low_handler = qe_ic_cascade_low;
|
|
high_handler = qe_ic_cascade_high;
|
|
} else {
|
|
low_handler = qe_ic_cascade_muxed_mpic;
|
|
high_handler = NULL;
|
|
}
|
|
|
|
/* default priority scheme is grouped. If spread mode is */
|
|
/* required, configure cicr accordingly. */
|
|
if (flags & QE_IC_SPREADMODE_GRP_W)
|
|
temp |= CICR_GWCC;
|
|
if (flags & QE_IC_SPREADMODE_GRP_X)
|
|
temp |= CICR_GXCC;
|
|
if (flags & QE_IC_SPREADMODE_GRP_Y)
|
|
temp |= CICR_GYCC;
|
|
if (flags & QE_IC_SPREADMODE_GRP_Z)
|
|
temp |= CICR_GZCC;
|
|
if (flags & QE_IC_SPREADMODE_GRP_RISCA)
|
|
temp |= CICR_GRTA;
|
|
if (flags & QE_IC_SPREADMODE_GRP_RISCB)
|
|
temp |= CICR_GRTB;
|
|
|
|
/* choose destination signal for highest priority interrupt */
|
|
if (flags & QE_IC_HIGH_SIGNAL)
|
|
temp |= (SIGNAL_HIGH << CICR_HPIT_SHIFT);
|
|
|
|
qe_ic_write(qe_ic->regs, QEIC_CICR, temp);
|
|
|
|
irq_set_handler_data(qe_ic->virq_low, qe_ic);
|
|
irq_set_chained_handler(qe_ic->virq_low, low_handler);
|
|
|
|
if (qe_ic->virq_high != NO_IRQ &&
|
|
qe_ic->virq_high != qe_ic->virq_low) {
|
|
irq_set_handler_data(qe_ic->virq_high, qe_ic);
|
|
irq_set_chained_handler(qe_ic->virq_high, high_handler);
|
|
}
|
|
}
|
|
|
|
static int __init qe_ic_of_init(void)
|
|
{
|
|
struct device_node *np;
|
|
|
|
np = of_find_compatible_node(NULL, NULL, "fsl,qe-ic");
|
|
if (!np) {
|
|
np = of_find_node_by_type(NULL, "qeic");
|
|
if (!np)
|
|
return -ENODEV;
|
|
}
|
|
qe_ic_init(np, 0);
|
|
of_node_put(np);
|
|
return 0;
|
|
}
|
|
subsys_initcall(qe_ic_of_init);
|