162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ARM DMC-620 memory controller PMU driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2020 Ampere Computing LLC. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define DMC620_PMUNAME "arm_dmc620" 962306a36Sopenharmony_ci#define DMC620_DRVNAME DMC620_PMUNAME "_pmu" 1062306a36Sopenharmony_ci#define pr_fmt(fmt) DMC620_DRVNAME ": " fmt 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/acpi.h> 1362306a36Sopenharmony_ci#include <linux/bitfield.h> 1462306a36Sopenharmony_ci#include <linux/bitops.h> 1562306a36Sopenharmony_ci#include <linux/cpuhotplug.h> 1662306a36Sopenharmony_ci#include <linux/cpumask.h> 1762306a36Sopenharmony_ci#include <linux/device.h> 1862306a36Sopenharmony_ci#include <linux/errno.h> 1962306a36Sopenharmony_ci#include <linux/interrupt.h> 2062306a36Sopenharmony_ci#include <linux/irq.h> 2162306a36Sopenharmony_ci#include <linux/kernel.h> 2262306a36Sopenharmony_ci#include <linux/list.h> 2362306a36Sopenharmony_ci#include <linux/module.h> 2462306a36Sopenharmony_ci#include <linux/mutex.h> 2562306a36Sopenharmony_ci#include <linux/perf_event.h> 2662306a36Sopenharmony_ci#include <linux/platform_device.h> 2762306a36Sopenharmony_ci#include <linux/printk.h> 2862306a36Sopenharmony_ci#include <linux/rculist.h> 2962306a36Sopenharmony_ci#include <linux/refcount.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define DMC620_PA_SHIFT 12 3262306a36Sopenharmony_ci#define DMC620_CNT_INIT 0x80000000 3362306a36Sopenharmony_ci#define DMC620_CNT_MAX_PERIOD 0xffffffff 3462306a36Sopenharmony_ci#define DMC620_PMU_CLKDIV2_MAX_COUNTERS 8 3562306a36Sopenharmony_ci#define DMC620_PMU_CLK_MAX_COUNTERS 2 3662306a36Sopenharmony_ci#define DMC620_PMU_MAX_COUNTERS \ 3762306a36Sopenharmony_ci (DMC620_PMU_CLKDIV2_MAX_COUNTERS + DMC620_PMU_CLK_MAX_COUNTERS) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* 4062306a36Sopenharmony_ci * The PMU registers start at 0xA00 in the DMC-620 memory map, and these 4162306a36Sopenharmony_ci * offsets are relative to that base. 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci * Each counter has a group of control/value registers, and the 4462306a36Sopenharmony_ci * DMC620_PMU_COUNTERn offsets are within a counter group. 4562306a36Sopenharmony_ci * 4662306a36Sopenharmony_ci * The counter registers groups start at 0xA10. 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_ci#define DMC620_PMU_OVERFLOW_STATUS_CLKDIV2 0x8 4962306a36Sopenharmony_ci#define DMC620_PMU_OVERFLOW_STATUS_CLKDIV2_MASK \ 5062306a36Sopenharmony_ci (DMC620_PMU_CLKDIV2_MAX_COUNTERS - 1) 5162306a36Sopenharmony_ci#define DMC620_PMU_OVERFLOW_STATUS_CLK 0xC 5262306a36Sopenharmony_ci#define DMC620_PMU_OVERFLOW_STATUS_CLK_MASK \ 5362306a36Sopenharmony_ci (DMC620_PMU_CLK_MAX_COUNTERS - 1) 5462306a36Sopenharmony_ci#define DMC620_PMU_COUNTERS_BASE 0x10 5562306a36Sopenharmony_ci#define DMC620_PMU_COUNTERn_MASK_31_00 0x0 5662306a36Sopenharmony_ci#define DMC620_PMU_COUNTERn_MASK_63_32 0x4 5762306a36Sopenharmony_ci#define DMC620_PMU_COUNTERn_MATCH_31_00 0x8 5862306a36Sopenharmony_ci#define DMC620_PMU_COUNTERn_MATCH_63_32 0xC 5962306a36Sopenharmony_ci#define DMC620_PMU_COUNTERn_CONTROL 0x10 6062306a36Sopenharmony_ci#define DMC620_PMU_COUNTERn_CONTROL_ENABLE BIT(0) 6162306a36Sopenharmony_ci#define DMC620_PMU_COUNTERn_CONTROL_INVERT BIT(1) 6262306a36Sopenharmony_ci#define DMC620_PMU_COUNTERn_CONTROL_EVENT_MUX GENMASK(6, 2) 6362306a36Sopenharmony_ci#define DMC620_PMU_COUNTERn_CONTROL_INCR_MUX GENMASK(8, 7) 6462306a36Sopenharmony_ci#define DMC620_PMU_COUNTERn_VALUE 0x20 6562306a36Sopenharmony_ci/* Offset of the registers for a given counter, relative to 0xA00 */ 6662306a36Sopenharmony_ci#define DMC620_PMU_COUNTERn_OFFSET(n) \ 6762306a36Sopenharmony_ci (DMC620_PMU_COUNTERS_BASE + 0x28 * (n)) 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* 7062306a36Sopenharmony_ci * dmc620_pmu_irqs_lock: protects dmc620_pmu_irqs list 7162306a36Sopenharmony_ci * dmc620_pmu_node_lock: protects pmus_node lists in all dmc620_pmu instances 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_cistatic DEFINE_MUTEX(dmc620_pmu_irqs_lock); 7462306a36Sopenharmony_cistatic DEFINE_MUTEX(dmc620_pmu_node_lock); 7562306a36Sopenharmony_cistatic LIST_HEAD(dmc620_pmu_irqs); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistruct dmc620_pmu_irq { 7862306a36Sopenharmony_ci struct hlist_node node; 7962306a36Sopenharmony_ci struct list_head pmus_node; 8062306a36Sopenharmony_ci struct list_head irqs_node; 8162306a36Sopenharmony_ci refcount_t refcount; 8262306a36Sopenharmony_ci unsigned int irq_num; 8362306a36Sopenharmony_ci unsigned int cpu; 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistruct dmc620_pmu { 8762306a36Sopenharmony_ci struct pmu pmu; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci void __iomem *base; 9062306a36Sopenharmony_ci struct dmc620_pmu_irq *irq; 9162306a36Sopenharmony_ci struct list_head pmus_node; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* 9462306a36Sopenharmony_ci * We put all clkdiv2 and clk counters to a same array. 9562306a36Sopenharmony_ci * The first DMC620_PMU_CLKDIV2_MAX_COUNTERS bits belong to 9662306a36Sopenharmony_ci * clkdiv2 counters, the last DMC620_PMU_CLK_MAX_COUNTERS 9762306a36Sopenharmony_ci * belong to clk counters. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_ci DECLARE_BITMAP(used_mask, DMC620_PMU_MAX_COUNTERS); 10062306a36Sopenharmony_ci struct perf_event *events[DMC620_PMU_MAX_COUNTERS]; 10162306a36Sopenharmony_ci}; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define to_dmc620_pmu(p) (container_of(p, struct dmc620_pmu, pmu)) 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic int cpuhp_state_num; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistruct dmc620_pmu_event_attr { 10862306a36Sopenharmony_ci struct device_attribute attr; 10962306a36Sopenharmony_ci u8 clkdiv2; 11062306a36Sopenharmony_ci u8 eventid; 11162306a36Sopenharmony_ci}; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic ssize_t 11462306a36Sopenharmony_cidmc620_pmu_event_show(struct device *dev, 11562306a36Sopenharmony_ci struct device_attribute *attr, char *page) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct dmc620_pmu_event_attr *eattr; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci eattr = container_of(attr, typeof(*eattr), attr); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci return sysfs_emit(page, "event=0x%x,clkdiv2=0x%x\n", eattr->eventid, eattr->clkdiv2); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci#define DMC620_PMU_EVENT_ATTR(_name, _eventid, _clkdiv2) \ 12562306a36Sopenharmony_ci (&((struct dmc620_pmu_event_attr[]) {{ \ 12662306a36Sopenharmony_ci .attr = __ATTR(_name, 0444, dmc620_pmu_event_show, NULL), \ 12762306a36Sopenharmony_ci .clkdiv2 = _clkdiv2, \ 12862306a36Sopenharmony_ci .eventid = _eventid, \ 12962306a36Sopenharmony_ci }})[0].attr.attr) 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic struct attribute *dmc620_pmu_events_attrs[] = { 13262306a36Sopenharmony_ci /* clkdiv2 events list */ 13362306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clkdiv2_cycle_count, 0x0, 1), 13462306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clkdiv2_allocate, 0x1, 1), 13562306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clkdiv2_queue_depth, 0x2, 1), 13662306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clkdiv2_waiting_for_wr_data, 0x3, 1), 13762306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clkdiv2_read_backlog, 0x4, 1), 13862306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clkdiv2_waiting_for_mi, 0x5, 1), 13962306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clkdiv2_hazard_resolution, 0x6, 1), 14062306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clkdiv2_enqueue, 0x7, 1), 14162306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clkdiv2_arbitrate, 0x8, 1), 14262306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clkdiv2_lrank_turnaround_activate, 0x9, 1), 14362306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clkdiv2_prank_turnaround_activate, 0xa, 1), 14462306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clkdiv2_read_depth, 0xb, 1), 14562306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clkdiv2_write_depth, 0xc, 1), 14662306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clkdiv2_highigh_qos_depth, 0xd, 1), 14762306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clkdiv2_high_qos_depth, 0xe, 1), 14862306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clkdiv2_medium_qos_depth, 0xf, 1), 14962306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clkdiv2_low_qos_depth, 0x10, 1), 15062306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clkdiv2_activate, 0x11, 1), 15162306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clkdiv2_rdwr, 0x12, 1), 15262306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clkdiv2_refresh, 0x13, 1), 15362306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clkdiv2_training_request, 0x14, 1), 15462306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clkdiv2_t_mac_tracker, 0x15, 1), 15562306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clkdiv2_bk_fsm_tracker, 0x16, 1), 15662306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clkdiv2_bk_open_tracker, 0x17, 1), 15762306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clkdiv2_ranks_in_pwr_down, 0x18, 1), 15862306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clkdiv2_ranks_in_sref, 0x19, 1), 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* clk events list */ 16162306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clk_cycle_count, 0x0, 0), 16262306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clk_request, 0x1, 0), 16362306a36Sopenharmony_ci DMC620_PMU_EVENT_ATTR(clk_upload_stall, 0x2, 0), 16462306a36Sopenharmony_ci NULL, 16562306a36Sopenharmony_ci}; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic const struct attribute_group dmc620_pmu_events_attr_group = { 16862306a36Sopenharmony_ci .name = "events", 16962306a36Sopenharmony_ci .attrs = dmc620_pmu_events_attrs, 17062306a36Sopenharmony_ci}; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci/* User ABI */ 17362306a36Sopenharmony_ci#define ATTR_CFG_FLD_mask_CFG config 17462306a36Sopenharmony_ci#define ATTR_CFG_FLD_mask_LO 0 17562306a36Sopenharmony_ci#define ATTR_CFG_FLD_mask_HI 44 17662306a36Sopenharmony_ci#define ATTR_CFG_FLD_match_CFG config1 17762306a36Sopenharmony_ci#define ATTR_CFG_FLD_match_LO 0 17862306a36Sopenharmony_ci#define ATTR_CFG_FLD_match_HI 44 17962306a36Sopenharmony_ci#define ATTR_CFG_FLD_invert_CFG config2 18062306a36Sopenharmony_ci#define ATTR_CFG_FLD_invert_LO 0 18162306a36Sopenharmony_ci#define ATTR_CFG_FLD_invert_HI 0 18262306a36Sopenharmony_ci#define ATTR_CFG_FLD_incr_CFG config2 18362306a36Sopenharmony_ci#define ATTR_CFG_FLD_incr_LO 1 18462306a36Sopenharmony_ci#define ATTR_CFG_FLD_incr_HI 2 18562306a36Sopenharmony_ci#define ATTR_CFG_FLD_event_CFG config2 18662306a36Sopenharmony_ci#define ATTR_CFG_FLD_event_LO 3 18762306a36Sopenharmony_ci#define ATTR_CFG_FLD_event_HI 8 18862306a36Sopenharmony_ci#define ATTR_CFG_FLD_clkdiv2_CFG config2 18962306a36Sopenharmony_ci#define ATTR_CFG_FLD_clkdiv2_LO 9 19062306a36Sopenharmony_ci#define ATTR_CFG_FLD_clkdiv2_HI 9 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci#define __GEN_PMU_FORMAT_ATTR(cfg, lo, hi) \ 19362306a36Sopenharmony_ci (lo) == (hi) ? #cfg ":" #lo "\n" : #cfg ":" #lo "-" #hi 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci#define _GEN_PMU_FORMAT_ATTR(cfg, lo, hi) \ 19662306a36Sopenharmony_ci __GEN_PMU_FORMAT_ATTR(cfg, lo, hi) 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci#define GEN_PMU_FORMAT_ATTR(name) \ 19962306a36Sopenharmony_ci PMU_FORMAT_ATTR(name, \ 20062306a36Sopenharmony_ci _GEN_PMU_FORMAT_ATTR(ATTR_CFG_FLD_##name##_CFG, \ 20162306a36Sopenharmony_ci ATTR_CFG_FLD_##name##_LO, \ 20262306a36Sopenharmony_ci ATTR_CFG_FLD_##name##_HI)) 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci#define _ATTR_CFG_GET_FLD(attr, cfg, lo, hi) \ 20562306a36Sopenharmony_ci ((((attr)->cfg) >> lo) & GENMASK_ULL(hi - lo, 0)) 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci#define ATTR_CFG_GET_FLD(attr, name) \ 20862306a36Sopenharmony_ci _ATTR_CFG_GET_FLD(attr, \ 20962306a36Sopenharmony_ci ATTR_CFG_FLD_##name##_CFG, \ 21062306a36Sopenharmony_ci ATTR_CFG_FLD_##name##_LO, \ 21162306a36Sopenharmony_ci ATTR_CFG_FLD_##name##_HI) 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ciGEN_PMU_FORMAT_ATTR(mask); 21462306a36Sopenharmony_ciGEN_PMU_FORMAT_ATTR(match); 21562306a36Sopenharmony_ciGEN_PMU_FORMAT_ATTR(invert); 21662306a36Sopenharmony_ciGEN_PMU_FORMAT_ATTR(incr); 21762306a36Sopenharmony_ciGEN_PMU_FORMAT_ATTR(event); 21862306a36Sopenharmony_ciGEN_PMU_FORMAT_ATTR(clkdiv2); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic struct attribute *dmc620_pmu_formats_attrs[] = { 22162306a36Sopenharmony_ci &format_attr_mask.attr, 22262306a36Sopenharmony_ci &format_attr_match.attr, 22362306a36Sopenharmony_ci &format_attr_invert.attr, 22462306a36Sopenharmony_ci &format_attr_incr.attr, 22562306a36Sopenharmony_ci &format_attr_event.attr, 22662306a36Sopenharmony_ci &format_attr_clkdiv2.attr, 22762306a36Sopenharmony_ci NULL, 22862306a36Sopenharmony_ci}; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic const struct attribute_group dmc620_pmu_format_attr_group = { 23162306a36Sopenharmony_ci .name = "format", 23262306a36Sopenharmony_ci .attrs = dmc620_pmu_formats_attrs, 23362306a36Sopenharmony_ci}; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic ssize_t dmc620_pmu_cpumask_show(struct device *dev, 23662306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct dmc620_pmu *dmc620_pmu = to_dmc620_pmu(dev_get_drvdata(dev)); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci return cpumap_print_to_pagebuf(true, buf, 24162306a36Sopenharmony_ci cpumask_of(dmc620_pmu->irq->cpu)); 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic struct device_attribute dmc620_pmu_cpumask_attr = 24562306a36Sopenharmony_ci __ATTR(cpumask, 0444, dmc620_pmu_cpumask_show, NULL); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic struct attribute *dmc620_pmu_cpumask_attrs[] = { 24862306a36Sopenharmony_ci &dmc620_pmu_cpumask_attr.attr, 24962306a36Sopenharmony_ci NULL, 25062306a36Sopenharmony_ci}; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic const struct attribute_group dmc620_pmu_cpumask_attr_group = { 25362306a36Sopenharmony_ci .attrs = dmc620_pmu_cpumask_attrs, 25462306a36Sopenharmony_ci}; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic const struct attribute_group *dmc620_pmu_attr_groups[] = { 25762306a36Sopenharmony_ci &dmc620_pmu_events_attr_group, 25862306a36Sopenharmony_ci &dmc620_pmu_format_attr_group, 25962306a36Sopenharmony_ci &dmc620_pmu_cpumask_attr_group, 26062306a36Sopenharmony_ci NULL, 26162306a36Sopenharmony_ci}; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic inline 26462306a36Sopenharmony_ciu32 dmc620_pmu_creg_read(struct dmc620_pmu *dmc620_pmu, 26562306a36Sopenharmony_ci unsigned int idx, unsigned int reg) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci return readl(dmc620_pmu->base + DMC620_PMU_COUNTERn_OFFSET(idx) + reg); 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic inline 27162306a36Sopenharmony_civoid dmc620_pmu_creg_write(struct dmc620_pmu *dmc620_pmu, 27262306a36Sopenharmony_ci unsigned int idx, unsigned int reg, u32 val) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci writel(val, dmc620_pmu->base + DMC620_PMU_COUNTERn_OFFSET(idx) + reg); 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic 27862306a36Sopenharmony_ciunsigned int dmc620_event_to_counter_control(struct perf_event *event) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci struct perf_event_attr *attr = &event->attr; 28162306a36Sopenharmony_ci unsigned int reg = 0; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci reg |= FIELD_PREP(DMC620_PMU_COUNTERn_CONTROL_INVERT, 28462306a36Sopenharmony_ci ATTR_CFG_GET_FLD(attr, invert)); 28562306a36Sopenharmony_ci reg |= FIELD_PREP(DMC620_PMU_COUNTERn_CONTROL_EVENT_MUX, 28662306a36Sopenharmony_ci ATTR_CFG_GET_FLD(attr, event)); 28762306a36Sopenharmony_ci reg |= FIELD_PREP(DMC620_PMU_COUNTERn_CONTROL_INCR_MUX, 28862306a36Sopenharmony_ci ATTR_CFG_GET_FLD(attr, incr)); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return reg; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic int dmc620_get_event_idx(struct perf_event *event) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct dmc620_pmu *dmc620_pmu = to_dmc620_pmu(event->pmu); 29662306a36Sopenharmony_ci int idx, start_idx, end_idx; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (ATTR_CFG_GET_FLD(&event->attr, clkdiv2)) { 29962306a36Sopenharmony_ci start_idx = 0; 30062306a36Sopenharmony_ci end_idx = DMC620_PMU_CLKDIV2_MAX_COUNTERS; 30162306a36Sopenharmony_ci } else { 30262306a36Sopenharmony_ci start_idx = DMC620_PMU_CLKDIV2_MAX_COUNTERS; 30362306a36Sopenharmony_ci end_idx = DMC620_PMU_MAX_COUNTERS; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci for (idx = start_idx; idx < end_idx; ++idx) { 30762306a36Sopenharmony_ci if (!test_and_set_bit(idx, dmc620_pmu->used_mask)) 30862306a36Sopenharmony_ci return idx; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* The counters are all in use. */ 31262306a36Sopenharmony_ci return -EAGAIN; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic inline 31662306a36Sopenharmony_ciu64 dmc620_pmu_read_counter(struct perf_event *event) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci struct dmc620_pmu *dmc620_pmu = to_dmc620_pmu(event->pmu); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci return dmc620_pmu_creg_read(dmc620_pmu, 32162306a36Sopenharmony_ci event->hw.idx, DMC620_PMU_COUNTERn_VALUE); 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic void dmc620_pmu_event_update(struct perf_event *event) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 32762306a36Sopenharmony_ci u64 delta, prev_count, new_count; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci do { 33062306a36Sopenharmony_ci /* We may also be called from the irq handler */ 33162306a36Sopenharmony_ci prev_count = local64_read(&hwc->prev_count); 33262306a36Sopenharmony_ci new_count = dmc620_pmu_read_counter(event); 33362306a36Sopenharmony_ci } while (local64_cmpxchg(&hwc->prev_count, 33462306a36Sopenharmony_ci prev_count, new_count) != prev_count); 33562306a36Sopenharmony_ci delta = (new_count - prev_count) & DMC620_CNT_MAX_PERIOD; 33662306a36Sopenharmony_ci local64_add(delta, &event->count); 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic void dmc620_pmu_event_set_period(struct perf_event *event) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci struct dmc620_pmu *dmc620_pmu = to_dmc620_pmu(event->pmu); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci local64_set(&event->hw.prev_count, DMC620_CNT_INIT); 34462306a36Sopenharmony_ci dmc620_pmu_creg_write(dmc620_pmu, 34562306a36Sopenharmony_ci event->hw.idx, DMC620_PMU_COUNTERn_VALUE, DMC620_CNT_INIT); 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic void dmc620_pmu_enable_counter(struct perf_event *event) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci struct dmc620_pmu *dmc620_pmu = to_dmc620_pmu(event->pmu); 35162306a36Sopenharmony_ci u32 reg; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci reg = dmc620_event_to_counter_control(event) | DMC620_PMU_COUNTERn_CONTROL_ENABLE; 35462306a36Sopenharmony_ci dmc620_pmu_creg_write(dmc620_pmu, 35562306a36Sopenharmony_ci event->hw.idx, DMC620_PMU_COUNTERn_CONTROL, reg); 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic void dmc620_pmu_disable_counter(struct perf_event *event) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct dmc620_pmu *dmc620_pmu = to_dmc620_pmu(event->pmu); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci dmc620_pmu_creg_write(dmc620_pmu, 36362306a36Sopenharmony_ci event->hw.idx, DMC620_PMU_COUNTERn_CONTROL, 0); 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic irqreturn_t dmc620_pmu_handle_irq(int irq_num, void *data) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci struct dmc620_pmu_irq *irq = data; 36962306a36Sopenharmony_ci struct dmc620_pmu *dmc620_pmu; 37062306a36Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci rcu_read_lock(); 37362306a36Sopenharmony_ci list_for_each_entry_rcu(dmc620_pmu, &irq->pmus_node, pmus_node) { 37462306a36Sopenharmony_ci unsigned long status; 37562306a36Sopenharmony_ci struct perf_event *event; 37662306a36Sopenharmony_ci unsigned int idx; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* 37962306a36Sopenharmony_ci * HW doesn't provide a control to atomically disable all counters. 38062306a36Sopenharmony_ci * To prevent race condition (overflow happens while clearing status register), 38162306a36Sopenharmony_ci * disable all events before continuing 38262306a36Sopenharmony_ci */ 38362306a36Sopenharmony_ci for (idx = 0; idx < DMC620_PMU_MAX_COUNTERS; idx++) { 38462306a36Sopenharmony_ci event = dmc620_pmu->events[idx]; 38562306a36Sopenharmony_ci if (!event) 38662306a36Sopenharmony_ci continue; 38762306a36Sopenharmony_ci dmc620_pmu_disable_counter(event); 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci status = readl(dmc620_pmu->base + DMC620_PMU_OVERFLOW_STATUS_CLKDIV2); 39162306a36Sopenharmony_ci status |= (readl(dmc620_pmu->base + DMC620_PMU_OVERFLOW_STATUS_CLK) << 39262306a36Sopenharmony_ci DMC620_PMU_CLKDIV2_MAX_COUNTERS); 39362306a36Sopenharmony_ci if (status) { 39462306a36Sopenharmony_ci for_each_set_bit(idx, &status, 39562306a36Sopenharmony_ci DMC620_PMU_MAX_COUNTERS) { 39662306a36Sopenharmony_ci event = dmc620_pmu->events[idx]; 39762306a36Sopenharmony_ci if (WARN_ON_ONCE(!event)) 39862306a36Sopenharmony_ci continue; 39962306a36Sopenharmony_ci dmc620_pmu_event_update(event); 40062306a36Sopenharmony_ci dmc620_pmu_event_set_period(event); 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (status & DMC620_PMU_OVERFLOW_STATUS_CLKDIV2_MASK) 40462306a36Sopenharmony_ci writel(0, dmc620_pmu->base + DMC620_PMU_OVERFLOW_STATUS_CLKDIV2); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if ((status >> DMC620_PMU_CLKDIV2_MAX_COUNTERS) & 40762306a36Sopenharmony_ci DMC620_PMU_OVERFLOW_STATUS_CLK_MASK) 40862306a36Sopenharmony_ci writel(0, dmc620_pmu->base + DMC620_PMU_OVERFLOW_STATUS_CLK); 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci for (idx = 0; idx < DMC620_PMU_MAX_COUNTERS; idx++) { 41262306a36Sopenharmony_ci event = dmc620_pmu->events[idx]; 41362306a36Sopenharmony_ci if (!event) 41462306a36Sopenharmony_ci continue; 41562306a36Sopenharmony_ci if (!(event->hw.state & PERF_HES_STOPPED)) 41662306a36Sopenharmony_ci dmc620_pmu_enable_counter(event); 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci ret = IRQ_HANDLED; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci rcu_read_unlock(); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci return ret; 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic struct dmc620_pmu_irq *__dmc620_pmu_get_irq(int irq_num) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct dmc620_pmu_irq *irq; 42962306a36Sopenharmony_ci int ret; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci list_for_each_entry(irq, &dmc620_pmu_irqs, irqs_node) 43262306a36Sopenharmony_ci if (irq->irq_num == irq_num && refcount_inc_not_zero(&irq->refcount)) 43362306a36Sopenharmony_ci return irq; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci irq = kzalloc(sizeof(*irq), GFP_KERNEL); 43662306a36Sopenharmony_ci if (!irq) 43762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci INIT_LIST_HEAD(&irq->pmus_node); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci /* Pick one CPU to be the preferred one to use */ 44262306a36Sopenharmony_ci irq->cpu = raw_smp_processor_id(); 44362306a36Sopenharmony_ci refcount_set(&irq->refcount, 1); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci ret = request_irq(irq_num, dmc620_pmu_handle_irq, 44662306a36Sopenharmony_ci IRQF_NOBALANCING | IRQF_NO_THREAD, 44762306a36Sopenharmony_ci "dmc620-pmu", irq); 44862306a36Sopenharmony_ci if (ret) 44962306a36Sopenharmony_ci goto out_free_aff; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci ret = irq_set_affinity(irq_num, cpumask_of(irq->cpu)); 45262306a36Sopenharmony_ci if (ret) 45362306a36Sopenharmony_ci goto out_free_irq; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci ret = cpuhp_state_add_instance_nocalls(cpuhp_state_num, &irq->node); 45662306a36Sopenharmony_ci if (ret) 45762306a36Sopenharmony_ci goto out_free_irq; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci irq->irq_num = irq_num; 46062306a36Sopenharmony_ci list_add(&irq->irqs_node, &dmc620_pmu_irqs); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci return irq; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ciout_free_irq: 46562306a36Sopenharmony_ci free_irq(irq_num, irq); 46662306a36Sopenharmony_ciout_free_aff: 46762306a36Sopenharmony_ci kfree(irq); 46862306a36Sopenharmony_ci return ERR_PTR(ret); 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cistatic int dmc620_pmu_get_irq(struct dmc620_pmu *dmc620_pmu, int irq_num) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci struct dmc620_pmu_irq *irq; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci mutex_lock(&dmc620_pmu_irqs_lock); 47662306a36Sopenharmony_ci irq = __dmc620_pmu_get_irq(irq_num); 47762306a36Sopenharmony_ci mutex_unlock(&dmc620_pmu_irqs_lock); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (IS_ERR(irq)) 48062306a36Sopenharmony_ci return PTR_ERR(irq); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci dmc620_pmu->irq = irq; 48362306a36Sopenharmony_ci mutex_lock(&dmc620_pmu_node_lock); 48462306a36Sopenharmony_ci list_add_rcu(&dmc620_pmu->pmus_node, &irq->pmus_node); 48562306a36Sopenharmony_ci mutex_unlock(&dmc620_pmu_node_lock); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci return 0; 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic void dmc620_pmu_put_irq(struct dmc620_pmu *dmc620_pmu) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci struct dmc620_pmu_irq *irq = dmc620_pmu->irq; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci mutex_lock(&dmc620_pmu_node_lock); 49562306a36Sopenharmony_ci list_del_rcu(&dmc620_pmu->pmus_node); 49662306a36Sopenharmony_ci mutex_unlock(&dmc620_pmu_node_lock); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci mutex_lock(&dmc620_pmu_irqs_lock); 49962306a36Sopenharmony_ci if (!refcount_dec_and_test(&irq->refcount)) { 50062306a36Sopenharmony_ci mutex_unlock(&dmc620_pmu_irqs_lock); 50162306a36Sopenharmony_ci return; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci list_del(&irq->irqs_node); 50562306a36Sopenharmony_ci mutex_unlock(&dmc620_pmu_irqs_lock); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci free_irq(irq->irq_num, irq); 50862306a36Sopenharmony_ci cpuhp_state_remove_instance_nocalls(cpuhp_state_num, &irq->node); 50962306a36Sopenharmony_ci kfree(irq); 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_cistatic int dmc620_pmu_event_init(struct perf_event *event) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci struct dmc620_pmu *dmc620_pmu = to_dmc620_pmu(event->pmu); 51562306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 51662306a36Sopenharmony_ci struct perf_event *sibling; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (event->attr.type != event->pmu->type) 51962306a36Sopenharmony_ci return -ENOENT; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* 52262306a36Sopenharmony_ci * DMC 620 PMUs are shared across all cpus and cannot 52362306a36Sopenharmony_ci * support task bound and sampling events. 52462306a36Sopenharmony_ci */ 52562306a36Sopenharmony_ci if (is_sampling_event(event) || 52662306a36Sopenharmony_ci event->attach_state & PERF_ATTACH_TASK) { 52762306a36Sopenharmony_ci dev_dbg(dmc620_pmu->pmu.dev, 52862306a36Sopenharmony_ci "Can't support per-task counters\n"); 52962306a36Sopenharmony_ci return -EOPNOTSUPP; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci /* 53362306a36Sopenharmony_ci * Many perf core operations (eg. events rotation) operate on a 53462306a36Sopenharmony_ci * single CPU context. This is obvious for CPU PMUs, where one 53562306a36Sopenharmony_ci * expects the same sets of events being observed on all CPUs, 53662306a36Sopenharmony_ci * but can lead to issues for off-core PMUs, where each 53762306a36Sopenharmony_ci * event could be theoretically assigned to a different CPU. To 53862306a36Sopenharmony_ci * mitigate this, we enforce CPU assignment to one, selected 53962306a36Sopenharmony_ci * processor. 54062306a36Sopenharmony_ci */ 54162306a36Sopenharmony_ci event->cpu = dmc620_pmu->irq->cpu; 54262306a36Sopenharmony_ci if (event->cpu < 0) 54362306a36Sopenharmony_ci return -EINVAL; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci /* 54662306a36Sopenharmony_ci * We can't atomically disable all HW counters so only one event allowed, 54762306a36Sopenharmony_ci * although software events are acceptable. 54862306a36Sopenharmony_ci */ 54962306a36Sopenharmony_ci if (event->group_leader != event && 55062306a36Sopenharmony_ci !is_software_event(event->group_leader)) 55162306a36Sopenharmony_ci return -EINVAL; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci for_each_sibling_event(sibling, event->group_leader) { 55462306a36Sopenharmony_ci if (sibling != event && 55562306a36Sopenharmony_ci !is_software_event(sibling)) 55662306a36Sopenharmony_ci return -EINVAL; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci hwc->idx = -1; 56062306a36Sopenharmony_ci return 0; 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cistatic void dmc620_pmu_read(struct perf_event *event) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci dmc620_pmu_event_update(event); 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic void dmc620_pmu_start(struct perf_event *event, int flags) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci event->hw.state = 0; 57162306a36Sopenharmony_ci dmc620_pmu_event_set_period(event); 57262306a36Sopenharmony_ci dmc620_pmu_enable_counter(event); 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_cistatic void dmc620_pmu_stop(struct perf_event *event, int flags) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci if (event->hw.state & PERF_HES_STOPPED) 57862306a36Sopenharmony_ci return; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci dmc620_pmu_disable_counter(event); 58162306a36Sopenharmony_ci dmc620_pmu_event_update(event); 58262306a36Sopenharmony_ci event->hw.state |= PERF_HES_STOPPED | PERF_HES_UPTODATE; 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic int dmc620_pmu_add(struct perf_event *event, int flags) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci struct dmc620_pmu *dmc620_pmu = to_dmc620_pmu(event->pmu); 58862306a36Sopenharmony_ci struct perf_event_attr *attr = &event->attr; 58962306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 59062306a36Sopenharmony_ci int idx; 59162306a36Sopenharmony_ci u64 reg; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci idx = dmc620_get_event_idx(event); 59462306a36Sopenharmony_ci if (idx < 0) 59562306a36Sopenharmony_ci return idx; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci hwc->idx = idx; 59862306a36Sopenharmony_ci dmc620_pmu->events[idx] = event; 59962306a36Sopenharmony_ci hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci reg = ATTR_CFG_GET_FLD(attr, mask); 60262306a36Sopenharmony_ci dmc620_pmu_creg_write(dmc620_pmu, 60362306a36Sopenharmony_ci idx, DMC620_PMU_COUNTERn_MASK_31_00, lower_32_bits(reg)); 60462306a36Sopenharmony_ci dmc620_pmu_creg_write(dmc620_pmu, 60562306a36Sopenharmony_ci idx, DMC620_PMU_COUNTERn_MASK_63_32, upper_32_bits(reg)); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci reg = ATTR_CFG_GET_FLD(attr, match); 60862306a36Sopenharmony_ci dmc620_pmu_creg_write(dmc620_pmu, 60962306a36Sopenharmony_ci idx, DMC620_PMU_COUNTERn_MATCH_31_00, lower_32_bits(reg)); 61062306a36Sopenharmony_ci dmc620_pmu_creg_write(dmc620_pmu, 61162306a36Sopenharmony_ci idx, DMC620_PMU_COUNTERn_MATCH_63_32, upper_32_bits(reg)); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci if (flags & PERF_EF_START) 61462306a36Sopenharmony_ci dmc620_pmu_start(event, PERF_EF_RELOAD); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci perf_event_update_userpage(event); 61762306a36Sopenharmony_ci return 0; 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cistatic void dmc620_pmu_del(struct perf_event *event, int flags) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci struct dmc620_pmu *dmc620_pmu = to_dmc620_pmu(event->pmu); 62362306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 62462306a36Sopenharmony_ci int idx = hwc->idx; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci dmc620_pmu_stop(event, PERF_EF_UPDATE); 62762306a36Sopenharmony_ci dmc620_pmu->events[idx] = NULL; 62862306a36Sopenharmony_ci clear_bit(idx, dmc620_pmu->used_mask); 62962306a36Sopenharmony_ci perf_event_update_userpage(event); 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_cistatic int dmc620_pmu_cpu_teardown(unsigned int cpu, 63362306a36Sopenharmony_ci struct hlist_node *node) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci struct dmc620_pmu_irq *irq; 63662306a36Sopenharmony_ci struct dmc620_pmu *dmc620_pmu; 63762306a36Sopenharmony_ci unsigned int target; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci irq = hlist_entry_safe(node, struct dmc620_pmu_irq, node); 64062306a36Sopenharmony_ci if (cpu != irq->cpu) 64162306a36Sopenharmony_ci return 0; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci target = cpumask_any_but(cpu_online_mask, cpu); 64462306a36Sopenharmony_ci if (target >= nr_cpu_ids) 64562306a36Sopenharmony_ci return 0; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci /* We're only reading, but this isn't the place to be involving RCU */ 64862306a36Sopenharmony_ci mutex_lock(&dmc620_pmu_node_lock); 64962306a36Sopenharmony_ci list_for_each_entry(dmc620_pmu, &irq->pmus_node, pmus_node) 65062306a36Sopenharmony_ci perf_pmu_migrate_context(&dmc620_pmu->pmu, irq->cpu, target); 65162306a36Sopenharmony_ci mutex_unlock(&dmc620_pmu_node_lock); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci WARN_ON(irq_set_affinity(irq->irq_num, cpumask_of(target))); 65462306a36Sopenharmony_ci irq->cpu = target; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci return 0; 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_cistatic int dmc620_pmu_device_probe(struct platform_device *pdev) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci struct dmc620_pmu *dmc620_pmu; 66262306a36Sopenharmony_ci struct resource *res; 66362306a36Sopenharmony_ci char *name; 66462306a36Sopenharmony_ci int irq_num; 66562306a36Sopenharmony_ci int i, ret; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci dmc620_pmu = devm_kzalloc(&pdev->dev, 66862306a36Sopenharmony_ci sizeof(struct dmc620_pmu), GFP_KERNEL); 66962306a36Sopenharmony_ci if (!dmc620_pmu) 67062306a36Sopenharmony_ci return -ENOMEM; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci platform_set_drvdata(pdev, dmc620_pmu); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci dmc620_pmu->pmu = (struct pmu) { 67562306a36Sopenharmony_ci .module = THIS_MODULE, 67662306a36Sopenharmony_ci .capabilities = PERF_PMU_CAP_NO_EXCLUDE, 67762306a36Sopenharmony_ci .task_ctx_nr = perf_invalid_context, 67862306a36Sopenharmony_ci .event_init = dmc620_pmu_event_init, 67962306a36Sopenharmony_ci .add = dmc620_pmu_add, 68062306a36Sopenharmony_ci .del = dmc620_pmu_del, 68162306a36Sopenharmony_ci .start = dmc620_pmu_start, 68262306a36Sopenharmony_ci .stop = dmc620_pmu_stop, 68362306a36Sopenharmony_ci .read = dmc620_pmu_read, 68462306a36Sopenharmony_ci .attr_groups = dmc620_pmu_attr_groups, 68562306a36Sopenharmony_ci }; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci dmc620_pmu->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 68862306a36Sopenharmony_ci if (IS_ERR(dmc620_pmu->base)) 68962306a36Sopenharmony_ci return PTR_ERR(dmc620_pmu->base); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci /* Make sure device is reset before enabling interrupt */ 69262306a36Sopenharmony_ci for (i = 0; i < DMC620_PMU_MAX_COUNTERS; i++) 69362306a36Sopenharmony_ci dmc620_pmu_creg_write(dmc620_pmu, i, DMC620_PMU_COUNTERn_CONTROL, 0); 69462306a36Sopenharmony_ci writel(0, dmc620_pmu->base + DMC620_PMU_OVERFLOW_STATUS_CLKDIV2); 69562306a36Sopenharmony_ci writel(0, dmc620_pmu->base + DMC620_PMU_OVERFLOW_STATUS_CLK); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci irq_num = platform_get_irq(pdev, 0); 69862306a36Sopenharmony_ci if (irq_num < 0) 69962306a36Sopenharmony_ci return irq_num; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci ret = dmc620_pmu_get_irq(dmc620_pmu, irq_num); 70262306a36Sopenharmony_ci if (ret) 70362306a36Sopenharmony_ci return ret; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci name = devm_kasprintf(&pdev->dev, GFP_KERNEL, 70662306a36Sopenharmony_ci "%s_%llx", DMC620_PMUNAME, 70762306a36Sopenharmony_ci (u64)(res->start >> DMC620_PA_SHIFT)); 70862306a36Sopenharmony_ci if (!name) { 70962306a36Sopenharmony_ci dev_err(&pdev->dev, 71062306a36Sopenharmony_ci "Create name failed, PMU @%pa\n", &res->start); 71162306a36Sopenharmony_ci ret = -ENOMEM; 71262306a36Sopenharmony_ci goto out_teardown_dev; 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci ret = perf_pmu_register(&dmc620_pmu->pmu, name, -1); 71662306a36Sopenharmony_ci if (ret) 71762306a36Sopenharmony_ci goto out_teardown_dev; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci return 0; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ciout_teardown_dev: 72262306a36Sopenharmony_ci dmc620_pmu_put_irq(dmc620_pmu); 72362306a36Sopenharmony_ci synchronize_rcu(); 72462306a36Sopenharmony_ci return ret; 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cistatic int dmc620_pmu_device_remove(struct platform_device *pdev) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci struct dmc620_pmu *dmc620_pmu = platform_get_drvdata(pdev); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci dmc620_pmu_put_irq(dmc620_pmu); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci /* perf will synchronise RCU before devres can free dmc620_pmu */ 73462306a36Sopenharmony_ci perf_pmu_unregister(&dmc620_pmu->pmu); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci return 0; 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_cistatic const struct acpi_device_id dmc620_acpi_match[] = { 74062306a36Sopenharmony_ci { "ARMHD620", 0}, 74162306a36Sopenharmony_ci {}, 74262306a36Sopenharmony_ci}; 74362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, dmc620_acpi_match); 74462306a36Sopenharmony_cistatic struct platform_driver dmc620_pmu_driver = { 74562306a36Sopenharmony_ci .driver = { 74662306a36Sopenharmony_ci .name = DMC620_DRVNAME, 74762306a36Sopenharmony_ci .acpi_match_table = dmc620_acpi_match, 74862306a36Sopenharmony_ci .suppress_bind_attrs = true, 74962306a36Sopenharmony_ci }, 75062306a36Sopenharmony_ci .probe = dmc620_pmu_device_probe, 75162306a36Sopenharmony_ci .remove = dmc620_pmu_device_remove, 75262306a36Sopenharmony_ci}; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_cistatic int __init dmc620_pmu_init(void) 75562306a36Sopenharmony_ci{ 75662306a36Sopenharmony_ci int ret; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci cpuhp_state_num = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, 75962306a36Sopenharmony_ci DMC620_DRVNAME, 76062306a36Sopenharmony_ci NULL, 76162306a36Sopenharmony_ci dmc620_pmu_cpu_teardown); 76262306a36Sopenharmony_ci if (cpuhp_state_num < 0) 76362306a36Sopenharmony_ci return cpuhp_state_num; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci ret = platform_driver_register(&dmc620_pmu_driver); 76662306a36Sopenharmony_ci if (ret) 76762306a36Sopenharmony_ci cpuhp_remove_multi_state(cpuhp_state_num); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci return ret; 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_cistatic void __exit dmc620_pmu_exit(void) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci platform_driver_unregister(&dmc620_pmu_driver); 77562306a36Sopenharmony_ci cpuhp_remove_multi_state(cpuhp_state_num); 77662306a36Sopenharmony_ci} 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_cimodule_init(dmc620_pmu_init); 77962306a36Sopenharmony_cimodule_exit(dmc620_pmu_exit); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ciMODULE_DESCRIPTION("Perf driver for the ARM DMC-620 memory controller"); 78262306a36Sopenharmony_ciMODULE_AUTHOR("Tuan Phan <tuanphan@os.amperecomputing.com"); 78362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 784