MN1 support IRQ for counter overflow handling. MN1 use the index 26 of the Fabric Totem IRQ. The interrupt parent will be Hisilicon Mbigen-v2. The interrupt type is LPI.
Signed-off-by: Shaokun Zhang <zhangshao...@hisilicon.com> Signed-off-by: Anurup M <anuru...@huawei.com> --- drivers/perf/hisilicon/hisi_uncore_mn.c | 121 ++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/drivers/perf/hisilicon/hisi_uncore_mn.c b/drivers/perf/hisilicon/hisi_uncore_mn.c index 3fe5982..490b11f 100644 --- a/drivers/perf/hisilicon/hisi_uncore_mn.c +++ b/drivers/perf/hisilicon/hisi_uncore_mn.c @@ -23,6 +23,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/of_irq.h> #include <linux/perf_event.h> #include "hisi_uncore_pmu.h" @@ -54,6 +55,11 @@ enum armv8_hisi_mn_counters { #define MN1_EVENT_EN 0x01 #define MN1_BANK_SELECT 0x01 +#define MN1_INTM_REG_OFF 0x060 +#define MN1_INTS_REG_OFF 0x068 +#define MN1_INTC_REG_OFF 0x06C +#define MN1_INTM_UNMASK_ALL 0x0 + #define GET_MODULE_ID(hwmod_data) hwmod_data->mn_hwcfg.module_id struct hisi_mn_hwcfg { @@ -118,6 +124,49 @@ static u64 hisi_mn_event_update(struct perf_event *event, return new_raw_count; } +static irqreturn_t hisi_pmu_mn_isr(int irq, void *dev_id) +{ + struct hisi_pmu *mn_pmu = dev_id; + struct hisi_mn_data *mn_data = mn_pmu->hwmod_data; + struct hisi_djtag_client *client = mn_data->client; + struct perf_event *event; + u32 module_id = GET_MODULE_ID(mn_data); + unsigned long flags; + u32 value = 0; + int bit_pos; + + raw_spin_lock_irqsave(&mn_pmu->lock, flags); + + /* Read the INTS register */ + hisi_djtag_readreg(module_id, MN1_BANK_SELECT, MN1_INTS_REG_OFF, + client, &value); + if (!value) { + raw_spin_unlock_irqrestore(&mn_pmu->lock, flags); + return IRQ_NONE; + } + + /* Find the counter index which overflowed and handle them */ + for (bit_pos = 0; bit_pos < HISI_MAX_CFG_MN_CNTR; bit_pos++) { + if (test_bit(bit_pos, (void *)&value)) { + /* Clear the IRQ status flag */ + hisi_djtag_writereg(module_id, MN1_BANK_SELECT, + MN1_INTC_REG_OFF, (1 << bit_pos), client); + + /* Get the corresponding event struct */ + event = mn_pmu->hw_perf_events[bit_pos]; + if (!event) + continue; + + hisi_mn_event_update(event, &event->hw, bit_pos); + hisi_pmu_set_event_period(event); + perf_event_update_userpage(event); + } + } + + raw_spin_unlock_irqrestore(&mn_pmu->lock, flags); + return IRQ_HANDLED; +} + static void hisi_mn_set_evtype(struct hisi_pmu *mn_pmu, int idx, u32 val) { struct hisi_mn_data *mn_data = mn_pmu->hwmod_data; @@ -265,6 +314,51 @@ static int hisi_mn_get_event_idx(struct hisi_pmu *mn_pmu) return event_idx; } +static void hisi_mn_enable_interrupts(u32 module_id, + struct hisi_djtag_client *client) +{ + u32 value = 0; + + hisi_djtag_readreg(module_id, MN1_BANK_SELECT, MN1_INTM_REG_OFF, + client, &value); + if (value) + hisi_djtag_writereg(module_id, MN1_BANK_SELECT, + MN1_INTM_REG_OFF, MN1_INTM_UNMASK_ALL, + client); +} + +static int hisi_mn_init_irq(int irq, struct hisi_pmu *mn_pmu, + struct hisi_djtag_client *client) +{ + struct hisi_mn_data *mn_data = mn_pmu->hwmod_data; + u32 module_id = GET_MODULE_ID(mn_data); + struct device *dev = &client->dev; + int rc; + + rc = devm_request_irq(dev, irq, hisi_pmu_mn_isr, + IRQF_NOBALANCING | IRQF_NO_THREAD, + dev_name(dev), mn_pmu); + if (rc) { + dev_err(dev, "Could not request IRQ:%d\n", irq); + return rc; + } + + /* Overflow interrupt also should use the same CPU */ + rc = irq_set_affinity(irq, &mn_pmu->cpu); + if (rc) { + dev_err(dev, "could not set IRQ affinity!\n"); + return rc; + } + + /* + * Unmask all interrupts in Mask register + * Enable all IRQ's + */ + hisi_mn_enable_interrupts(module_id, client); + + return 0; +} + static const struct of_device_id mn_of_match[] = { { .compatible = "hisilicon,hip05-pmu-mn-v1", }, { .compatible = "hisilicon,hip06-pmu-mn-v1", }, @@ -273,6 +367,29 @@ static const struct of_device_id mn_of_match[] = { }; MODULE_DEVICE_TABLE(of, mn_of_match); +static int hisi_mn_init_irqs_fdt(struct device *dev, + struct hisi_pmu *mn_pmu) +{ + struct hisi_mn_data *mn_data = mn_pmu->hwmod_data; + struct hisi_djtag_client *client = mn_data->client; + int irq = -1, num_irqs, i; + + num_irqs = of_irq_count(dev->of_node); + for (i = 0; i < num_irqs; i++) { + irq = of_irq_get(dev->of_node, i); + if (irq < 0) + dev_info(dev, "No IRQ resource!\n"); + } + + if (irq < 0) + return 0; + + /* The last entry in the IRQ list to be chosen + * This is as per mbigen-v2 IRQ mapping + */ + return hisi_mn_init_irq(irq, mn_pmu, client); +} + static int hisi_mn_init_data(struct hisi_pmu *mn_pmu, struct hisi_djtag_client *client) { @@ -306,6 +423,10 @@ static int hisi_mn_init_data(struct hisi_pmu *mn_pmu, return -EINVAL; } + ret = hisi_mn_init_irqs_fdt(dev, mn_pmu); + if (ret) + return ret; + ret = device_property_read_u32(dev, "hisilicon,module-id", &mn_hwcfg->module_id); if (ret < 0) { -- 2.1.4