744 lines
19 KiB
C
744 lines
19 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
#include <linux/pci.h>
|
|
#include <linux/clocksource.h>
|
|
|
|
#include <asm/sw64_init.h>
|
|
#include <asm/sw64io.h>
|
|
#include <asm/pci.h>
|
|
#include <asm/irq_impl.h>
|
|
#include <asm/wrperfmon.h>
|
|
#include "../../../../drivers/pci/pci.h"
|
|
|
|
static u64 read_longtime(struct clocksource *cs)
|
|
{
|
|
u64 result;
|
|
unsigned long node;
|
|
|
|
if (IS_ENABLED(CONFIG_SW64_FPGA) || IS_ENABLED(CONFIG_SW64_SIM))
|
|
node = 0;
|
|
else
|
|
node = __this_cpu_read(hard_node_id);
|
|
result = sw64_io_read(node, LONG_TIME);
|
|
|
|
return result;
|
|
}
|
|
|
|
static int longtime_enable(struct clocksource *cs)
|
|
{
|
|
switch (cpu_desc.model) {
|
|
case CPU_SW3231:
|
|
sw64_io_write(0, GPIO_SWPORTA_DR, 0);
|
|
sw64_io_write(0, GPIO_SWPORTA_DDR, 0xff);
|
|
break;
|
|
case CPU_SW831:
|
|
sw64_io_write(0, LONG_TIME_START_EN, 0x1);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct clocksource clocksource_longtime = {
|
|
.name = "longtime",
|
|
.rating = 100,
|
|
.enable = longtime_enable,
|
|
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
|
.mask = CLOCKSOURCE_MASK(64),
|
|
.shift = 0,
|
|
.mult = 0,
|
|
.read = read_longtime,
|
|
};
|
|
|
|
static u64 read_vtime(struct clocksource *cs)
|
|
{
|
|
u64 result;
|
|
unsigned long vtime_addr = PAGE_OFFSET | IO_BASE | LONG_TIME;
|
|
|
|
result = rdio64(vtime_addr);
|
|
return result;
|
|
}
|
|
|
|
static int vtime_enable(struct clocksource *cs)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static struct clocksource clocksource_vtime = {
|
|
.name = "vtime",
|
|
.rating = 100,
|
|
.enable = vtime_enable,
|
|
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
|
.mask = CLOCKSOURCE_MASK(64),
|
|
.shift = 0,
|
|
.mult = 0,
|
|
.read = read_vtime,
|
|
};
|
|
|
|
void setup_chip_clocksource(void)
|
|
{
|
|
#ifdef CONFIG_SW64_SIM
|
|
clocksource_register_khz(&clocksource_longtime, 400000); /* Hardware Simulator 400Mhz */
|
|
#elif defined(CONFIG_SW64_FPGA)
|
|
clocksource_register_khz(&clocksource_longtime, 1000); /* FPGA 1Mhz */
|
|
#else
|
|
if (is_in_host())
|
|
clocksource_register_khz(&clocksource_longtime, 25000);
|
|
else
|
|
clocksource_register_khz(&clocksource_vtime, 25000);
|
|
#endif
|
|
}
|
|
|
|
static int chip3_get_cpu_nums(void)
|
|
{
|
|
unsigned long trkmode;
|
|
int cpus;
|
|
|
|
if (is_guest_or_emul())
|
|
return 1;
|
|
|
|
trkmode = sw64_io_read(0, TRKMODE);
|
|
trkmode = (trkmode >> 6) & 0x3;
|
|
cpus = 1 << trkmode;
|
|
|
|
return cpus;
|
|
}
|
|
|
|
static unsigned long chip3_get_vt_node_mem(int nodeid)
|
|
{
|
|
return *(unsigned long *)MMSIZE & MMSIZE_MASK;
|
|
}
|
|
|
|
static unsigned long chip3_get_node_mem(int nodeid)
|
|
{
|
|
unsigned long mc_config, mc_online, mc_cap, mc_num;
|
|
unsigned long node_mem;
|
|
|
|
mc_config = sw64_io_read(nodeid, MC_CAP_CFG) & 0xf;
|
|
mc_cap = (1UL << mc_config) << 28;
|
|
mc_online = sw64_io_read(nodeid, MC_ONLINE) & 0xff;
|
|
mc_num = __kernel_ctpop(mc_online);
|
|
node_mem = mc_cap * mc_num;
|
|
|
|
return node_mem;
|
|
}
|
|
|
|
static void chip3_setup_vt_core_start(struct cpumask *cpumask)
|
|
{
|
|
int i;
|
|
unsigned long coreonline;
|
|
|
|
coreonline = sw64_io_read(0, CORE_ONLINE);
|
|
|
|
for (i = 0; i < 64 ; i++) {
|
|
if (coreonline & (1UL << i))
|
|
cpumask_set_cpu(i, cpumask);
|
|
}
|
|
}
|
|
|
|
static void chip3_setup_core_start(struct cpumask *cpumask)
|
|
{
|
|
int i, j, cpus;
|
|
unsigned long coreonline;
|
|
|
|
cpus = chip3_get_cpu_nums();
|
|
for (i = 0; i < cpus; i++) {
|
|
coreonline = sw64_io_read(i, CORE_ONLINE);
|
|
for (j = 0; j < 32 ; j++) {
|
|
if (coreonline & (1UL << j))
|
|
cpumask_set_cpu(i * 32 + j, cpumask);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
int chip_pcie_configure(struct pci_controller *hose)
|
|
{
|
|
struct pci_dev *dev;
|
|
struct pci_bus *bus, *top;
|
|
struct list_head *next;
|
|
unsigned int max_read_size, smallest_max_payload;
|
|
int max_payloadsize, iov_bus = 0;
|
|
unsigned long rc_index, node;
|
|
unsigned long piuconfig0, value;
|
|
unsigned int pcie_caps_offset;
|
|
unsigned int rc_conf_value;
|
|
u16 devctl, new_values;
|
|
bool rc_ari_disabled = false, found = false;
|
|
|
|
node = hose->node;
|
|
rc_index = hose->index;
|
|
smallest_max_payload = read_rc_conf(node, rc_index, RC_EXP_DEVCAP);
|
|
smallest_max_payload &= PCI_EXP_DEVCAP_PAYLOAD;
|
|
|
|
top = hose->bus;
|
|
bus = top;
|
|
next = top->devices.next;
|
|
|
|
for (;;) {
|
|
if (next == &bus->devices) {
|
|
/* end of this bus, go up or finish */
|
|
if (bus == top)
|
|
break;
|
|
next = bus->self->bus_list.next;
|
|
bus = bus->self->bus;
|
|
continue;
|
|
}
|
|
dev = list_entry(next, struct pci_dev, bus_list);
|
|
if (dev->subordinate) {
|
|
/* this is a pci-pci bridge, do its devices next */
|
|
next = dev->subordinate->devices.next;
|
|
bus = dev->subordinate;
|
|
} else
|
|
next = dev->bus_list.next;
|
|
|
|
if (!found) {
|
|
if (pci_is_root_bus(dev->bus)) {
|
|
if (list_empty(&dev->subordinate->devices))
|
|
rc_ari_disabled = true;
|
|
} else {
|
|
if (!pci_ari_enabled(dev->bus)) {
|
|
rc_ari_disabled = true;
|
|
found = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_PCI_IOV
|
|
if (dev->is_physfn)
|
|
iov_bus += dev->sriov->max_VF_buses - dev->bus->number;
|
|
#endif
|
|
|
|
/* Query device PCIe capability register */
|
|
pcie_caps_offset = dev->pcie_cap;
|
|
if (pcie_caps_offset == 0)
|
|
continue;
|
|
max_payloadsize = dev->pcie_mpss;
|
|
if (max_payloadsize < smallest_max_payload)
|
|
smallest_max_payload = max_payloadsize;
|
|
}
|
|
|
|
if (rc_ari_disabled) {
|
|
rc_conf_value = read_rc_conf(node, rc_index, RC_EXP_DEVCTL2);
|
|
rc_conf_value &= ~PCI_EXP_DEVCTL2_ARI;
|
|
write_rc_conf(node, rc_index, RC_EXP_DEVCTL2, rc_conf_value);
|
|
} else {
|
|
rc_conf_value = read_rc_conf(node, rc_index, RC_EXP_DEVCTL2);
|
|
rc_conf_value |= PCI_EXP_DEVCTL2_ARI;
|
|
write_rc_conf(node, rc_index, RC_EXP_DEVCTL2, rc_conf_value);
|
|
}
|
|
|
|
rc_conf_value = read_rc_conf(node, rc_index, RC_EXP_DEVCAP);
|
|
rc_conf_value &= PCI_EXP_DEVCAP_PAYLOAD;
|
|
max_payloadsize = rc_conf_value;
|
|
if (max_payloadsize < smallest_max_payload)
|
|
smallest_max_payload = max_payloadsize;
|
|
|
|
max_read_size = 0x2; /* Limit to 512B */
|
|
value = read_rc_conf(node, rc_index, RC_EXP_DEVCTL);
|
|
value &= ~(PCI_EXP_DEVCTL_PAYLOAD | PCI_EXP_DEVCTL_READRQ);
|
|
value |= (max_read_size << 12) | (smallest_max_payload << 5);
|
|
write_rc_conf(node, rc_index, RC_EXP_DEVCTL, value);
|
|
new_values = (max_read_size << 12) | (smallest_max_payload << 5);
|
|
|
|
piuconfig0 = read_piu_ior0(node, rc_index, PIUCONFIG0);
|
|
piuconfig0 &= ~(0x7fUL << 9);
|
|
if (smallest_max_payload == 0x2) {
|
|
piuconfig0 |= (0x20UL << 9);
|
|
write_piu_ior0(node, rc_index, PIUCONFIG0, piuconfig0);
|
|
} else {
|
|
piuconfig0 |= (0x40UL << 9);
|
|
write_piu_ior0(node, rc_index, PIUCONFIG0, piuconfig0);
|
|
}
|
|
|
|
printk("Node%ld RC%ld MPSS %luB, MRRS %luB, Piuconfig0 %#lx, ARI %s\n",
|
|
node, rc_index, (1UL << smallest_max_payload) << 7,
|
|
(1UL << max_read_size) << 7, piuconfig0,
|
|
rc_ari_disabled ? "disabled" : "enabled");
|
|
|
|
/* Now, set the max_payload_size for all devices to that value. */
|
|
bus = top;
|
|
next = top->devices.next;
|
|
for (;;) {
|
|
if (next == &bus->devices) {
|
|
/* end of this bus, go up or finish */
|
|
if (bus == top)
|
|
break;
|
|
next = bus->self->bus_list.next;
|
|
bus = bus->self->bus;
|
|
continue;
|
|
}
|
|
dev = list_entry(next, struct pci_dev, bus_list);
|
|
if (dev->subordinate) {
|
|
/* this is a pci-pci bridge, do its devices next */
|
|
next = dev->subordinate->devices.next;
|
|
bus = dev->subordinate;
|
|
} else
|
|
next = dev->bus_list.next;
|
|
|
|
pcie_caps_offset = dev->pcie_cap;
|
|
if (pcie_caps_offset == 0)
|
|
continue;
|
|
|
|
pci_read_config_word(dev, pcie_caps_offset + PCI_EXP_DEVCTL, &devctl);
|
|
devctl &= ~(PCI_EXP_DEVCTL_PAYLOAD | PCI_EXP_DEVCTL_READRQ);
|
|
devctl |= new_values;
|
|
pci_write_config_word(dev, pcie_caps_offset + PCI_EXP_DEVCTL, devctl);
|
|
}
|
|
|
|
return iov_bus;
|
|
}
|
|
|
|
static int chip3_check_pci_vt_linkup(unsigned long node, unsigned long index)
|
|
{
|
|
if (node == 0 && index == 0)
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
static int chip3_check_pci_linkup(unsigned long node, unsigned long index)
|
|
{
|
|
unsigned long rc_debug;
|
|
|
|
#ifdef CONFIG_SW64_FPGA //for PCIE4.0
|
|
printk("waiting for link up...\n");
|
|
if (index == 0)
|
|
sw64_io_write(node, PIU_TOP0_CONFIG, 0x10011);
|
|
else
|
|
sw64_io_write(node, PIU_TOP1_CONFIG, 0x10011);
|
|
mdelay(10);
|
|
rc_debug = read_piu_ior1(node, index, RCDEBUGINF1);
|
|
while (!(rc_debug & 0x1)) {
|
|
udelay(10);
|
|
rc_debug = read_piu_ior1(node, index, RCDEBUGINF1);
|
|
}
|
|
mdelay(10);
|
|
#endif
|
|
#ifdef CONFIG_SW64_SIM
|
|
printk("waiting for link up...\n");
|
|
rc_debug = read_piu_ior1(node, index, RCDEBUGINF1);
|
|
while (!(rc_debug & 0x1)) {
|
|
udelay(10);
|
|
rc_debug = read_piu_ior1(node, index, RCDEBUGINF1);
|
|
}
|
|
#endif
|
|
rc_debug = read_piu_ior1(node, index, RCDEBUGINF1);
|
|
|
|
return !(rc_debug & 0x1);
|
|
}
|
|
|
|
static void chip3_set_rc_piu(unsigned long node, unsigned long index)
|
|
{
|
|
unsigned int i, value;
|
|
u32 rc_misc_ctrl;
|
|
|
|
if (is_guest_or_emul())
|
|
return;
|
|
|
|
/* configure RC, set PCI-E root controller */
|
|
write_rc_conf(node, index, RC_COMMAND, 0x00100007);
|
|
write_rc_conf(node, index, RC_PORT_LINK_CTL, 0x1f0020);
|
|
write_rc_conf(node, index, RC_EXP_DEVCTL, 0x2850);
|
|
write_rc_conf(node, index, RC_EXP_DEVCTL2, 0x6);
|
|
write_rc_conf(node, index, RC_ORDER_RULE_CTL, 0x0100);
|
|
|
|
if (IS_ENABLED(CONFIG_SUSPEND) && IS_ENABLED(CONFIG_SW64_SIM)) {
|
|
value = read_rc_conf(node, index, RC_LINK_STAT);
|
|
value |= 0x3;
|
|
write_rc_conf(node, index, RC_LINK_STAT, value);
|
|
}
|
|
|
|
/* enable DBI_RO_WR_EN */
|
|
rc_misc_ctrl = read_rc_conf(node, index, RC_MISC_CONTROL_1);
|
|
write_rc_conf(node, index, RC_MISC_CONTROL_1, rc_misc_ctrl | 0x1);
|
|
|
|
/* fix up DEVICE_ID_VENDOR_ID register */
|
|
value = (PCI_DEVICE_ID_CHIP3 << 16) | PCI_VENDOR_ID_JN;
|
|
write_rc_conf(node, index, RC_VENDOR_ID, value);
|
|
|
|
/* set PCI-E root class code */
|
|
value = read_rc_conf(node, index, RC_REVISION_ID);
|
|
write_rc_conf(node, index, RC_REVISION_ID, (PCI_CLASS_BRIDGE_HOST << 16) | value);
|
|
|
|
/* disable DBI_RO_WR_EN */
|
|
write_rc_conf(node, index, RC_MISC_CONTROL_1, rc_misc_ctrl);
|
|
|
|
write_rc_conf(node, index, RC_PRIMARY_BUS, 0xffffff);
|
|
write_piu_ior0(node, index, PIUCONFIG0, 0x38056);
|
|
write_piu_ior1(node, index, PIUCONFIG1, 0x2);
|
|
write_piu_ior1(node, index, ERRENABLE, -1);
|
|
|
|
/* set DMA offset value PCITODMA_OFFSET */
|
|
write_piu_ior0(node, index, EPDMABAR, PCITODMA_OFFSET);
|
|
if (IS_ENABLED(CONFIG_PCI_MSI)) {
|
|
write_piu_ior0(node, index, PIUCONFIG0, 0x38076);
|
|
write_piu_ior0(node, index, MSIADDR, MSIX_MSG_ADDR);
|
|
for (i = 0; i < 256; i++)
|
|
write_piu_ior0(node, index, MSICONFIG0 + (i << 7), 0);
|
|
}
|
|
}
|
|
|
|
static void chip3_set_intx(unsigned long node, unsigned long index,
|
|
unsigned long int_conf)
|
|
{
|
|
if (is_guest_or_emul())
|
|
return;
|
|
|
|
write_piu_ior0(node, index, INTACONFIG, int_conf | (0x8UL << 10));
|
|
write_piu_ior0(node, index, INTBCONFIG, int_conf | (0x4UL << 10));
|
|
write_piu_ior0(node, index, INTCCONFIG, int_conf | (0x2UL << 10));
|
|
write_piu_ior0(node, index, INTDCONFIG, int_conf | (0x1UL << 10));
|
|
}
|
|
|
|
static unsigned long chip3_get_rc_enable(unsigned long node)
|
|
{
|
|
unsigned long rc_enable;
|
|
|
|
if (is_guest_or_emul())
|
|
return 1;
|
|
|
|
if (!IS_ENABLED(CONFIG_SW64_ASIC)) {
|
|
rc_enable = 0x1;
|
|
sw64_io_write(node, IO_START, rc_enable);
|
|
}
|
|
rc_enable = sw64_io_read(node, IO_START);
|
|
|
|
return rc_enable;
|
|
}
|
|
|
|
static int chip3_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
|
|
{
|
|
struct pci_controller *hose = dev->sysdata;
|
|
|
|
if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT)
|
|
return hose->service_irq;
|
|
else
|
|
return hose->int_irq;
|
|
}
|
|
|
|
extern struct pci_controller *hose_head, **hose_tail;
|
|
static void sw6_handle_intx(unsigned int offset)
|
|
{
|
|
struct pci_controller *hose;
|
|
unsigned long value, pme_value, aer_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);
|
|
}
|
|
|
|
pme_value = read_piu_ior0(hose->node, hose->index, PMEINTCONFIG);
|
|
aer_value = read_piu_ior0(hose->node, hose->index, AERERRINTCONFIG);
|
|
if ((pme_value >> 63) || (aer_value >> 63)) {
|
|
handle_irq(hose->service_irq);
|
|
|
|
if (pme_value >> 63)
|
|
write_piu_ior0(hose->node, hose->index, PMEINTCONFIG, pme_value);
|
|
if (aer_value >> 63)
|
|
write_piu_ior0(hose->node, hose->index, AERERRINTCONFIG, aer_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 chip3_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)
|
|
sw6_handle_intx(i);
|
|
}
|
|
}
|
|
|
|
static void chip3_hose_init(struct pci_controller *hose)
|
|
{
|
|
unsigned long pci_io_base;
|
|
|
|
hose->sparse_mem_base = 0;
|
|
hose->sparse_io_base = 0;
|
|
pci_io_base = IO_BASE | (hose->node << IO_NODE_SHIFT)
|
|
| PCI_BASE | (hose->index << IO_RC_SHIFT);
|
|
|
|
hose->dense_mem_base = pci_io_base;
|
|
hose->dense_io_base = pci_io_base | PCI_LEGACY_IO;
|
|
hose->ep_config_space_base = PAGE_OFFSET | pci_io_base | PCI_EP_CFG;
|
|
hose->rc_config_space_base = PAGE_OFFSET | pci_io_base | PCI_RC_CFG;
|
|
|
|
hose->mem_space->start = pci_io_base + PCI_32BIT_MEMIO;
|
|
hose->mem_space->end = hose->mem_space->start + PCI_32BIT_MEMIO_SIZE - 1;
|
|
hose->mem_space->name = "pci memory space";
|
|
hose->mem_space->flags = IORESOURCE_MEM;
|
|
|
|
if (request_resource(&iomem_resource, hose->mem_space) < 0)
|
|
pr_err("Failed to request MEM on hose %ld\n", hose->index);
|
|
hose->pre_mem_space->start = pci_io_base | PCI_64BIT_MEMIO;
|
|
hose->pre_mem_space->end = hose->pre_mem_space->start + PCI_64BIT_MEMIO_SIZE - 1;
|
|
hose->pre_mem_space->name = "pci pre mem space";
|
|
hose->pre_mem_space->flags = IORESOURCE_MEM | IORESOURCE_PREFETCH | IORESOURCE_MEM_64;
|
|
|
|
if (request_resource(&iomem_resource, hose->pre_mem_space) < 0)
|
|
pr_err("Failed to request 64bit MEM on hose %ld\n", hose->index);
|
|
hose->io_space->start = pci_io_base | PCI_LEGACY_IO;
|
|
hose->io_space->end = hose->io_space->start + PCI_LEGACY_IO_SIZE - 1;
|
|
hose->io_space->name = "pci io space";
|
|
hose->io_space->flags = IORESOURCE_IO;
|
|
|
|
if (request_resource(&ioport_resource, hose->io_space) < 0)
|
|
pr_err("Failed to request IO on hose %ld\n", hose->index);
|
|
hose->busn_space->name = "PCI busn";
|
|
hose->busn_space->start = 0xff;
|
|
hose->busn_space->end = 0xff;
|
|
hose->busn_space->flags = IORESOURCE_BUS;
|
|
hose->first_busno = hose->self_busno = hose->busn_space->start;
|
|
hose->last_busno = hose->busn_space->end;
|
|
|
|
if (is_in_host()) {
|
|
if (IS_ENABLED(CONFIG_PCI_MSI))
|
|
memset(hose->piu_msiconfig, 0, 256/8);
|
|
}
|
|
};
|
|
|
|
static void chip3_init_ops_fixup(void)
|
|
{
|
|
if (is_guest_or_emul()) {
|
|
sw64_chip_init->early_init.setup_core_start = chip3_setup_vt_core_start;
|
|
sw64_chip_init->early_init.get_node_mem = chip3_get_vt_node_mem;
|
|
sw64_chip_init->pci_init.check_pci_linkup = chip3_check_pci_vt_linkup;
|
|
}
|
|
};
|
|
|
|
static void chip3_ops_fixup(void)
|
|
{
|
|
if (is_guest_or_emul())
|
|
sw64_chip->suspend = NULL;
|
|
};
|
|
|
|
static struct sw64_chip_init_ops chip3_chip_init_ops = {
|
|
.early_init = {
|
|
.setup_core_start = chip3_setup_core_start,
|
|
.get_node_mem = chip3_get_node_mem,
|
|
},
|
|
.pci_init = {
|
|
.map_irq = chip3_map_irq,
|
|
.get_rc_enable = chip3_get_rc_enable,
|
|
.hose_init = chip3_hose_init,
|
|
.set_rc_piu = chip3_set_rc_piu,
|
|
.check_pci_linkup = chip3_check_pci_linkup,
|
|
.set_intx = chip3_set_intx,
|
|
},
|
|
.fixup = chip3_init_ops_fixup,
|
|
};
|
|
|
|
static struct sw64_chip_ops chip3_chip_ops = {
|
|
.get_cpu_num = chip3_get_cpu_nums,
|
|
.fixup = chip3_ops_fixup,
|
|
};
|
|
|
|
void __init sw64_setup_chip_ops(void)
|
|
{
|
|
sw64_chip_init = &chip3_chip_init_ops;
|
|
sw64_chip = &chip3_chip_ops;
|
|
}
|
|
|
|
/* 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, struct pt_regs*) = dummy_perf;
|
|
EXPORT_SYMBOL(perf_irq);
|
|
|
|
#ifdef CONFIG_PCI_MSI
|
|
extern void handle_pci_msi_interrupt(unsigned long type,
|
|
unsigned long vector,
|
|
unsigned long pci_msi1_addr);
|
|
#else
|
|
void handle_pci_msi_interrupt(unsigned long type,
|
|
unsigned long vector, unsigned long pci_msi1_addr)
|
|
{
|
|
pr_warn("SW arch disable CONFIG_PCI_MSI option.\n");
|
|
}
|
|
#endif
|
|
|
|
static void handle_fault_int(void)
|
|
{
|
|
int node;
|
|
|
|
node = __this_cpu_read(hard_node_id);
|
|
printk("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);
|
|
sw64_io_write(node, DUAL_CG0_FAULT_INTEN, 0);
|
|
sw64_io_write(node, DUAL_CG1_FAULT_INTEN, 0);
|
|
sw64_io_write(node, DUAL_CG2_FAULT_INTEN, 0);
|
|
sw64_io_write(node, DUAL_CG3_FAULT_INTEN, 0);
|
|
sw64_io_write(node, DUAL_CG4_FAULT_INTEN, 0);
|
|
sw64_io_write(node, DUAL_CG5_FAULT_INTEN, 0);
|
|
sw64_io_write(node, DUAL_CG6_FAULT_INTEN, 0);
|
|
sw64_io_write(node, DUAL_CG7_FAULT_INTEN, 0);
|
|
}
|
|
|
|
static void handle_mt_int(void)
|
|
{
|
|
printk("enter mt int\n");
|
|
}
|
|
|
|
static void handle_nmi_int(void)
|
|
{
|
|
printk("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;
|
|
handle_domain_irq(NULL, hwirq, regs);
|
|
stat &= ~(1UL << hwirq);
|
|
}
|
|
/*do handle irq */
|
|
|
|
sw64_io_write(node, DEV_INT_CONFIG, config_val);
|
|
}
|
|
|
|
void handle_chip_irq(unsigned long type, unsigned long vector,
|
|
unsigned long irq_arg, struct pt_regs *regs)
|
|
{
|
|
struct pt_regs *old_regs;
|
|
|
|
if (is_guest_or_emul()) {
|
|
if ((type & 0xffff) > 15) {
|
|
vector = type;
|
|
if (vector == 16)
|
|
type = INT_INTx;
|
|
else
|
|
type = INT_MSI;
|
|
}
|
|
}
|
|
|
|
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);
|
|
chip3_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_PC0:
|
|
perf_irq(PERFMON_PC0, regs);
|
|
return;
|
|
case INT_PC1:
|
|
perf_irq(PERFMON_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);
|
|
}
|
|
|
|
/*
|
|
* Early fix up the chip3 Root Complex settings
|
|
*/
|
|
static void chip3_pci_fixup_root_complex(struct pci_dev *dev)
|
|
{
|
|
int i;
|
|
struct pci_bus *bus = dev->bus;
|
|
struct pci_controller *hose = bus->sysdata;
|
|
|
|
hose->self_busno = hose->busn_space->start;
|
|
|
|
if (likely(bus->number == hose->self_busno)) {
|
|
if (IS_ENABLED(CONFIG_HOTPLUG_PCI_PCIE)) {
|
|
/* Check Root Complex port again */
|
|
dev->is_hotplug_bridge = 0;
|
|
dev->current_state = PCI_D0;
|
|
}
|
|
|
|
dev->class &= 0xff;
|
|
dev->class |= PCI_CLASS_BRIDGE_PCI << 8;
|
|
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
|
|
dev->resource[i].start = 0;
|
|
dev->resource[i].end = 0;
|
|
dev->resource[i].flags = IORESOURCE_PCI_FIXED;
|
|
}
|
|
}
|
|
atomic_inc(&dev->enable_cnt);
|
|
|
|
dev->no_msi = 1;
|
|
}
|
|
|
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_JN, PCI_DEVICE_ID_CHIP3, chip3_pci_fixup_root_complex);
|