214 lines
4.8 KiB
C
214 lines
4.8 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
#include <linux/kconfig.h>
|
|
#include <linux/pci.h>
|
|
|
|
#include <asm/hw_init.h>
|
|
#include <asm/irq_impl.h>
|
|
#include <asm/pmc.h>
|
|
#include <asm/sw64_init.h>
|
|
|
|
static void handle_intx(unsigned int offset)
|
|
{
|
|
struct pci_controller *hose;
|
|
unsigned long value;
|
|
|
|
hose = hose_head;
|
|
for (hose = hose_head; hose; hose = hose->next) {
|
|
value = read_piu_ior0(hose->node, hose->index, INTACONFIG + (offset << 7));
|
|
if (value >> 63) {
|
|
value = value & (~(1UL << 62));
|
|
write_piu_ior0(hose->node, hose->index, INTACONFIG + (offset << 7), value);
|
|
handle_irq(hose->int_irq);
|
|
value = value | (1UL << 62);
|
|
write_piu_ior0(hose->node, hose->index, INTACONFIG + (offset << 7), value);
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_PCIE_PME)) {
|
|
value = read_piu_ior0(hose->node, hose->index, PMEINTCONFIG);
|
|
if (value >> 63) {
|
|
handle_irq(hose->service_irq);
|
|
write_piu_ior0(hose->node, hose->index, PMEINTCONFIG, value);
|
|
}
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_PCIEAER)) {
|
|
value = read_piu_ior0(hose->node, hose->index, AERERRINTCONFIG);
|
|
if (value >> 63) {
|
|
handle_irq(hose->service_irq);
|
|
write_piu_ior0(hose->node, hose->index, AERERRINTCONFIG, value);
|
|
}
|
|
}
|
|
|
|
if (hose->iommu_enable) {
|
|
value = read_piu_ior0(hose->node, hose->index, IOMMUEXCPT_STATUS);
|
|
if (value >> 63)
|
|
handle_irq(hose->int_irq);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void handle_device_interrupt(unsigned long irq_info)
|
|
{
|
|
unsigned int i;
|
|
|
|
if (is_guest_or_emul()) {
|
|
handle_irq(irq_info);
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
if ((irq_info >> i) & 0x1)
|
|
handle_intx(i);
|
|
}
|
|
}
|
|
|
|
/* Performance counter hook. A module can override this to do something useful. */
|
|
static void dummy_perf(unsigned long vector, struct pt_regs *regs)
|
|
{
|
|
irq_err_count++;
|
|
pr_crit("Performance counter interrupt!\n");
|
|
}
|
|
|
|
void (*perf_irq)(unsigned long vector, struct pt_regs *regs) = dummy_perf;
|
|
EXPORT_SYMBOL(perf_irq);
|
|
|
|
static void handle_fault_int(void)
|
|
{
|
|
int node;
|
|
unsigned long value;
|
|
|
|
node = __this_cpu_read(hard_node_id);
|
|
pr_info("enter fault int, si_fault_stat = %#lx\n",
|
|
sw64_io_read(node, SI_FAULT_STAT));
|
|
sw64_io_write(node, SI_FAULT_INT_EN, 0);
|
|
sw64_io_write(node, DLI_RLTD_FAULT_INTEN, 0);
|
|
#if defined(CONFIG_UNCORE_XUELANG)
|
|
value = 0;
|
|
#elif defined(CONFIG_UNCORE_JUNZHANG)
|
|
value = sw64_io_read(node, FAULT_INT_CONFIG);
|
|
value |= (1 << 8);
|
|
#endif
|
|
__io_write_fault_int_en(node, value);
|
|
}
|
|
|
|
static void handle_mt_int(void)
|
|
{
|
|
pr_info("enter mt int\n");
|
|
}
|
|
|
|
static void handle_nmi_int(void)
|
|
{
|
|
pr_info("enter nmi int\n");
|
|
}
|
|
|
|
static void handle_dev_int(struct pt_regs *regs)
|
|
{
|
|
unsigned long config_val, val, stat;
|
|
int node = 0;
|
|
unsigned int hwirq;
|
|
|
|
config_val = sw64_io_read(node, DEV_INT_CONFIG);
|
|
val = config_val & (~(1UL << 8));
|
|
sw64_io_write(node, DEV_INT_CONFIG, val);
|
|
stat = sw64_io_read(node, MCU_DVC_INT);
|
|
|
|
while (stat) {
|
|
hwirq = ffs(stat) - 1;
|
|
generic_handle_domain_irq(NULL, hwirq);
|
|
stat &= ~(1UL << hwirq);
|
|
}
|
|
/*do handle irq */
|
|
|
|
sw64_io_write(node, DEV_INT_CONFIG, config_val);
|
|
}
|
|
|
|
asmlinkage void do_entInt(unsigned long type, unsigned long vector,
|
|
unsigned long irq_arg, struct pt_regs *regs)
|
|
{
|
|
struct pt_regs *old_regs;
|
|
extern char __idle_start[], __idle_end[];
|
|
|
|
if (is_guest_or_emul()) {
|
|
if ((type & 0xffff) > 15) {
|
|
vector = type;
|
|
if (vector == 16)
|
|
type = INT_INTx;
|
|
else
|
|
type = INT_MSI;
|
|
}
|
|
}
|
|
|
|
/* restart idle routine if it is interrupted */
|
|
if (regs->pc > (u64)__idle_start && regs->pc < (u64)__idle_end)
|
|
regs->pc = (u64)__idle_start;
|
|
|
|
switch (type & 0xffff) {
|
|
case INT_MSI:
|
|
old_regs = set_irq_regs(regs);
|
|
handle_pci_msi_interrupt(type, vector, irq_arg);
|
|
set_irq_regs(old_regs);
|
|
return;
|
|
case INT_INTx:
|
|
old_regs = set_irq_regs(regs);
|
|
handle_device_interrupt(vector);
|
|
set_irq_regs(old_regs);
|
|
return;
|
|
|
|
case INT_IPI:
|
|
#ifdef CONFIG_SMP
|
|
handle_ipi(regs);
|
|
return;
|
|
#else
|
|
irq_err_count++;
|
|
pr_crit("Interprocessor interrupt? You must be kidding!\n");
|
|
#endif
|
|
break;
|
|
case INT_RTC:
|
|
old_regs = set_irq_regs(regs);
|
|
sw64_timer_interrupt();
|
|
set_irq_regs(old_regs);
|
|
return;
|
|
case INT_VT_SERIAL:
|
|
old_regs = set_irq_regs(regs);
|
|
handle_irq(type);
|
|
set_irq_regs(old_regs);
|
|
return;
|
|
case INT_VT_HOTPLUG:
|
|
old_regs = set_irq_regs(regs);
|
|
handle_irq(type);
|
|
set_irq_regs(old_regs);
|
|
return;
|
|
case INT_PC0:
|
|
perf_irq(PMC_PC0, regs);
|
|
return;
|
|
case INT_PC1:
|
|
perf_irq(PMC_PC1, regs);
|
|
return;
|
|
case INT_DEV:
|
|
old_regs = set_irq_regs(regs);
|
|
handle_dev_int(regs);
|
|
set_irq_regs(old_regs);
|
|
return;
|
|
case INT_FAULT:
|
|
old_regs = set_irq_regs(regs);
|
|
handle_fault_int();
|
|
set_irq_regs(old_regs);
|
|
return;
|
|
case INT_MT:
|
|
old_regs = set_irq_regs(regs);
|
|
handle_mt_int();
|
|
set_irq_regs(old_regs);
|
|
return;
|
|
case INT_NMI:
|
|
old_regs = set_irq_regs(regs);
|
|
handle_nmi_int();
|
|
set_irq_regs(old_regs);
|
|
return;
|
|
default:
|
|
pr_crit("Hardware intr %ld %lx? uh?\n", type, vector);
|
|
}
|
|
pr_crit("PC = %016lx PS = %04lx\n", regs->pc, regs->ps);
|
|
}
|
|
EXPORT_SYMBOL(do_entInt);
|