1832c927dSTai Nguyen /* 2832c927dSTai Nguyen * APM X-Gene SoC PMU (Performance Monitor Unit) 3832c927dSTai Nguyen * 4832c927dSTai Nguyen * Copyright (c) 2016, Applied Micro Circuits Corporation 5832c927dSTai Nguyen * Author: Hoan Tran <hotran@apm.com> 6832c927dSTai Nguyen * Tai Nguyen <ttnguyen@apm.com> 7832c927dSTai Nguyen * 8832c927dSTai Nguyen * This program is free software; you can redistribute it and/or modify it 9832c927dSTai Nguyen * under the terms of the GNU General Public License as published by the 10832c927dSTai Nguyen * Free Software Foundation; either version 2 of the License, or (at your 11832c927dSTai Nguyen * option) any later version. 12832c927dSTai Nguyen * 13832c927dSTai Nguyen * This program is distributed in the hope that it will be useful, 14832c927dSTai Nguyen * but WITHOUT ANY WARRANTY; without even the implied warranty of 15832c927dSTai Nguyen * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16832c927dSTai Nguyen * GNU General Public License for more details. 17832c927dSTai Nguyen * 18832c927dSTai Nguyen * You should have received a copy of the GNU General Public License 19832c927dSTai Nguyen * along with this program. If not, see <http://www.gnu.org/licenses/>. 20832c927dSTai Nguyen */ 21832c927dSTai Nguyen 22832c927dSTai Nguyen #include <linux/acpi.h> 23832c927dSTai Nguyen #include <linux/clk.h> 24832c927dSTai Nguyen #include <linux/cpumask.h> 25832c927dSTai Nguyen #include <linux/interrupt.h> 26832c927dSTai Nguyen #include <linux/io.h> 27832c927dSTai Nguyen #include <linux/mfd/syscon.h> 28832c927dSTai Nguyen #include <linux/of_address.h> 29832c927dSTai Nguyen #include <linux/of_fdt.h> 30832c927dSTai Nguyen #include <linux/of_irq.h> 31832c927dSTai Nguyen #include <linux/of_platform.h> 32832c927dSTai Nguyen #include <linux/perf_event.h> 33832c927dSTai Nguyen #include <linux/platform_device.h> 34832c927dSTai Nguyen #include <linux/regmap.h> 35832c927dSTai Nguyen #include <linux/slab.h> 36832c927dSTai Nguyen 37832c927dSTai Nguyen #define CSW_CSWCR 0x0000 38832c927dSTai Nguyen #define CSW_CSWCR_DUALMCB_MASK BIT(0) 39832c927dSTai Nguyen #define MCBADDRMR 0x0000 40832c927dSTai Nguyen #define MCBADDRMR_DUALMCU_MODE_MASK BIT(2) 41832c927dSTai Nguyen 42832c927dSTai Nguyen #define PCPPMU_INTSTATUS_REG 0x000 43832c927dSTai Nguyen #define PCPPMU_INTMASK_REG 0x004 44832c927dSTai Nguyen #define PCPPMU_INTMASK 0x0000000F 45832c927dSTai Nguyen #define PCPPMU_INTENMASK 0xFFFFFFFF 46832c927dSTai Nguyen #define PCPPMU_INTCLRMASK 0xFFFFFFF0 47832c927dSTai Nguyen #define PCPPMU_INT_MCU BIT(0) 48832c927dSTai Nguyen #define PCPPMU_INT_MCB BIT(1) 49832c927dSTai Nguyen #define PCPPMU_INT_L3C BIT(2) 50832c927dSTai Nguyen #define PCPPMU_INT_IOB BIT(3) 51832c927dSTai Nguyen 52832c927dSTai Nguyen #define PMU_MAX_COUNTERS 4 53832c927dSTai Nguyen #define PMU_CNT_MAX_PERIOD 0x100000000ULL 54832c927dSTai Nguyen #define PMU_OVERFLOW_MASK 0xF 55832c927dSTai Nguyen #define PMU_PMCR_E BIT(0) 56832c927dSTai Nguyen #define PMU_PMCR_P BIT(1) 57832c927dSTai Nguyen 58832c927dSTai Nguyen #define PMU_PMEVCNTR0 0x000 59832c927dSTai Nguyen #define PMU_PMEVCNTR1 0x004 60832c927dSTai Nguyen #define PMU_PMEVCNTR2 0x008 61832c927dSTai Nguyen #define PMU_PMEVCNTR3 0x00C 62832c927dSTai Nguyen #define PMU_PMEVTYPER0 0x400 63832c927dSTai Nguyen #define PMU_PMEVTYPER1 0x404 64832c927dSTai Nguyen #define PMU_PMEVTYPER2 0x408 65832c927dSTai Nguyen #define PMU_PMEVTYPER3 0x40C 66832c927dSTai Nguyen #define PMU_PMAMR0 0xA00 67832c927dSTai Nguyen #define PMU_PMAMR1 0xA04 68832c927dSTai Nguyen #define PMU_PMCNTENSET 0xC00 69832c927dSTai Nguyen #define PMU_PMCNTENCLR 0xC20 70832c927dSTai Nguyen #define PMU_PMINTENSET 0xC40 71832c927dSTai Nguyen #define PMU_PMINTENCLR 0xC60 72832c927dSTai Nguyen #define PMU_PMOVSR 0xC80 73832c927dSTai Nguyen #define PMU_PMCR 0xE04 74832c927dSTai Nguyen 75832c927dSTai Nguyen #define to_pmu_dev(p) container_of(p, struct xgene_pmu_dev, pmu) 76832c927dSTai Nguyen #define GET_CNTR(ev) (ev->hw.idx) 77832c927dSTai Nguyen #define GET_EVENTID(ev) (ev->hw.config & 0xFFULL) 78832c927dSTai Nguyen #define GET_AGENTID(ev) (ev->hw.config_base & 0xFFFFFFFFUL) 79832c927dSTai Nguyen #define GET_AGENT1ID(ev) ((ev->hw.config_base >> 32) & 0xFFFFFFFFUL) 80832c927dSTai Nguyen 81832c927dSTai Nguyen struct hw_pmu_info { 82832c927dSTai Nguyen u32 type; 83832c927dSTai Nguyen u32 enable_mask; 84832c927dSTai Nguyen void __iomem *csr; 85832c927dSTai Nguyen }; 86832c927dSTai Nguyen 87832c927dSTai Nguyen struct xgene_pmu_dev { 88832c927dSTai Nguyen struct hw_pmu_info *inf; 89832c927dSTai Nguyen struct xgene_pmu *parent; 90832c927dSTai Nguyen struct pmu pmu; 91832c927dSTai Nguyen u8 max_counters; 92832c927dSTai Nguyen DECLARE_BITMAP(cntr_assign_mask, PMU_MAX_COUNTERS); 93832c927dSTai Nguyen u64 max_period; 94832c927dSTai Nguyen const struct attribute_group **attr_groups; 95832c927dSTai Nguyen struct perf_event *pmu_counter_event[PMU_MAX_COUNTERS]; 96832c927dSTai Nguyen }; 97832c927dSTai Nguyen 98832c927dSTai Nguyen struct xgene_pmu { 99832c927dSTai Nguyen struct device *dev; 100832c927dSTai Nguyen int version; 101832c927dSTai Nguyen void __iomem *pcppmu_csr; 102832c927dSTai Nguyen u32 mcb_active_mask; 103832c927dSTai Nguyen u32 mc_active_mask; 104832c927dSTai Nguyen cpumask_t cpu; 105832c927dSTai Nguyen raw_spinlock_t lock; 106832c927dSTai Nguyen struct list_head l3cpmus; 107832c927dSTai Nguyen struct list_head iobpmus; 108832c927dSTai Nguyen struct list_head mcbpmus; 109832c927dSTai Nguyen struct list_head mcpmus; 110832c927dSTai Nguyen }; 111832c927dSTai Nguyen 112832c927dSTai Nguyen struct xgene_pmu_dev_ctx { 113832c927dSTai Nguyen char *name; 114832c927dSTai Nguyen struct list_head next; 115832c927dSTai Nguyen struct xgene_pmu_dev *pmu_dev; 116832c927dSTai Nguyen struct hw_pmu_info inf; 117832c927dSTai Nguyen }; 118832c927dSTai Nguyen 119832c927dSTai Nguyen struct xgene_pmu_data { 120832c927dSTai Nguyen int id; 121832c927dSTai Nguyen u32 data; 122832c927dSTai Nguyen }; 123832c927dSTai Nguyen 124832c927dSTai Nguyen enum xgene_pmu_version { 125832c927dSTai Nguyen PCP_PMU_V1 = 1, 126832c927dSTai Nguyen PCP_PMU_V2, 127832c927dSTai Nguyen }; 128832c927dSTai Nguyen 129832c927dSTai Nguyen enum xgene_pmu_dev_type { 130832c927dSTai Nguyen PMU_TYPE_L3C = 0, 131832c927dSTai Nguyen PMU_TYPE_IOB, 132832c927dSTai Nguyen PMU_TYPE_MCB, 133832c927dSTai Nguyen PMU_TYPE_MC, 134832c927dSTai Nguyen }; 135832c927dSTai Nguyen 136832c927dSTai Nguyen /* 137832c927dSTai Nguyen * sysfs format attributes 138832c927dSTai Nguyen */ 139832c927dSTai Nguyen static ssize_t xgene_pmu_format_show(struct device *dev, 140832c927dSTai Nguyen struct device_attribute *attr, char *buf) 141832c927dSTai Nguyen { 142832c927dSTai Nguyen struct dev_ext_attribute *eattr; 143832c927dSTai Nguyen 144832c927dSTai Nguyen eattr = container_of(attr, struct dev_ext_attribute, attr); 145832c927dSTai Nguyen return sprintf(buf, "%s\n", (char *) eattr->var); 146832c927dSTai Nguyen } 147832c927dSTai Nguyen 148832c927dSTai Nguyen #define XGENE_PMU_FORMAT_ATTR(_name, _config) \ 149832c927dSTai Nguyen (&((struct dev_ext_attribute[]) { \ 150832c927dSTai Nguyen { .attr = __ATTR(_name, S_IRUGO, xgene_pmu_format_show, NULL), \ 151832c927dSTai Nguyen .var = (void *) _config, } \ 152832c927dSTai Nguyen })[0].attr.attr) 153832c927dSTai Nguyen 154832c927dSTai Nguyen static struct attribute *l3c_pmu_format_attrs[] = { 155832c927dSTai Nguyen XGENE_PMU_FORMAT_ATTR(l3c_eventid, "config:0-7"), 156832c927dSTai Nguyen XGENE_PMU_FORMAT_ATTR(l3c_agentid, "config1:0-9"), 157832c927dSTai Nguyen NULL, 158832c927dSTai Nguyen }; 159832c927dSTai Nguyen 160832c927dSTai Nguyen static struct attribute *iob_pmu_format_attrs[] = { 161832c927dSTai Nguyen XGENE_PMU_FORMAT_ATTR(iob_eventid, "config:0-7"), 162832c927dSTai Nguyen XGENE_PMU_FORMAT_ATTR(iob_agentid, "config1:0-63"), 163832c927dSTai Nguyen NULL, 164832c927dSTai Nguyen }; 165832c927dSTai Nguyen 166832c927dSTai Nguyen static struct attribute *mcb_pmu_format_attrs[] = { 167832c927dSTai Nguyen XGENE_PMU_FORMAT_ATTR(mcb_eventid, "config:0-5"), 168832c927dSTai Nguyen XGENE_PMU_FORMAT_ATTR(mcb_agentid, "config1:0-9"), 169832c927dSTai Nguyen NULL, 170832c927dSTai Nguyen }; 171832c927dSTai Nguyen 172832c927dSTai Nguyen static struct attribute *mc_pmu_format_attrs[] = { 173832c927dSTai Nguyen XGENE_PMU_FORMAT_ATTR(mc_eventid, "config:0-28"), 174832c927dSTai Nguyen NULL, 175832c927dSTai Nguyen }; 176832c927dSTai Nguyen 177832c927dSTai Nguyen static const struct attribute_group l3c_pmu_format_attr_group = { 178832c927dSTai Nguyen .name = "format", 179832c927dSTai Nguyen .attrs = l3c_pmu_format_attrs, 180832c927dSTai Nguyen }; 181832c927dSTai Nguyen 182832c927dSTai Nguyen static const struct attribute_group iob_pmu_format_attr_group = { 183832c927dSTai Nguyen .name = "format", 184832c927dSTai Nguyen .attrs = iob_pmu_format_attrs, 185832c927dSTai Nguyen }; 186832c927dSTai Nguyen 187832c927dSTai Nguyen static const struct attribute_group mcb_pmu_format_attr_group = { 188832c927dSTai Nguyen .name = "format", 189832c927dSTai Nguyen .attrs = mcb_pmu_format_attrs, 190832c927dSTai Nguyen }; 191832c927dSTai Nguyen 192832c927dSTai Nguyen static const struct attribute_group mc_pmu_format_attr_group = { 193832c927dSTai Nguyen .name = "format", 194832c927dSTai Nguyen .attrs = mc_pmu_format_attrs, 195832c927dSTai Nguyen }; 196832c927dSTai Nguyen 197832c927dSTai Nguyen /* 198832c927dSTai Nguyen * sysfs event attributes 199832c927dSTai Nguyen */ 200832c927dSTai Nguyen static ssize_t xgene_pmu_event_show(struct device *dev, 201832c927dSTai Nguyen struct device_attribute *attr, char *buf) 202832c927dSTai Nguyen { 203832c927dSTai Nguyen struct dev_ext_attribute *eattr; 204832c927dSTai Nguyen 205832c927dSTai Nguyen eattr = container_of(attr, struct dev_ext_attribute, attr); 206832c927dSTai Nguyen return sprintf(buf, "config=0x%lx\n", (unsigned long) eattr->var); 207832c927dSTai Nguyen } 208832c927dSTai Nguyen 209832c927dSTai Nguyen #define XGENE_PMU_EVENT_ATTR(_name, _config) \ 210832c927dSTai Nguyen (&((struct dev_ext_attribute[]) { \ 211832c927dSTai Nguyen { .attr = __ATTR(_name, S_IRUGO, xgene_pmu_event_show, NULL), \ 212832c927dSTai Nguyen .var = (void *) _config, } \ 213832c927dSTai Nguyen })[0].attr.attr) 214832c927dSTai Nguyen 215832c927dSTai Nguyen static struct attribute *l3c_pmu_events_attrs[] = { 216832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(cycle-count, 0x00), 217832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(cycle-count-div-64, 0x01), 218832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(read-hit, 0x02), 219832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(read-miss, 0x03), 220832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(write-need-replacement, 0x06), 221832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(write-not-need-replacement, 0x07), 222832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(tq-full, 0x08), 223832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(ackq-full, 0x09), 224832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(wdb-full, 0x0a), 225832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(bank-fifo-full, 0x0b), 226832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(odb-full, 0x0c), 227832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(wbq-full, 0x0d), 228832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(bank-conflict-fifo-issue, 0x0e), 229832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(bank-fifo-issue, 0x0f), 230832c927dSTai Nguyen NULL, 231832c927dSTai Nguyen }; 232832c927dSTai Nguyen 233832c927dSTai Nguyen static struct attribute *iob_pmu_events_attrs[] = { 234832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(cycle-count, 0x00), 235832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(cycle-count-div-64, 0x01), 236832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(axi0-read, 0x02), 237832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(axi0-read-partial, 0x03), 238832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(axi1-read, 0x04), 239832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(axi1-read-partial, 0x05), 240832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(csw-read-block, 0x06), 241832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(csw-read-partial, 0x07), 242832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(axi0-write, 0x10), 243832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(axi0-write-partial, 0x11), 244832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(axi1-write, 0x13), 245832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(axi1-write-partial, 0x14), 246832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(csw-inbound-dirty, 0x16), 247832c927dSTai Nguyen NULL, 248832c927dSTai Nguyen }; 249832c927dSTai Nguyen 250832c927dSTai Nguyen static struct attribute *mcb_pmu_events_attrs[] = { 251832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(cycle-count, 0x00), 252832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(cycle-count-div-64, 0x01), 253832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(csw-read, 0x02), 254832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(csw-write-request, 0x03), 255832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(mcb-csw-stall, 0x04), 256832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(cancel-read-gack, 0x05), 257832c927dSTai Nguyen NULL, 258832c927dSTai Nguyen }; 259832c927dSTai Nguyen 260832c927dSTai Nguyen static struct attribute *mc_pmu_events_attrs[] = { 261832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(cycle-count, 0x00), 262832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(cycle-count-div-64, 0x01), 263832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(act-cmd-sent, 0x02), 264832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(pre-cmd-sent, 0x03), 265832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(rd-cmd-sent, 0x04), 266832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(rda-cmd-sent, 0x05), 267832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(wr-cmd-sent, 0x06), 268832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(wra-cmd-sent, 0x07), 269832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(pde-cmd-sent, 0x08), 270832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(sre-cmd-sent, 0x09), 271832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(prea-cmd-sent, 0x0a), 272832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(ref-cmd-sent, 0x0b), 273832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(rd-rda-cmd-sent, 0x0c), 274832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(wr-wra-cmd-sent, 0x0d), 275832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(in-rd-collision, 0x0e), 276832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(in-wr-collision, 0x0f), 277832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(collision-queue-not-empty, 0x10), 278832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(collision-queue-full, 0x11), 279832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(mcu-request, 0x12), 280832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(mcu-rd-request, 0x13), 281832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(mcu-hp-rd-request, 0x14), 282832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(mcu-wr-request, 0x15), 283832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(mcu-rd-proceed-all, 0x16), 284832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(mcu-rd-proceed-cancel, 0x17), 285832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(mcu-rd-response, 0x18), 286832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(mcu-rd-proceed-speculative-all, 0x19), 287832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(mcu-rd-proceed-speculative-cancel, 0x1a), 288832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(mcu-wr-proceed-all, 0x1b), 289832c927dSTai Nguyen XGENE_PMU_EVENT_ATTR(mcu-wr-proceed-cancel, 0x1c), 290832c927dSTai Nguyen NULL, 291832c927dSTai Nguyen }; 292832c927dSTai Nguyen 293832c927dSTai Nguyen static const struct attribute_group l3c_pmu_events_attr_group = { 294832c927dSTai Nguyen .name = "events", 295832c927dSTai Nguyen .attrs = l3c_pmu_events_attrs, 296832c927dSTai Nguyen }; 297832c927dSTai Nguyen 298832c927dSTai Nguyen static const struct attribute_group iob_pmu_events_attr_group = { 299832c927dSTai Nguyen .name = "events", 300832c927dSTai Nguyen .attrs = iob_pmu_events_attrs, 301832c927dSTai Nguyen }; 302832c927dSTai Nguyen 303832c927dSTai Nguyen static const struct attribute_group mcb_pmu_events_attr_group = { 304832c927dSTai Nguyen .name = "events", 305832c927dSTai Nguyen .attrs = mcb_pmu_events_attrs, 306832c927dSTai Nguyen }; 307832c927dSTai Nguyen 308832c927dSTai Nguyen static const struct attribute_group mc_pmu_events_attr_group = { 309832c927dSTai Nguyen .name = "events", 310832c927dSTai Nguyen .attrs = mc_pmu_events_attrs, 311832c927dSTai Nguyen }; 312832c927dSTai Nguyen 313832c927dSTai Nguyen /* 314832c927dSTai Nguyen * sysfs cpumask attributes 315832c927dSTai Nguyen */ 316832c927dSTai Nguyen static ssize_t xgene_pmu_cpumask_show(struct device *dev, 317832c927dSTai Nguyen struct device_attribute *attr, char *buf) 318832c927dSTai Nguyen { 319832c927dSTai Nguyen struct xgene_pmu_dev *pmu_dev = to_pmu_dev(dev_get_drvdata(dev)); 320832c927dSTai Nguyen 321832c927dSTai Nguyen return cpumap_print_to_pagebuf(true, buf, &pmu_dev->parent->cpu); 322832c927dSTai Nguyen } 323832c927dSTai Nguyen 324832c927dSTai Nguyen static DEVICE_ATTR(cpumask, S_IRUGO, xgene_pmu_cpumask_show, NULL); 325832c927dSTai Nguyen 326832c927dSTai Nguyen static struct attribute *xgene_pmu_cpumask_attrs[] = { 327832c927dSTai Nguyen &dev_attr_cpumask.attr, 328832c927dSTai Nguyen NULL, 329832c927dSTai Nguyen }; 330832c927dSTai Nguyen 331832c927dSTai Nguyen static const struct attribute_group pmu_cpumask_attr_group = { 332832c927dSTai Nguyen .attrs = xgene_pmu_cpumask_attrs, 333832c927dSTai Nguyen }; 334832c927dSTai Nguyen 335832c927dSTai Nguyen /* 336832c927dSTai Nguyen * Per PMU device attribute groups 337832c927dSTai Nguyen */ 338832c927dSTai Nguyen static const struct attribute_group *l3c_pmu_attr_groups[] = { 339832c927dSTai Nguyen &l3c_pmu_format_attr_group, 340832c927dSTai Nguyen &pmu_cpumask_attr_group, 341832c927dSTai Nguyen &l3c_pmu_events_attr_group, 342832c927dSTai Nguyen NULL 343832c927dSTai Nguyen }; 344832c927dSTai Nguyen 345832c927dSTai Nguyen static const struct attribute_group *iob_pmu_attr_groups[] = { 346832c927dSTai Nguyen &iob_pmu_format_attr_group, 347832c927dSTai Nguyen &pmu_cpumask_attr_group, 348832c927dSTai Nguyen &iob_pmu_events_attr_group, 349832c927dSTai Nguyen NULL 350832c927dSTai Nguyen }; 351832c927dSTai Nguyen 352832c927dSTai Nguyen static const struct attribute_group *mcb_pmu_attr_groups[] = { 353832c927dSTai Nguyen &mcb_pmu_format_attr_group, 354832c927dSTai Nguyen &pmu_cpumask_attr_group, 355832c927dSTai Nguyen &mcb_pmu_events_attr_group, 356832c927dSTai Nguyen NULL 357832c927dSTai Nguyen }; 358832c927dSTai Nguyen 359832c927dSTai Nguyen static const struct attribute_group *mc_pmu_attr_groups[] = { 360832c927dSTai Nguyen &mc_pmu_format_attr_group, 361832c927dSTai Nguyen &pmu_cpumask_attr_group, 362832c927dSTai Nguyen &mc_pmu_events_attr_group, 363832c927dSTai Nguyen NULL 364832c927dSTai Nguyen }; 365832c927dSTai Nguyen 366832c927dSTai Nguyen static int get_next_avail_cntr(struct xgene_pmu_dev *pmu_dev) 367832c927dSTai Nguyen { 368832c927dSTai Nguyen int cntr; 369832c927dSTai Nguyen 370832c927dSTai Nguyen cntr = find_first_zero_bit(pmu_dev->cntr_assign_mask, 371832c927dSTai Nguyen pmu_dev->max_counters); 372832c927dSTai Nguyen if (cntr == pmu_dev->max_counters) 373832c927dSTai Nguyen return -ENOSPC; 374832c927dSTai Nguyen set_bit(cntr, pmu_dev->cntr_assign_mask); 375832c927dSTai Nguyen 376832c927dSTai Nguyen return cntr; 377832c927dSTai Nguyen } 378832c927dSTai Nguyen 379832c927dSTai Nguyen static void clear_avail_cntr(struct xgene_pmu_dev *pmu_dev, int cntr) 380832c927dSTai Nguyen { 381832c927dSTai Nguyen clear_bit(cntr, pmu_dev->cntr_assign_mask); 382832c927dSTai Nguyen } 383832c927dSTai Nguyen 384832c927dSTai Nguyen static inline void xgene_pmu_mask_int(struct xgene_pmu *xgene_pmu) 385832c927dSTai Nguyen { 386832c927dSTai Nguyen writel(PCPPMU_INTENMASK, xgene_pmu->pcppmu_csr + PCPPMU_INTMASK_REG); 387832c927dSTai Nguyen } 388832c927dSTai Nguyen 389832c927dSTai Nguyen static inline void xgene_pmu_unmask_int(struct xgene_pmu *xgene_pmu) 390832c927dSTai Nguyen { 391832c927dSTai Nguyen writel(PCPPMU_INTCLRMASK, xgene_pmu->pcppmu_csr + PCPPMU_INTMASK_REG); 392832c927dSTai Nguyen } 393832c927dSTai Nguyen 394832c927dSTai Nguyen static inline u32 xgene_pmu_read_counter(struct xgene_pmu_dev *pmu_dev, int idx) 395832c927dSTai Nguyen { 396832c927dSTai Nguyen return readl(pmu_dev->inf->csr + PMU_PMEVCNTR0 + (4 * idx)); 397832c927dSTai Nguyen } 398832c927dSTai Nguyen 399832c927dSTai Nguyen static inline void 400832c927dSTai Nguyen xgene_pmu_write_counter(struct xgene_pmu_dev *pmu_dev, int idx, u32 val) 401832c927dSTai Nguyen { 402832c927dSTai Nguyen writel(val, pmu_dev->inf->csr + PMU_PMEVCNTR0 + (4 * idx)); 403832c927dSTai Nguyen } 404832c927dSTai Nguyen 405832c927dSTai Nguyen static inline void 406832c927dSTai Nguyen xgene_pmu_write_evttype(struct xgene_pmu_dev *pmu_dev, int idx, u32 val) 407832c927dSTai Nguyen { 408832c927dSTai Nguyen writel(val, pmu_dev->inf->csr + PMU_PMEVTYPER0 + (4 * idx)); 409832c927dSTai Nguyen } 410832c927dSTai Nguyen 411832c927dSTai Nguyen static inline void 412832c927dSTai Nguyen xgene_pmu_write_agentmsk(struct xgene_pmu_dev *pmu_dev, u32 val) 413832c927dSTai Nguyen { 414832c927dSTai Nguyen writel(val, pmu_dev->inf->csr + PMU_PMAMR0); 415832c927dSTai Nguyen } 416832c927dSTai Nguyen 417832c927dSTai Nguyen static inline void 418832c927dSTai Nguyen xgene_pmu_write_agent1msk(struct xgene_pmu_dev *pmu_dev, u32 val) 419832c927dSTai Nguyen { 420832c927dSTai Nguyen writel(val, pmu_dev->inf->csr + PMU_PMAMR1); 421832c927dSTai Nguyen } 422832c927dSTai Nguyen 423832c927dSTai Nguyen static inline void 424832c927dSTai Nguyen xgene_pmu_enable_counter(struct xgene_pmu_dev *pmu_dev, int idx) 425832c927dSTai Nguyen { 426832c927dSTai Nguyen u32 val; 427832c927dSTai Nguyen 428832c927dSTai Nguyen val = readl(pmu_dev->inf->csr + PMU_PMCNTENSET); 429832c927dSTai Nguyen val |= 1 << idx; 430832c927dSTai Nguyen writel(val, pmu_dev->inf->csr + PMU_PMCNTENSET); 431832c927dSTai Nguyen } 432832c927dSTai Nguyen 433832c927dSTai Nguyen static inline void 434832c927dSTai Nguyen xgene_pmu_disable_counter(struct xgene_pmu_dev *pmu_dev, int idx) 435832c927dSTai Nguyen { 436832c927dSTai Nguyen u32 val; 437832c927dSTai Nguyen 438832c927dSTai Nguyen val = readl(pmu_dev->inf->csr + PMU_PMCNTENCLR); 439832c927dSTai Nguyen val |= 1 << idx; 440832c927dSTai Nguyen writel(val, pmu_dev->inf->csr + PMU_PMCNTENCLR); 441832c927dSTai Nguyen } 442832c927dSTai Nguyen 443832c927dSTai Nguyen static inline void 444832c927dSTai Nguyen xgene_pmu_enable_counter_int(struct xgene_pmu_dev *pmu_dev, int idx) 445832c927dSTai Nguyen { 446832c927dSTai Nguyen u32 val; 447832c927dSTai Nguyen 448832c927dSTai Nguyen val = readl(pmu_dev->inf->csr + PMU_PMINTENSET); 449832c927dSTai Nguyen val |= 1 << idx; 450832c927dSTai Nguyen writel(val, pmu_dev->inf->csr + PMU_PMINTENSET); 451832c927dSTai Nguyen } 452832c927dSTai Nguyen 453832c927dSTai Nguyen static inline void 454832c927dSTai Nguyen xgene_pmu_disable_counter_int(struct xgene_pmu_dev *pmu_dev, int idx) 455832c927dSTai Nguyen { 456832c927dSTai Nguyen u32 val; 457832c927dSTai Nguyen 458832c927dSTai Nguyen val = readl(pmu_dev->inf->csr + PMU_PMINTENCLR); 459832c927dSTai Nguyen val |= 1 << idx; 460832c927dSTai Nguyen writel(val, pmu_dev->inf->csr + PMU_PMINTENCLR); 461832c927dSTai Nguyen } 462832c927dSTai Nguyen 463832c927dSTai Nguyen static inline void xgene_pmu_reset_counters(struct xgene_pmu_dev *pmu_dev) 464832c927dSTai Nguyen { 465832c927dSTai Nguyen u32 val; 466832c927dSTai Nguyen 467832c927dSTai Nguyen val = readl(pmu_dev->inf->csr + PMU_PMCR); 468832c927dSTai Nguyen val |= PMU_PMCR_P; 469832c927dSTai Nguyen writel(val, pmu_dev->inf->csr + PMU_PMCR); 470832c927dSTai Nguyen } 471832c927dSTai Nguyen 472832c927dSTai Nguyen static inline void xgene_pmu_start_counters(struct xgene_pmu_dev *pmu_dev) 473832c927dSTai Nguyen { 474832c927dSTai Nguyen u32 val; 475832c927dSTai Nguyen 476832c927dSTai Nguyen val = readl(pmu_dev->inf->csr + PMU_PMCR); 477832c927dSTai Nguyen val |= PMU_PMCR_E; 478832c927dSTai Nguyen writel(val, pmu_dev->inf->csr + PMU_PMCR); 479832c927dSTai Nguyen } 480832c927dSTai Nguyen 481832c927dSTai Nguyen static inline void xgene_pmu_stop_counters(struct xgene_pmu_dev *pmu_dev) 482832c927dSTai Nguyen { 483832c927dSTai Nguyen u32 val; 484832c927dSTai Nguyen 485832c927dSTai Nguyen val = readl(pmu_dev->inf->csr + PMU_PMCR); 486832c927dSTai Nguyen val &= ~PMU_PMCR_E; 487832c927dSTai Nguyen writel(val, pmu_dev->inf->csr + PMU_PMCR); 488832c927dSTai Nguyen } 489832c927dSTai Nguyen 490832c927dSTai Nguyen static void xgene_perf_pmu_enable(struct pmu *pmu) 491832c927dSTai Nguyen { 492832c927dSTai Nguyen struct xgene_pmu_dev *pmu_dev = to_pmu_dev(pmu); 493832c927dSTai Nguyen int enabled = bitmap_weight(pmu_dev->cntr_assign_mask, 494832c927dSTai Nguyen pmu_dev->max_counters); 495832c927dSTai Nguyen 496832c927dSTai Nguyen if (!enabled) 497832c927dSTai Nguyen return; 498832c927dSTai Nguyen 499832c927dSTai Nguyen xgene_pmu_start_counters(pmu_dev); 500832c927dSTai Nguyen } 501832c927dSTai Nguyen 502832c927dSTai Nguyen static void xgene_perf_pmu_disable(struct pmu *pmu) 503832c927dSTai Nguyen { 504832c927dSTai Nguyen struct xgene_pmu_dev *pmu_dev = to_pmu_dev(pmu); 505832c927dSTai Nguyen 506832c927dSTai Nguyen xgene_pmu_stop_counters(pmu_dev); 507832c927dSTai Nguyen } 508832c927dSTai Nguyen 509832c927dSTai Nguyen static int xgene_perf_event_init(struct perf_event *event) 510832c927dSTai Nguyen { 511832c927dSTai Nguyen struct xgene_pmu_dev *pmu_dev = to_pmu_dev(event->pmu); 512832c927dSTai Nguyen struct hw_perf_event *hw = &event->hw; 513832c927dSTai Nguyen struct perf_event *sibling; 514832c927dSTai Nguyen 515832c927dSTai Nguyen /* Test the event attr type check for PMU enumeration */ 516832c927dSTai Nguyen if (event->attr.type != event->pmu->type) 517832c927dSTai Nguyen return -ENOENT; 518832c927dSTai Nguyen 519832c927dSTai Nguyen /* 520832c927dSTai Nguyen * SOC PMU counters are shared across all cores. 521832c927dSTai Nguyen * Therefore, it does not support per-process mode. 522832c927dSTai Nguyen * Also, it does not support event sampling mode. 523832c927dSTai Nguyen */ 524832c927dSTai Nguyen if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK) 525832c927dSTai Nguyen return -EINVAL; 526832c927dSTai Nguyen 527832c927dSTai Nguyen /* SOC counters do not have usr/os/guest/host bits */ 528832c927dSTai Nguyen if (event->attr.exclude_user || event->attr.exclude_kernel || 529832c927dSTai Nguyen event->attr.exclude_host || event->attr.exclude_guest) 530832c927dSTai Nguyen return -EINVAL; 531832c927dSTai Nguyen 532832c927dSTai Nguyen if (event->cpu < 0) 533832c927dSTai Nguyen return -EINVAL; 534832c927dSTai Nguyen /* 535832c927dSTai Nguyen * Many perf core operations (eg. events rotation) operate on a 536832c927dSTai Nguyen * single CPU context. This is obvious for CPU PMUs, where one 537832c927dSTai Nguyen * expects the same sets of events being observed on all CPUs, 538832c927dSTai Nguyen * but can lead to issues for off-core PMUs, where each 539832c927dSTai Nguyen * event could be theoretically assigned to a different CPU. To 540832c927dSTai Nguyen * mitigate this, we enforce CPU assignment to one, selected 541832c927dSTai Nguyen * processor (the one described in the "cpumask" attribute). 542832c927dSTai Nguyen */ 543832c927dSTai Nguyen event->cpu = cpumask_first(&pmu_dev->parent->cpu); 544832c927dSTai Nguyen 545832c927dSTai Nguyen hw->config = event->attr.config; 546832c927dSTai Nguyen /* 547832c927dSTai Nguyen * Each bit of the config1 field represents an agent from which the 548832c927dSTai Nguyen * request of the event come. The event is counted only if it's caused 549832c927dSTai Nguyen * by a request of an agent has the bit cleared. 550832c927dSTai Nguyen * By default, the event is counted for all agents. 551832c927dSTai Nguyen */ 552832c927dSTai Nguyen hw->config_base = event->attr.config1; 553832c927dSTai Nguyen 554832c927dSTai Nguyen /* 555832c927dSTai Nguyen * We must NOT create groups containing mixed PMUs, although software 556832c927dSTai Nguyen * events are acceptable 557832c927dSTai Nguyen */ 558832c927dSTai Nguyen if (event->group_leader->pmu != event->pmu && 559832c927dSTai Nguyen !is_software_event(event->group_leader)) 560832c927dSTai Nguyen return -EINVAL; 561832c927dSTai Nguyen 562832c927dSTai Nguyen list_for_each_entry(sibling, &event->group_leader->sibling_list, 563832c927dSTai Nguyen group_entry) 564832c927dSTai Nguyen if (sibling->pmu != event->pmu && 565832c927dSTai Nguyen !is_software_event(sibling)) 566832c927dSTai Nguyen return -EINVAL; 567832c927dSTai Nguyen 568832c927dSTai Nguyen return 0; 569832c927dSTai Nguyen } 570832c927dSTai Nguyen 571832c927dSTai Nguyen static void xgene_perf_enable_event(struct perf_event *event) 572832c927dSTai Nguyen { 573832c927dSTai Nguyen struct xgene_pmu_dev *pmu_dev = to_pmu_dev(event->pmu); 574832c927dSTai Nguyen 575832c927dSTai Nguyen xgene_pmu_write_evttype(pmu_dev, GET_CNTR(event), GET_EVENTID(event)); 576832c927dSTai Nguyen xgene_pmu_write_agentmsk(pmu_dev, ~((u32)GET_AGENTID(event))); 577832c927dSTai Nguyen if (pmu_dev->inf->type == PMU_TYPE_IOB) 578832c927dSTai Nguyen xgene_pmu_write_agent1msk(pmu_dev, ~((u32)GET_AGENT1ID(event))); 579832c927dSTai Nguyen 580832c927dSTai Nguyen xgene_pmu_enable_counter(pmu_dev, GET_CNTR(event)); 581832c927dSTai Nguyen xgene_pmu_enable_counter_int(pmu_dev, GET_CNTR(event)); 582832c927dSTai Nguyen } 583832c927dSTai Nguyen 584832c927dSTai Nguyen static void xgene_perf_disable_event(struct perf_event *event) 585832c927dSTai Nguyen { 586832c927dSTai Nguyen struct xgene_pmu_dev *pmu_dev = to_pmu_dev(event->pmu); 587832c927dSTai Nguyen 588832c927dSTai Nguyen xgene_pmu_disable_counter(pmu_dev, GET_CNTR(event)); 589832c927dSTai Nguyen xgene_pmu_disable_counter_int(pmu_dev, GET_CNTR(event)); 590832c927dSTai Nguyen } 591832c927dSTai Nguyen 592832c927dSTai Nguyen static void xgene_perf_event_set_period(struct perf_event *event) 593832c927dSTai Nguyen { 594832c927dSTai Nguyen struct xgene_pmu_dev *pmu_dev = to_pmu_dev(event->pmu); 595832c927dSTai Nguyen struct hw_perf_event *hw = &event->hw; 596832c927dSTai Nguyen /* 597832c927dSTai Nguyen * The X-Gene PMU counters have a period of 2^32. To account for the 598832c927dSTai Nguyen * possiblity of extreme interrupt latency we program for a period of 599832c927dSTai Nguyen * half that. Hopefully we can handle the interrupt before another 2^31 600832c927dSTai Nguyen * events occur and the counter overtakes its previous value. 601832c927dSTai Nguyen */ 602832c927dSTai Nguyen u64 val = 1ULL << 31; 603832c927dSTai Nguyen 604832c927dSTai Nguyen local64_set(&hw->prev_count, val); 605832c927dSTai Nguyen xgene_pmu_write_counter(pmu_dev, hw->idx, (u32) val); 606832c927dSTai Nguyen } 607832c927dSTai Nguyen 608832c927dSTai Nguyen static void xgene_perf_event_update(struct perf_event *event) 609832c927dSTai Nguyen { 610832c927dSTai Nguyen struct xgene_pmu_dev *pmu_dev = to_pmu_dev(event->pmu); 611832c927dSTai Nguyen struct hw_perf_event *hw = &event->hw; 612832c927dSTai Nguyen u64 delta, prev_raw_count, new_raw_count; 613832c927dSTai Nguyen 614832c927dSTai Nguyen again: 615832c927dSTai Nguyen prev_raw_count = local64_read(&hw->prev_count); 616832c927dSTai Nguyen new_raw_count = xgene_pmu_read_counter(pmu_dev, GET_CNTR(event)); 617832c927dSTai Nguyen 618832c927dSTai Nguyen if (local64_cmpxchg(&hw->prev_count, prev_raw_count, 619832c927dSTai Nguyen new_raw_count) != prev_raw_count) 620832c927dSTai Nguyen goto again; 621832c927dSTai Nguyen 622832c927dSTai Nguyen delta = (new_raw_count - prev_raw_count) & pmu_dev->max_period; 623832c927dSTai Nguyen 624832c927dSTai Nguyen local64_add(delta, &event->count); 625832c927dSTai Nguyen } 626832c927dSTai Nguyen 627832c927dSTai Nguyen static void xgene_perf_read(struct perf_event *event) 628832c927dSTai Nguyen { 629832c927dSTai Nguyen xgene_perf_event_update(event); 630832c927dSTai Nguyen } 631832c927dSTai Nguyen 632832c927dSTai Nguyen static void xgene_perf_start(struct perf_event *event, int flags) 633832c927dSTai Nguyen { 634832c927dSTai Nguyen struct xgene_pmu_dev *pmu_dev = to_pmu_dev(event->pmu); 635832c927dSTai Nguyen struct hw_perf_event *hw = &event->hw; 636832c927dSTai Nguyen 637832c927dSTai Nguyen if (WARN_ON_ONCE(!(hw->state & PERF_HES_STOPPED))) 638832c927dSTai Nguyen return; 639832c927dSTai Nguyen 640832c927dSTai Nguyen WARN_ON_ONCE(!(hw->state & PERF_HES_UPTODATE)); 641832c927dSTai Nguyen hw->state = 0; 642832c927dSTai Nguyen 643832c927dSTai Nguyen xgene_perf_event_set_period(event); 644832c927dSTai Nguyen 645832c927dSTai Nguyen if (flags & PERF_EF_RELOAD) { 646832c927dSTai Nguyen u64 prev_raw_count = local64_read(&hw->prev_count); 647832c927dSTai Nguyen 648832c927dSTai Nguyen xgene_pmu_write_counter(pmu_dev, GET_CNTR(event), 649832c927dSTai Nguyen (u32) prev_raw_count); 650832c927dSTai Nguyen } 651832c927dSTai Nguyen 652832c927dSTai Nguyen xgene_perf_enable_event(event); 653832c927dSTai Nguyen perf_event_update_userpage(event); 654832c927dSTai Nguyen } 655832c927dSTai Nguyen 656832c927dSTai Nguyen static void xgene_perf_stop(struct perf_event *event, int flags) 657832c927dSTai Nguyen { 658832c927dSTai Nguyen struct hw_perf_event *hw = &event->hw; 659832c927dSTai Nguyen u64 config; 660832c927dSTai Nguyen 661832c927dSTai Nguyen if (hw->state & PERF_HES_UPTODATE) 662832c927dSTai Nguyen return; 663832c927dSTai Nguyen 664832c927dSTai Nguyen xgene_perf_disable_event(event); 665832c927dSTai Nguyen WARN_ON_ONCE(hw->state & PERF_HES_STOPPED); 666832c927dSTai Nguyen hw->state |= PERF_HES_STOPPED; 667832c927dSTai Nguyen 668832c927dSTai Nguyen if (hw->state & PERF_HES_UPTODATE) 669832c927dSTai Nguyen return; 670832c927dSTai Nguyen 671832c927dSTai Nguyen config = hw->config; 672832c927dSTai Nguyen xgene_perf_read(event); 673832c927dSTai Nguyen hw->state |= PERF_HES_UPTODATE; 674832c927dSTai Nguyen } 675832c927dSTai Nguyen 676832c927dSTai Nguyen static int xgene_perf_add(struct perf_event *event, int flags) 677832c927dSTai Nguyen { 678832c927dSTai Nguyen struct xgene_pmu_dev *pmu_dev = to_pmu_dev(event->pmu); 679832c927dSTai Nguyen struct hw_perf_event *hw = &event->hw; 680832c927dSTai Nguyen 681832c927dSTai Nguyen hw->state = PERF_HES_UPTODATE | PERF_HES_STOPPED; 682832c927dSTai Nguyen 683832c927dSTai Nguyen /* Allocate an event counter */ 684832c927dSTai Nguyen hw->idx = get_next_avail_cntr(pmu_dev); 685832c927dSTai Nguyen if (hw->idx < 0) 686832c927dSTai Nguyen return -EAGAIN; 687832c927dSTai Nguyen 688832c927dSTai Nguyen /* Update counter event pointer for Interrupt handler */ 689832c927dSTai Nguyen pmu_dev->pmu_counter_event[hw->idx] = event; 690832c927dSTai Nguyen 691832c927dSTai Nguyen if (flags & PERF_EF_START) 692832c927dSTai Nguyen xgene_perf_start(event, PERF_EF_RELOAD); 693832c927dSTai Nguyen 694832c927dSTai Nguyen return 0; 695832c927dSTai Nguyen } 696832c927dSTai Nguyen 697832c927dSTai Nguyen static void xgene_perf_del(struct perf_event *event, int flags) 698832c927dSTai Nguyen { 699832c927dSTai Nguyen struct xgene_pmu_dev *pmu_dev = to_pmu_dev(event->pmu); 700832c927dSTai Nguyen struct hw_perf_event *hw = &event->hw; 701832c927dSTai Nguyen 702832c927dSTai Nguyen xgene_perf_stop(event, PERF_EF_UPDATE); 703832c927dSTai Nguyen 704832c927dSTai Nguyen /* clear the assigned counter */ 705832c927dSTai Nguyen clear_avail_cntr(pmu_dev, GET_CNTR(event)); 706832c927dSTai Nguyen 707832c927dSTai Nguyen perf_event_update_userpage(event); 708832c927dSTai Nguyen pmu_dev->pmu_counter_event[hw->idx] = NULL; 709832c927dSTai Nguyen } 710832c927dSTai Nguyen 711832c927dSTai Nguyen static int xgene_init_perf(struct xgene_pmu_dev *pmu_dev, char *name) 712832c927dSTai Nguyen { 713832c927dSTai Nguyen struct xgene_pmu *xgene_pmu; 714832c927dSTai Nguyen 715832c927dSTai Nguyen pmu_dev->max_period = PMU_CNT_MAX_PERIOD - 1; 716832c927dSTai Nguyen /* First version PMU supports only single event counter */ 717832c927dSTai Nguyen xgene_pmu = pmu_dev->parent; 718832c927dSTai Nguyen if (xgene_pmu->version == PCP_PMU_V1) 719832c927dSTai Nguyen pmu_dev->max_counters = 1; 720832c927dSTai Nguyen else 721832c927dSTai Nguyen pmu_dev->max_counters = PMU_MAX_COUNTERS; 722832c927dSTai Nguyen 723832c927dSTai Nguyen /* Perf driver registration */ 724832c927dSTai Nguyen pmu_dev->pmu = (struct pmu) { 725832c927dSTai Nguyen .attr_groups = pmu_dev->attr_groups, 726832c927dSTai Nguyen .task_ctx_nr = perf_invalid_context, 727832c927dSTai Nguyen .pmu_enable = xgene_perf_pmu_enable, 728832c927dSTai Nguyen .pmu_disable = xgene_perf_pmu_disable, 729832c927dSTai Nguyen .event_init = xgene_perf_event_init, 730832c927dSTai Nguyen .add = xgene_perf_add, 731832c927dSTai Nguyen .del = xgene_perf_del, 732832c927dSTai Nguyen .start = xgene_perf_start, 733832c927dSTai Nguyen .stop = xgene_perf_stop, 734832c927dSTai Nguyen .read = xgene_perf_read, 735832c927dSTai Nguyen }; 736832c927dSTai Nguyen 737832c927dSTai Nguyen /* Hardware counter init */ 738832c927dSTai Nguyen xgene_pmu_stop_counters(pmu_dev); 739832c927dSTai Nguyen xgene_pmu_reset_counters(pmu_dev); 740832c927dSTai Nguyen 741832c927dSTai Nguyen return perf_pmu_register(&pmu_dev->pmu, name, -1); 742832c927dSTai Nguyen } 743832c927dSTai Nguyen 744832c927dSTai Nguyen static int 745832c927dSTai Nguyen xgene_pmu_dev_add(struct xgene_pmu *xgene_pmu, struct xgene_pmu_dev_ctx *ctx) 746832c927dSTai Nguyen { 747832c927dSTai Nguyen struct device *dev = xgene_pmu->dev; 748832c927dSTai Nguyen struct xgene_pmu_dev *pmu; 749832c927dSTai Nguyen int rc; 750832c927dSTai Nguyen 751832c927dSTai Nguyen pmu = devm_kzalloc(dev, sizeof(*pmu), GFP_KERNEL); 752832c927dSTai Nguyen if (!pmu) 753832c927dSTai Nguyen return -ENOMEM; 754832c927dSTai Nguyen pmu->parent = xgene_pmu; 755832c927dSTai Nguyen pmu->inf = &ctx->inf; 756832c927dSTai Nguyen ctx->pmu_dev = pmu; 757832c927dSTai Nguyen 758832c927dSTai Nguyen switch (pmu->inf->type) { 759832c927dSTai Nguyen case PMU_TYPE_L3C: 760832c927dSTai Nguyen pmu->attr_groups = l3c_pmu_attr_groups; 761832c927dSTai Nguyen break; 762832c927dSTai Nguyen case PMU_TYPE_IOB: 763832c927dSTai Nguyen pmu->attr_groups = iob_pmu_attr_groups; 764832c927dSTai Nguyen break; 765832c927dSTai Nguyen case PMU_TYPE_MCB: 766832c927dSTai Nguyen if (!(xgene_pmu->mcb_active_mask & pmu->inf->enable_mask)) 767832c927dSTai Nguyen goto dev_err; 768832c927dSTai Nguyen pmu->attr_groups = mcb_pmu_attr_groups; 769832c927dSTai Nguyen break; 770832c927dSTai Nguyen case PMU_TYPE_MC: 771832c927dSTai Nguyen if (!(xgene_pmu->mc_active_mask & pmu->inf->enable_mask)) 772832c927dSTai Nguyen goto dev_err; 773832c927dSTai Nguyen pmu->attr_groups = mc_pmu_attr_groups; 774832c927dSTai Nguyen break; 775832c927dSTai Nguyen default: 776832c927dSTai Nguyen return -EINVAL; 777832c927dSTai Nguyen } 778832c927dSTai Nguyen 779832c927dSTai Nguyen rc = xgene_init_perf(pmu, ctx->name); 780832c927dSTai Nguyen if (rc) { 781832c927dSTai Nguyen dev_err(dev, "%s PMU: Failed to init perf driver\n", ctx->name); 782832c927dSTai Nguyen goto dev_err; 783832c927dSTai Nguyen } 784832c927dSTai Nguyen 785832c927dSTai Nguyen dev_info(dev, "%s PMU registered\n", ctx->name); 786832c927dSTai Nguyen 787832c927dSTai Nguyen return rc; 788832c927dSTai Nguyen 789832c927dSTai Nguyen dev_err: 790832c927dSTai Nguyen devm_kfree(dev, pmu); 791832c927dSTai Nguyen return -ENODEV; 792832c927dSTai Nguyen } 793832c927dSTai Nguyen 794832c927dSTai Nguyen static void _xgene_pmu_isr(int irq, struct xgene_pmu_dev *pmu_dev) 795832c927dSTai Nguyen { 796832c927dSTai Nguyen struct xgene_pmu *xgene_pmu = pmu_dev->parent; 797832c927dSTai Nguyen u32 pmovsr; 798832c927dSTai Nguyen int idx; 799832c927dSTai Nguyen 800832c927dSTai Nguyen pmovsr = readl(pmu_dev->inf->csr + PMU_PMOVSR) & PMU_OVERFLOW_MASK; 801832c927dSTai Nguyen if (!pmovsr) 802832c927dSTai Nguyen return; 803832c927dSTai Nguyen 804832c927dSTai Nguyen /* Clear interrupt flag */ 805832c927dSTai Nguyen if (xgene_pmu->version == PCP_PMU_V1) 806832c927dSTai Nguyen writel(0x0, pmu_dev->inf->csr + PMU_PMOVSR); 807832c927dSTai Nguyen else 808832c927dSTai Nguyen writel(pmovsr, pmu_dev->inf->csr + PMU_PMOVSR); 809832c927dSTai Nguyen 810832c927dSTai Nguyen for (idx = 0; idx < PMU_MAX_COUNTERS; idx++) { 811832c927dSTai Nguyen struct perf_event *event = pmu_dev->pmu_counter_event[idx]; 812832c927dSTai Nguyen int overflowed = pmovsr & BIT(idx); 813832c927dSTai Nguyen 814832c927dSTai Nguyen /* Ignore if we don't have an event. */ 815832c927dSTai Nguyen if (!event || !overflowed) 816832c927dSTai Nguyen continue; 817832c927dSTai Nguyen xgene_perf_event_update(event); 818832c927dSTai Nguyen xgene_perf_event_set_period(event); 819832c927dSTai Nguyen } 820832c927dSTai Nguyen } 821832c927dSTai Nguyen 822832c927dSTai Nguyen static irqreturn_t xgene_pmu_isr(int irq, void *dev_id) 823832c927dSTai Nguyen { 824832c927dSTai Nguyen struct xgene_pmu_dev_ctx *ctx; 825832c927dSTai Nguyen struct xgene_pmu *xgene_pmu = dev_id; 826832c927dSTai Nguyen unsigned long flags; 827832c927dSTai Nguyen u32 val; 828832c927dSTai Nguyen 829832c927dSTai Nguyen raw_spin_lock_irqsave(&xgene_pmu->lock, flags); 830832c927dSTai Nguyen 831832c927dSTai Nguyen /* Get Interrupt PMU source */ 832832c927dSTai Nguyen val = readl(xgene_pmu->pcppmu_csr + PCPPMU_INTSTATUS_REG); 833832c927dSTai Nguyen if (val & PCPPMU_INT_MCU) { 834832c927dSTai Nguyen list_for_each_entry(ctx, &xgene_pmu->mcpmus, next) { 835832c927dSTai Nguyen _xgene_pmu_isr(irq, ctx->pmu_dev); 836832c927dSTai Nguyen } 837832c927dSTai Nguyen } 838832c927dSTai Nguyen if (val & PCPPMU_INT_MCB) { 839832c927dSTai Nguyen list_for_each_entry(ctx, &xgene_pmu->mcbpmus, next) { 840832c927dSTai Nguyen _xgene_pmu_isr(irq, ctx->pmu_dev); 841832c927dSTai Nguyen } 842832c927dSTai Nguyen } 843832c927dSTai Nguyen if (val & PCPPMU_INT_L3C) { 844832c927dSTai Nguyen list_for_each_entry(ctx, &xgene_pmu->l3cpmus, next) { 845832c927dSTai Nguyen _xgene_pmu_isr(irq, ctx->pmu_dev); 846832c927dSTai Nguyen } 847832c927dSTai Nguyen } 848832c927dSTai Nguyen if (val & PCPPMU_INT_IOB) { 849832c927dSTai Nguyen list_for_each_entry(ctx, &xgene_pmu->iobpmus, next) { 850832c927dSTai Nguyen _xgene_pmu_isr(irq, ctx->pmu_dev); 851832c927dSTai Nguyen } 852832c927dSTai Nguyen } 853832c927dSTai Nguyen 854832c927dSTai Nguyen raw_spin_unlock_irqrestore(&xgene_pmu->lock, flags); 855832c927dSTai Nguyen 856832c927dSTai Nguyen return IRQ_HANDLED; 857832c927dSTai Nguyen } 858832c927dSTai Nguyen 859832c927dSTai Nguyen static int acpi_pmu_probe_active_mcb_mcu(struct xgene_pmu *xgene_pmu, 860832c927dSTai Nguyen struct platform_device *pdev) 861832c927dSTai Nguyen { 862832c927dSTai Nguyen void __iomem *csw_csr, *mcba_csr, *mcbb_csr; 863832c927dSTai Nguyen struct resource *res; 864832c927dSTai Nguyen unsigned int reg; 865832c927dSTai Nguyen 866832c927dSTai Nguyen res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 867832c927dSTai Nguyen csw_csr = devm_ioremap_resource(&pdev->dev, res); 868832c927dSTai Nguyen if (IS_ERR(csw_csr)) { 869832c927dSTai Nguyen dev_err(&pdev->dev, "ioremap failed for CSW CSR resource\n"); 870832c927dSTai Nguyen return PTR_ERR(csw_csr); 871832c927dSTai Nguyen } 872832c927dSTai Nguyen 873832c927dSTai Nguyen res = platform_get_resource(pdev, IORESOURCE_MEM, 2); 874832c927dSTai Nguyen mcba_csr = devm_ioremap_resource(&pdev->dev, res); 875832c927dSTai Nguyen if (IS_ERR(mcba_csr)) { 876832c927dSTai Nguyen dev_err(&pdev->dev, "ioremap failed for MCBA CSR resource\n"); 877832c927dSTai Nguyen return PTR_ERR(mcba_csr); 878832c927dSTai Nguyen } 879832c927dSTai Nguyen 880832c927dSTai Nguyen res = platform_get_resource(pdev, IORESOURCE_MEM, 3); 881832c927dSTai Nguyen mcbb_csr = devm_ioremap_resource(&pdev->dev, res); 882832c927dSTai Nguyen if (IS_ERR(mcbb_csr)) { 883832c927dSTai Nguyen dev_err(&pdev->dev, "ioremap failed for MCBB CSR resource\n"); 884832c927dSTai Nguyen return PTR_ERR(mcbb_csr); 885832c927dSTai Nguyen } 886832c927dSTai Nguyen 887832c927dSTai Nguyen reg = readl(csw_csr + CSW_CSWCR); 888832c927dSTai Nguyen if (reg & CSW_CSWCR_DUALMCB_MASK) { 889832c927dSTai Nguyen /* Dual MCB active */ 890832c927dSTai Nguyen xgene_pmu->mcb_active_mask = 0x3; 891832c927dSTai Nguyen /* Probe all active MC(s) */ 892832c927dSTai Nguyen reg = readl(mcbb_csr + CSW_CSWCR); 893832c927dSTai Nguyen xgene_pmu->mc_active_mask = 894832c927dSTai Nguyen (reg & MCBADDRMR_DUALMCU_MODE_MASK) ? 0xF : 0x5; 895832c927dSTai Nguyen } else { 896832c927dSTai Nguyen /* Single MCB active */ 897832c927dSTai Nguyen xgene_pmu->mcb_active_mask = 0x1; 898832c927dSTai Nguyen /* Probe all active MC(s) */ 899832c927dSTai Nguyen reg = readl(mcba_csr + CSW_CSWCR); 900832c927dSTai Nguyen xgene_pmu->mc_active_mask = 901832c927dSTai Nguyen (reg & MCBADDRMR_DUALMCU_MODE_MASK) ? 0x3 : 0x1; 902832c927dSTai Nguyen } 903832c927dSTai Nguyen 904832c927dSTai Nguyen return 0; 905832c927dSTai Nguyen } 906832c927dSTai Nguyen 907832c927dSTai Nguyen static int fdt_pmu_probe_active_mcb_mcu(struct xgene_pmu *xgene_pmu, 908832c927dSTai Nguyen struct platform_device *pdev) 909832c927dSTai Nguyen { 910832c927dSTai Nguyen struct regmap *csw_map, *mcba_map, *mcbb_map; 911832c927dSTai Nguyen struct device_node *np = pdev->dev.of_node; 912832c927dSTai Nguyen unsigned int reg; 913832c927dSTai Nguyen 914832c927dSTai Nguyen csw_map = syscon_regmap_lookup_by_phandle(np, "regmap-csw"); 915832c927dSTai Nguyen if (IS_ERR(csw_map)) { 916832c927dSTai Nguyen dev_err(&pdev->dev, "unable to get syscon regmap csw\n"); 917832c927dSTai Nguyen return PTR_ERR(csw_map); 918832c927dSTai Nguyen } 919832c927dSTai Nguyen 920832c927dSTai Nguyen mcba_map = syscon_regmap_lookup_by_phandle(np, "regmap-mcba"); 921832c927dSTai Nguyen if (IS_ERR(mcba_map)) { 922832c927dSTai Nguyen dev_err(&pdev->dev, "unable to get syscon regmap mcba\n"); 923832c927dSTai Nguyen return PTR_ERR(mcba_map); 924832c927dSTai Nguyen } 925832c927dSTai Nguyen 926832c927dSTai Nguyen mcbb_map = syscon_regmap_lookup_by_phandle(np, "regmap-mcbb"); 927832c927dSTai Nguyen if (IS_ERR(mcbb_map)) { 928832c927dSTai Nguyen dev_err(&pdev->dev, "unable to get syscon regmap mcbb\n"); 929832c927dSTai Nguyen return PTR_ERR(mcbb_map); 930832c927dSTai Nguyen } 931832c927dSTai Nguyen 932832c927dSTai Nguyen if (regmap_read(csw_map, CSW_CSWCR, ®)) 933832c927dSTai Nguyen return -EINVAL; 934832c927dSTai Nguyen 935832c927dSTai Nguyen if (reg & CSW_CSWCR_DUALMCB_MASK) { 936832c927dSTai Nguyen /* Dual MCB active */ 937832c927dSTai Nguyen xgene_pmu->mcb_active_mask = 0x3; 938832c927dSTai Nguyen /* Probe all active MC(s) */ 939832c927dSTai Nguyen if (regmap_read(mcbb_map, MCBADDRMR, ®)) 940832c927dSTai Nguyen return 0; 941832c927dSTai Nguyen xgene_pmu->mc_active_mask = 942832c927dSTai Nguyen (reg & MCBADDRMR_DUALMCU_MODE_MASK) ? 0xF : 0x5; 943832c927dSTai Nguyen } else { 944832c927dSTai Nguyen /* Single MCB active */ 945832c927dSTai Nguyen xgene_pmu->mcb_active_mask = 0x1; 946832c927dSTai Nguyen /* Probe all active MC(s) */ 947832c927dSTai Nguyen if (regmap_read(mcba_map, MCBADDRMR, ®)) 948832c927dSTai Nguyen return 0; 949832c927dSTai Nguyen xgene_pmu->mc_active_mask = 950832c927dSTai Nguyen (reg & MCBADDRMR_DUALMCU_MODE_MASK) ? 0x3 : 0x1; 951832c927dSTai Nguyen } 952832c927dSTai Nguyen 953832c927dSTai Nguyen return 0; 954832c927dSTai Nguyen } 955832c927dSTai Nguyen 956832c927dSTai Nguyen static int xgene_pmu_probe_active_mcb_mcu(struct xgene_pmu *xgene_pmu, 957832c927dSTai Nguyen struct platform_device *pdev) 958832c927dSTai Nguyen { 959832c927dSTai Nguyen if (has_acpi_companion(&pdev->dev)) 960832c927dSTai Nguyen return acpi_pmu_probe_active_mcb_mcu(xgene_pmu, pdev); 961832c927dSTai Nguyen return fdt_pmu_probe_active_mcb_mcu(xgene_pmu, pdev); 962832c927dSTai Nguyen } 963832c927dSTai Nguyen 964832c927dSTai Nguyen static char *xgene_pmu_dev_name(struct device *dev, u32 type, int id) 965832c927dSTai Nguyen { 966832c927dSTai Nguyen switch (type) { 967832c927dSTai Nguyen case PMU_TYPE_L3C: 968832c927dSTai Nguyen return devm_kasprintf(dev, GFP_KERNEL, "l3c%d", id); 969832c927dSTai Nguyen case PMU_TYPE_IOB: 970832c927dSTai Nguyen return devm_kasprintf(dev, GFP_KERNEL, "iob%d", id); 971832c927dSTai Nguyen case PMU_TYPE_MCB: 972832c927dSTai Nguyen return devm_kasprintf(dev, GFP_KERNEL, "mcb%d", id); 973832c927dSTai Nguyen case PMU_TYPE_MC: 974832c927dSTai Nguyen return devm_kasprintf(dev, GFP_KERNEL, "mc%d", id); 975832c927dSTai Nguyen default: 976832c927dSTai Nguyen return devm_kasprintf(dev, GFP_KERNEL, "unknown"); 977832c927dSTai Nguyen } 978832c927dSTai Nguyen } 979832c927dSTai Nguyen 980832c927dSTai Nguyen #if defined(CONFIG_ACPI) 981832c927dSTai Nguyen static int acpi_pmu_dev_add_resource(struct acpi_resource *ares, void *data) 982832c927dSTai Nguyen { 983832c927dSTai Nguyen struct resource *res = data; 984832c927dSTai Nguyen 985832c927dSTai Nguyen if (ares->type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) 986832c927dSTai Nguyen acpi_dev_resource_memory(ares, res); 987832c927dSTai Nguyen 988832c927dSTai Nguyen /* Always tell the ACPI core to skip this resource */ 989832c927dSTai Nguyen return 1; 990832c927dSTai Nguyen } 991832c927dSTai Nguyen 992832c927dSTai Nguyen static struct 993832c927dSTai Nguyen xgene_pmu_dev_ctx *acpi_get_pmu_hw_inf(struct xgene_pmu *xgene_pmu, 994832c927dSTai Nguyen struct acpi_device *adev, u32 type) 995832c927dSTai Nguyen { 996832c927dSTai Nguyen struct device *dev = xgene_pmu->dev; 997832c927dSTai Nguyen struct list_head resource_list; 998832c927dSTai Nguyen struct xgene_pmu_dev_ctx *ctx; 999832c927dSTai Nguyen const union acpi_object *obj; 1000832c927dSTai Nguyen struct hw_pmu_info *inf; 1001832c927dSTai Nguyen void __iomem *dev_csr; 1002832c927dSTai Nguyen struct resource res; 1003832c927dSTai Nguyen int enable_bit; 1004832c927dSTai Nguyen int rc; 1005832c927dSTai Nguyen 1006832c927dSTai Nguyen ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 1007832c927dSTai Nguyen if (!ctx) 1008832c927dSTai Nguyen return NULL; 1009832c927dSTai Nguyen 1010832c927dSTai Nguyen INIT_LIST_HEAD(&resource_list); 1011832c927dSTai Nguyen rc = acpi_dev_get_resources(adev, &resource_list, 1012832c927dSTai Nguyen acpi_pmu_dev_add_resource, &res); 1013832c927dSTai Nguyen acpi_dev_free_resource_list(&resource_list); 10149a1a1f40STai Nguyen if (rc < 0) { 1015832c927dSTai Nguyen dev_err(dev, "PMU type %d: No resource address found\n", type); 1016832c927dSTai Nguyen goto err; 1017832c927dSTai Nguyen } 1018832c927dSTai Nguyen 1019832c927dSTai Nguyen dev_csr = devm_ioremap_resource(dev, &res); 1020832c927dSTai Nguyen if (IS_ERR(dev_csr)) { 1021832c927dSTai Nguyen dev_err(dev, "PMU type %d: Fail to map resource\n", type); 1022832c927dSTai Nguyen goto err; 1023832c927dSTai Nguyen } 1024832c927dSTai Nguyen 1025832c927dSTai Nguyen /* A PMU device node without enable-bit-index is always enabled */ 1026832c927dSTai Nguyen rc = acpi_dev_get_property(adev, "enable-bit-index", 1027832c927dSTai Nguyen ACPI_TYPE_INTEGER, &obj); 1028832c927dSTai Nguyen if (rc < 0) 1029832c927dSTai Nguyen enable_bit = 0; 1030832c927dSTai Nguyen else 1031832c927dSTai Nguyen enable_bit = (int) obj->integer.value; 1032832c927dSTai Nguyen 1033832c927dSTai Nguyen ctx->name = xgene_pmu_dev_name(dev, type, enable_bit); 1034832c927dSTai Nguyen if (!ctx->name) { 1035832c927dSTai Nguyen dev_err(dev, "PMU type %d: Fail to get device name\n", type); 1036832c927dSTai Nguyen goto err; 1037832c927dSTai Nguyen } 1038832c927dSTai Nguyen inf = &ctx->inf; 1039832c927dSTai Nguyen inf->type = type; 1040832c927dSTai Nguyen inf->csr = dev_csr; 1041832c927dSTai Nguyen inf->enable_mask = 1 << enable_bit; 1042832c927dSTai Nguyen 1043832c927dSTai Nguyen return ctx; 1044832c927dSTai Nguyen err: 1045832c927dSTai Nguyen devm_kfree(dev, ctx); 1046832c927dSTai Nguyen return NULL; 1047832c927dSTai Nguyen } 1048832c927dSTai Nguyen 1049832c927dSTai Nguyen static acpi_status acpi_pmu_dev_add(acpi_handle handle, u32 level, 1050832c927dSTai Nguyen void *data, void **return_value) 1051832c927dSTai Nguyen { 1052832c927dSTai Nguyen struct xgene_pmu *xgene_pmu = data; 1053832c927dSTai Nguyen struct xgene_pmu_dev_ctx *ctx; 1054832c927dSTai Nguyen struct acpi_device *adev; 1055832c927dSTai Nguyen 1056832c927dSTai Nguyen if (acpi_bus_get_device(handle, &adev)) 1057832c927dSTai Nguyen return AE_OK; 1058832c927dSTai Nguyen if (acpi_bus_get_status(adev) || !adev->status.present) 1059832c927dSTai Nguyen return AE_OK; 1060832c927dSTai Nguyen 1061832c927dSTai Nguyen if (!strcmp(acpi_device_hid(adev), "APMC0D5D")) 1062832c927dSTai Nguyen ctx = acpi_get_pmu_hw_inf(xgene_pmu, adev, PMU_TYPE_L3C); 1063832c927dSTai Nguyen else if (!strcmp(acpi_device_hid(adev), "APMC0D5E")) 1064832c927dSTai Nguyen ctx = acpi_get_pmu_hw_inf(xgene_pmu, adev, PMU_TYPE_IOB); 1065832c927dSTai Nguyen else if (!strcmp(acpi_device_hid(adev), "APMC0D5F")) 1066832c927dSTai Nguyen ctx = acpi_get_pmu_hw_inf(xgene_pmu, adev, PMU_TYPE_MCB); 1067832c927dSTai Nguyen else if (!strcmp(acpi_device_hid(adev), "APMC0D60")) 1068832c927dSTai Nguyen ctx = acpi_get_pmu_hw_inf(xgene_pmu, adev, PMU_TYPE_MC); 1069832c927dSTai Nguyen else 1070832c927dSTai Nguyen ctx = NULL; 1071832c927dSTai Nguyen 1072832c927dSTai Nguyen if (!ctx) 1073832c927dSTai Nguyen return AE_OK; 1074832c927dSTai Nguyen 1075832c927dSTai Nguyen if (xgene_pmu_dev_add(xgene_pmu, ctx)) { 1076832c927dSTai Nguyen /* Can't add the PMU device, skip it */ 1077832c927dSTai Nguyen devm_kfree(xgene_pmu->dev, ctx); 1078832c927dSTai Nguyen return AE_OK; 1079832c927dSTai Nguyen } 1080832c927dSTai Nguyen 1081832c927dSTai Nguyen switch (ctx->inf.type) { 1082832c927dSTai Nguyen case PMU_TYPE_L3C: 1083832c927dSTai Nguyen list_add(&ctx->next, &xgene_pmu->l3cpmus); 1084832c927dSTai Nguyen break; 1085832c927dSTai Nguyen case PMU_TYPE_IOB: 1086832c927dSTai Nguyen list_add(&ctx->next, &xgene_pmu->iobpmus); 1087832c927dSTai Nguyen break; 1088832c927dSTai Nguyen case PMU_TYPE_MCB: 1089832c927dSTai Nguyen list_add(&ctx->next, &xgene_pmu->mcbpmus); 1090832c927dSTai Nguyen break; 1091832c927dSTai Nguyen case PMU_TYPE_MC: 1092832c927dSTai Nguyen list_add(&ctx->next, &xgene_pmu->mcpmus); 1093832c927dSTai Nguyen break; 1094832c927dSTai Nguyen } 1095832c927dSTai Nguyen return AE_OK; 1096832c927dSTai Nguyen } 1097832c927dSTai Nguyen 1098832c927dSTai Nguyen static int acpi_pmu_probe_pmu_dev(struct xgene_pmu *xgene_pmu, 1099832c927dSTai Nguyen struct platform_device *pdev) 1100832c927dSTai Nguyen { 1101832c927dSTai Nguyen struct device *dev = xgene_pmu->dev; 1102832c927dSTai Nguyen acpi_handle handle; 1103832c927dSTai Nguyen acpi_status status; 1104832c927dSTai Nguyen 1105832c927dSTai Nguyen handle = ACPI_HANDLE(dev); 1106832c927dSTai Nguyen if (!handle) 1107832c927dSTai Nguyen return -EINVAL; 1108832c927dSTai Nguyen 1109832c927dSTai Nguyen status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, 1110832c927dSTai Nguyen acpi_pmu_dev_add, NULL, xgene_pmu, NULL); 1111832c927dSTai Nguyen if (ACPI_FAILURE(status)) { 1112832c927dSTai Nguyen dev_err(dev, "failed to probe PMU devices\n"); 1113832c927dSTai Nguyen return -ENODEV; 1114832c927dSTai Nguyen } 1115832c927dSTai Nguyen 1116832c927dSTai Nguyen return 0; 1117832c927dSTai Nguyen } 1118832c927dSTai Nguyen #else 1119832c927dSTai Nguyen static int acpi_pmu_probe_pmu_dev(struct xgene_pmu *xgene_pmu, 1120832c927dSTai Nguyen struct platform_device *pdev) 1121832c927dSTai Nguyen { 1122832c927dSTai Nguyen return 0; 1123832c927dSTai Nguyen } 1124832c927dSTai Nguyen #endif 1125832c927dSTai Nguyen 1126832c927dSTai Nguyen static struct 1127832c927dSTai Nguyen xgene_pmu_dev_ctx *fdt_get_pmu_hw_inf(struct xgene_pmu *xgene_pmu, 1128832c927dSTai Nguyen struct device_node *np, u32 type) 1129832c927dSTai Nguyen { 1130832c927dSTai Nguyen struct device *dev = xgene_pmu->dev; 1131832c927dSTai Nguyen struct xgene_pmu_dev_ctx *ctx; 1132832c927dSTai Nguyen struct hw_pmu_info *inf; 1133832c927dSTai Nguyen void __iomem *dev_csr; 1134832c927dSTai Nguyen struct resource res; 1135832c927dSTai Nguyen int enable_bit; 1136832c927dSTai Nguyen int rc; 1137832c927dSTai Nguyen 1138832c927dSTai Nguyen ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 1139832c927dSTai Nguyen if (!ctx) 1140832c927dSTai Nguyen return NULL; 1141832c927dSTai Nguyen rc = of_address_to_resource(np, 0, &res); 1142832c927dSTai Nguyen if (rc < 0) { 1143832c927dSTai Nguyen dev_err(dev, "PMU type %d: No resource address found\n", type); 1144832c927dSTai Nguyen goto err; 1145832c927dSTai Nguyen } 1146832c927dSTai Nguyen dev_csr = devm_ioremap_resource(dev, &res); 1147832c927dSTai Nguyen if (IS_ERR(dev_csr)) { 1148832c927dSTai Nguyen dev_err(dev, "PMU type %d: Fail to map resource\n", type); 1149832c927dSTai Nguyen goto err; 1150832c927dSTai Nguyen } 1151832c927dSTai Nguyen 1152832c927dSTai Nguyen /* A PMU device node without enable-bit-index is always enabled */ 1153832c927dSTai Nguyen if (of_property_read_u32(np, "enable-bit-index", &enable_bit)) 1154832c927dSTai Nguyen enable_bit = 0; 1155832c927dSTai Nguyen 1156832c927dSTai Nguyen ctx->name = xgene_pmu_dev_name(dev, type, enable_bit); 1157832c927dSTai Nguyen if (!ctx->name) { 1158832c927dSTai Nguyen dev_err(dev, "PMU type %d: Fail to get device name\n", type); 1159832c927dSTai Nguyen goto err; 1160832c927dSTai Nguyen } 1161832c927dSTai Nguyen inf = &ctx->inf; 1162832c927dSTai Nguyen inf->type = type; 1163832c927dSTai Nguyen inf->csr = dev_csr; 1164832c927dSTai Nguyen inf->enable_mask = 1 << enable_bit; 1165832c927dSTai Nguyen 1166832c927dSTai Nguyen return ctx; 1167832c927dSTai Nguyen err: 1168832c927dSTai Nguyen devm_kfree(dev, ctx); 1169832c927dSTai Nguyen return NULL; 1170832c927dSTai Nguyen } 1171832c927dSTai Nguyen 1172832c927dSTai Nguyen static int fdt_pmu_probe_pmu_dev(struct xgene_pmu *xgene_pmu, 1173832c927dSTai Nguyen struct platform_device *pdev) 1174832c927dSTai Nguyen { 1175832c927dSTai Nguyen struct xgene_pmu_dev_ctx *ctx; 1176832c927dSTai Nguyen struct device_node *np; 1177832c927dSTai Nguyen 1178832c927dSTai Nguyen for_each_child_of_node(pdev->dev.of_node, np) { 1179832c927dSTai Nguyen if (!of_device_is_available(np)) 1180832c927dSTai Nguyen continue; 1181832c927dSTai Nguyen 1182832c927dSTai Nguyen if (of_device_is_compatible(np, "apm,xgene-pmu-l3c")) 1183832c927dSTai Nguyen ctx = fdt_get_pmu_hw_inf(xgene_pmu, np, PMU_TYPE_L3C); 1184832c927dSTai Nguyen else if (of_device_is_compatible(np, "apm,xgene-pmu-iob")) 1185832c927dSTai Nguyen ctx = fdt_get_pmu_hw_inf(xgene_pmu, np, PMU_TYPE_IOB); 1186832c927dSTai Nguyen else if (of_device_is_compatible(np, "apm,xgene-pmu-mcb")) 1187832c927dSTai Nguyen ctx = fdt_get_pmu_hw_inf(xgene_pmu, np, PMU_TYPE_MCB); 1188832c927dSTai Nguyen else if (of_device_is_compatible(np, "apm,xgene-pmu-mc")) 1189832c927dSTai Nguyen ctx = fdt_get_pmu_hw_inf(xgene_pmu, np, PMU_TYPE_MC); 1190832c927dSTai Nguyen else 1191832c927dSTai Nguyen ctx = NULL; 1192832c927dSTai Nguyen 1193832c927dSTai Nguyen if (!ctx) 1194832c927dSTai Nguyen continue; 1195832c927dSTai Nguyen 1196832c927dSTai Nguyen if (xgene_pmu_dev_add(xgene_pmu, ctx)) { 1197832c927dSTai Nguyen /* Can't add the PMU device, skip it */ 1198832c927dSTai Nguyen devm_kfree(xgene_pmu->dev, ctx); 1199832c927dSTai Nguyen continue; 1200832c927dSTai Nguyen } 1201832c927dSTai Nguyen 1202832c927dSTai Nguyen switch (ctx->inf.type) { 1203832c927dSTai Nguyen case PMU_TYPE_L3C: 1204832c927dSTai Nguyen list_add(&ctx->next, &xgene_pmu->l3cpmus); 1205832c927dSTai Nguyen break; 1206832c927dSTai Nguyen case PMU_TYPE_IOB: 1207832c927dSTai Nguyen list_add(&ctx->next, &xgene_pmu->iobpmus); 1208832c927dSTai Nguyen break; 1209832c927dSTai Nguyen case PMU_TYPE_MCB: 1210832c927dSTai Nguyen list_add(&ctx->next, &xgene_pmu->mcbpmus); 1211832c927dSTai Nguyen break; 1212832c927dSTai Nguyen case PMU_TYPE_MC: 1213832c927dSTai Nguyen list_add(&ctx->next, &xgene_pmu->mcpmus); 1214832c927dSTai Nguyen break; 1215832c927dSTai Nguyen } 1216832c927dSTai Nguyen } 1217832c927dSTai Nguyen 1218832c927dSTai Nguyen return 0; 1219832c927dSTai Nguyen } 1220832c927dSTai Nguyen 1221832c927dSTai Nguyen static int xgene_pmu_probe_pmu_dev(struct xgene_pmu *xgene_pmu, 1222832c927dSTai Nguyen struct platform_device *pdev) 1223832c927dSTai Nguyen { 1224832c927dSTai Nguyen if (has_acpi_companion(&pdev->dev)) 1225832c927dSTai Nguyen return acpi_pmu_probe_pmu_dev(xgene_pmu, pdev); 1226832c927dSTai Nguyen return fdt_pmu_probe_pmu_dev(xgene_pmu, pdev); 1227832c927dSTai Nguyen } 1228832c927dSTai Nguyen 1229832c927dSTai Nguyen static const struct xgene_pmu_data xgene_pmu_data = { 1230832c927dSTai Nguyen .id = PCP_PMU_V1, 1231832c927dSTai Nguyen }; 1232832c927dSTai Nguyen 1233832c927dSTai Nguyen static const struct xgene_pmu_data xgene_pmu_v2_data = { 1234832c927dSTai Nguyen .id = PCP_PMU_V2, 1235832c927dSTai Nguyen }; 1236832c927dSTai Nguyen 1237832c927dSTai Nguyen static const struct of_device_id xgene_pmu_of_match[] = { 1238832c927dSTai Nguyen { .compatible = "apm,xgene-pmu", .data = &xgene_pmu_data }, 1239832c927dSTai Nguyen { .compatible = "apm,xgene-pmu-v2", .data = &xgene_pmu_v2_data }, 1240832c927dSTai Nguyen {}, 1241832c927dSTai Nguyen }; 1242832c927dSTai Nguyen MODULE_DEVICE_TABLE(of, xgene_pmu_of_match); 1243832c927dSTai Nguyen #ifdef CONFIG_ACPI 1244832c927dSTai Nguyen static const struct acpi_device_id xgene_pmu_acpi_match[] = { 1245832c927dSTai Nguyen {"APMC0D5B", PCP_PMU_V1}, 1246832c927dSTai Nguyen {"APMC0D5C", PCP_PMU_V2}, 1247832c927dSTai Nguyen {}, 1248832c927dSTai Nguyen }; 1249832c927dSTai Nguyen MODULE_DEVICE_TABLE(acpi, xgene_pmu_acpi_match); 1250832c927dSTai Nguyen #endif 1251832c927dSTai Nguyen 1252832c927dSTai Nguyen static int xgene_pmu_probe(struct platform_device *pdev) 1253832c927dSTai Nguyen { 1254832c927dSTai Nguyen const struct xgene_pmu_data *dev_data; 1255832c927dSTai Nguyen const struct of_device_id *of_id; 1256832c927dSTai Nguyen struct xgene_pmu *xgene_pmu; 1257832c927dSTai Nguyen struct resource *res; 1258832c927dSTai Nguyen int irq, rc; 1259832c927dSTai Nguyen int version; 1260832c927dSTai Nguyen 1261832c927dSTai Nguyen xgene_pmu = devm_kzalloc(&pdev->dev, sizeof(*xgene_pmu), GFP_KERNEL); 1262832c927dSTai Nguyen if (!xgene_pmu) 1263832c927dSTai Nguyen return -ENOMEM; 1264832c927dSTai Nguyen xgene_pmu->dev = &pdev->dev; 1265832c927dSTai Nguyen platform_set_drvdata(pdev, xgene_pmu); 1266832c927dSTai Nguyen 1267832c927dSTai Nguyen version = -EINVAL; 1268832c927dSTai Nguyen of_id = of_match_device(xgene_pmu_of_match, &pdev->dev); 1269832c927dSTai Nguyen if (of_id) { 1270832c927dSTai Nguyen dev_data = (const struct xgene_pmu_data *) of_id->data; 1271832c927dSTai Nguyen version = dev_data->id; 1272832c927dSTai Nguyen } 1273832c927dSTai Nguyen 1274832c927dSTai Nguyen #ifdef CONFIG_ACPI 1275832c927dSTai Nguyen if (ACPI_COMPANION(&pdev->dev)) { 1276832c927dSTai Nguyen const struct acpi_device_id *acpi_id; 1277832c927dSTai Nguyen 1278832c927dSTai Nguyen acpi_id = acpi_match_device(xgene_pmu_acpi_match, &pdev->dev); 1279832c927dSTai Nguyen if (acpi_id) 1280832c927dSTai Nguyen version = (int) acpi_id->driver_data; 1281832c927dSTai Nguyen } 1282832c927dSTai Nguyen #endif 1283832c927dSTai Nguyen if (version < 0) 1284832c927dSTai Nguyen return -ENODEV; 1285832c927dSTai Nguyen 1286832c927dSTai Nguyen INIT_LIST_HEAD(&xgene_pmu->l3cpmus); 1287832c927dSTai Nguyen INIT_LIST_HEAD(&xgene_pmu->iobpmus); 1288832c927dSTai Nguyen INIT_LIST_HEAD(&xgene_pmu->mcbpmus); 1289832c927dSTai Nguyen INIT_LIST_HEAD(&xgene_pmu->mcpmus); 1290832c927dSTai Nguyen 1291832c927dSTai Nguyen xgene_pmu->version = version; 1292832c927dSTai Nguyen dev_info(&pdev->dev, "X-Gene PMU version %d\n", xgene_pmu->version); 1293832c927dSTai Nguyen 1294832c927dSTai Nguyen res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1295832c927dSTai Nguyen xgene_pmu->pcppmu_csr = devm_ioremap_resource(&pdev->dev, res); 1296832c927dSTai Nguyen if (IS_ERR(xgene_pmu->pcppmu_csr)) { 1297832c927dSTai Nguyen dev_err(&pdev->dev, "ioremap failed for PCP PMU resource\n"); 1298832c927dSTai Nguyen rc = PTR_ERR(xgene_pmu->pcppmu_csr); 1299832c927dSTai Nguyen goto err; 1300832c927dSTai Nguyen } 1301832c927dSTai Nguyen 1302832c927dSTai Nguyen irq = platform_get_irq(pdev, 0); 1303832c927dSTai Nguyen if (irq < 0) { 1304832c927dSTai Nguyen dev_err(&pdev->dev, "No IRQ resource\n"); 1305832c927dSTai Nguyen rc = -EINVAL; 1306832c927dSTai Nguyen goto err; 1307832c927dSTai Nguyen } 1308832c927dSTai Nguyen rc = devm_request_irq(&pdev->dev, irq, xgene_pmu_isr, 1309832c927dSTai Nguyen IRQF_NOBALANCING | IRQF_NO_THREAD, 1310832c927dSTai Nguyen dev_name(&pdev->dev), xgene_pmu); 1311832c927dSTai Nguyen if (rc) { 1312832c927dSTai Nguyen dev_err(&pdev->dev, "Could not request IRQ %d\n", irq); 1313832c927dSTai Nguyen goto err; 1314832c927dSTai Nguyen } 1315832c927dSTai Nguyen 1316832c927dSTai Nguyen raw_spin_lock_init(&xgene_pmu->lock); 1317832c927dSTai Nguyen 1318832c927dSTai Nguyen /* Check for active MCBs and MCUs */ 1319832c927dSTai Nguyen rc = xgene_pmu_probe_active_mcb_mcu(xgene_pmu, pdev); 1320832c927dSTai Nguyen if (rc) { 1321832c927dSTai Nguyen dev_warn(&pdev->dev, "Unknown MCB/MCU active status\n"); 1322832c927dSTai Nguyen xgene_pmu->mcb_active_mask = 0x1; 1323832c927dSTai Nguyen xgene_pmu->mc_active_mask = 0x1; 1324832c927dSTai Nguyen } 1325832c927dSTai Nguyen 1326832c927dSTai Nguyen /* Pick one core to use for cpumask attributes */ 1327832c927dSTai Nguyen cpumask_set_cpu(smp_processor_id(), &xgene_pmu->cpu); 1328832c927dSTai Nguyen 1329832c927dSTai Nguyen /* Make sure that the overflow interrupt is handled by this CPU */ 1330832c927dSTai Nguyen rc = irq_set_affinity(irq, &xgene_pmu->cpu); 1331832c927dSTai Nguyen if (rc) { 1332832c927dSTai Nguyen dev_err(&pdev->dev, "Failed to set interrupt affinity!\n"); 1333832c927dSTai Nguyen goto err; 1334832c927dSTai Nguyen } 1335832c927dSTai Nguyen 1336832c927dSTai Nguyen /* Walk through the tree for all PMU perf devices */ 1337832c927dSTai Nguyen rc = xgene_pmu_probe_pmu_dev(xgene_pmu, pdev); 1338832c927dSTai Nguyen if (rc) { 1339832c927dSTai Nguyen dev_err(&pdev->dev, "No PMU perf devices found!\n"); 1340832c927dSTai Nguyen goto err; 1341832c927dSTai Nguyen } 1342832c927dSTai Nguyen 1343832c927dSTai Nguyen /* Enable interrupt */ 1344832c927dSTai Nguyen xgene_pmu_unmask_int(xgene_pmu); 1345832c927dSTai Nguyen 1346832c927dSTai Nguyen return 0; 1347832c927dSTai Nguyen 1348832c927dSTai Nguyen err: 1349832c927dSTai Nguyen if (xgene_pmu->pcppmu_csr) 1350832c927dSTai Nguyen devm_iounmap(&pdev->dev, xgene_pmu->pcppmu_csr); 1351832c927dSTai Nguyen devm_kfree(&pdev->dev, xgene_pmu); 1352832c927dSTai Nguyen 1353832c927dSTai Nguyen return rc; 1354832c927dSTai Nguyen } 1355832c927dSTai Nguyen 1356832c927dSTai Nguyen static void 1357832c927dSTai Nguyen xgene_pmu_dev_cleanup(struct xgene_pmu *xgene_pmu, struct list_head *pmus) 1358832c927dSTai Nguyen { 1359832c927dSTai Nguyen struct xgene_pmu_dev_ctx *ctx; 1360832c927dSTai Nguyen struct device *dev = xgene_pmu->dev; 1361832c927dSTai Nguyen struct xgene_pmu_dev *pmu_dev; 1362832c927dSTai Nguyen 1363832c927dSTai Nguyen list_for_each_entry(ctx, pmus, next) { 1364832c927dSTai Nguyen pmu_dev = ctx->pmu_dev; 1365832c927dSTai Nguyen if (pmu_dev->inf->csr) 1366832c927dSTai Nguyen devm_iounmap(dev, pmu_dev->inf->csr); 1367832c927dSTai Nguyen devm_kfree(dev, ctx); 1368832c927dSTai Nguyen devm_kfree(dev, pmu_dev); 1369832c927dSTai Nguyen } 1370832c927dSTai Nguyen } 1371832c927dSTai Nguyen 1372832c927dSTai Nguyen static int xgene_pmu_remove(struct platform_device *pdev) 1373832c927dSTai Nguyen { 1374832c927dSTai Nguyen struct xgene_pmu *xgene_pmu = dev_get_drvdata(&pdev->dev); 1375832c927dSTai Nguyen 1376832c927dSTai Nguyen xgene_pmu_dev_cleanup(xgene_pmu, &xgene_pmu->l3cpmus); 1377832c927dSTai Nguyen xgene_pmu_dev_cleanup(xgene_pmu, &xgene_pmu->iobpmus); 1378832c927dSTai Nguyen xgene_pmu_dev_cleanup(xgene_pmu, &xgene_pmu->mcbpmus); 1379832c927dSTai Nguyen xgene_pmu_dev_cleanup(xgene_pmu, &xgene_pmu->mcpmus); 1380832c927dSTai Nguyen 1381832c927dSTai Nguyen if (xgene_pmu->pcppmu_csr) 1382832c927dSTai Nguyen devm_iounmap(&pdev->dev, xgene_pmu->pcppmu_csr); 1383832c927dSTai Nguyen devm_kfree(&pdev->dev, xgene_pmu); 1384832c927dSTai Nguyen 1385832c927dSTai Nguyen return 0; 1386832c927dSTai Nguyen } 1387832c927dSTai Nguyen 1388832c927dSTai Nguyen static struct platform_driver xgene_pmu_driver = { 1389832c927dSTai Nguyen .probe = xgene_pmu_probe, 1390832c927dSTai Nguyen .remove = xgene_pmu_remove, 1391832c927dSTai Nguyen .driver = { 1392832c927dSTai Nguyen .name = "xgene-pmu", 1393832c927dSTai Nguyen .of_match_table = xgene_pmu_of_match, 1394832c927dSTai Nguyen .acpi_match_table = ACPI_PTR(xgene_pmu_acpi_match), 1395832c927dSTai Nguyen }, 1396832c927dSTai Nguyen }; 1397832c927dSTai Nguyen 1398832c927dSTai Nguyen builtin_platform_driver(xgene_pmu_driver); 1399