hisi_uncore_l3c_pmu.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. /*
  2. * HiSilicon SoC L3C uncore Hardware event counters support
  3. *
  4. * Copyright (C) 2017 Hisilicon Limited
  5. * Author: Anurup M <anurup.m@huawei.com>
  6. * Shaokun Zhang <zhangshaokun@hisilicon.com>
  7. *
  8. * This code is based on the uncore PMUs like arm-cci and arm-ccn.
  9. *
  10. * This program is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License version 2 as
  12. * published by the Free Software Foundation.
  13. */
  14. #include <linux/acpi.h>
  15. #include <linux/bug.h>
  16. #include <linux/cpuhotplug.h>
  17. #include <linux/interrupt.h>
  18. #include <linux/irq.h>
  19. #include <linux/list.h>
  20. #include <linux/platform_device.h>
  21. #include <linux/smp.h>
  22. #include "hisi_uncore_pmu.h"
  23. /* L3C register definition */
  24. #define L3C_PERF_CTRL 0x0408
  25. #define L3C_INT_MASK 0x0800
  26. #define L3C_INT_STATUS 0x0808
  27. #define L3C_INT_CLEAR 0x080c
  28. #define L3C_EVENT_CTRL 0x1c00
  29. #define L3C_EVENT_TYPE0 0x1d00
  30. /*
  31. * Each counter is 48-bits and [48:63] are reserved
  32. * which are Read-As-Zero and Writes-Ignored.
  33. */
  34. #define L3C_CNTR0_LOWER 0x1e00
  35. /* L3C has 8-counters */
  36. #define L3C_NR_COUNTERS 0x8
  37. #define L3C_PERF_CTRL_EN 0x20000
  38. #define L3C_EVTYPE_NONE 0xff
  39. /*
  40. * Select the counter register offset using the counter index
  41. */
  42. static u32 hisi_l3c_pmu_get_counter_offset(int cntr_idx)
  43. {
  44. return (L3C_CNTR0_LOWER + (cntr_idx * 8));
  45. }
  46. static u64 hisi_l3c_pmu_read_counter(struct hisi_pmu *l3c_pmu,
  47. struct hw_perf_event *hwc)
  48. {
  49. u32 idx = hwc->idx;
  50. if (!hisi_uncore_pmu_counter_valid(l3c_pmu, idx)) {
  51. dev_err(l3c_pmu->dev, "Unsupported event index:%d!\n", idx);
  52. return 0;
  53. }
  54. /* Read 64-bits and the upper 16 bits are RAZ */
  55. return readq(l3c_pmu->base + hisi_l3c_pmu_get_counter_offset(idx));
  56. }
  57. static void hisi_l3c_pmu_write_counter(struct hisi_pmu *l3c_pmu,
  58. struct hw_perf_event *hwc, u64 val)
  59. {
  60. u32 idx = hwc->idx;
  61. if (!hisi_uncore_pmu_counter_valid(l3c_pmu, idx)) {
  62. dev_err(l3c_pmu->dev, "Unsupported event index:%d!\n", idx);
  63. return;
  64. }
  65. /* Write 64-bits and the upper 16 bits are WI */
  66. writeq(val, l3c_pmu->base + hisi_l3c_pmu_get_counter_offset(idx));
  67. }
  68. static void hisi_l3c_pmu_write_evtype(struct hisi_pmu *l3c_pmu, int idx,
  69. u32 type)
  70. {
  71. u32 reg, reg_idx, shift, val;
  72. /*
  73. * Select the appropriate event select register(L3C_EVENT_TYPE0/1).
  74. * There are 2 event select registers for the 8 hardware counters.
  75. * Event code is 8-bits and for the former 4 hardware counters,
  76. * L3C_EVENT_TYPE0 is chosen. For the latter 4 hardware counters,
  77. * L3C_EVENT_TYPE1 is chosen.
  78. */
  79. reg = L3C_EVENT_TYPE0 + (idx / 4) * 4;
  80. reg_idx = idx % 4;
  81. shift = 8 * reg_idx;
  82. /* Write event code to L3C_EVENT_TYPEx Register */
  83. val = readl(l3c_pmu->base + reg);
  84. val &= ~(L3C_EVTYPE_NONE << shift);
  85. val |= (type << shift);
  86. writel(val, l3c_pmu->base + reg);
  87. }
  88. static void hisi_l3c_pmu_start_counters(struct hisi_pmu *l3c_pmu)
  89. {
  90. u32 val;
  91. /*
  92. * Set perf_enable bit in L3C_PERF_CTRL register to start counting
  93. * for all enabled counters.
  94. */
  95. val = readl(l3c_pmu->base + L3C_PERF_CTRL);
  96. val |= L3C_PERF_CTRL_EN;
  97. writel(val, l3c_pmu->base + L3C_PERF_CTRL);
  98. }
  99. static void hisi_l3c_pmu_stop_counters(struct hisi_pmu *l3c_pmu)
  100. {
  101. u32 val;
  102. /*
  103. * Clear perf_enable bit in L3C_PERF_CTRL register to stop counting
  104. * for all enabled counters.
  105. */
  106. val = readl(l3c_pmu->base + L3C_PERF_CTRL);
  107. val &= ~(L3C_PERF_CTRL_EN);
  108. writel(val, l3c_pmu->base + L3C_PERF_CTRL);
  109. }
  110. static void hisi_l3c_pmu_enable_counter(struct hisi_pmu *l3c_pmu,
  111. struct hw_perf_event *hwc)
  112. {
  113. u32 val;
  114. /* Enable counter index in L3C_EVENT_CTRL register */
  115. val = readl(l3c_pmu->base + L3C_EVENT_CTRL);
  116. val |= (1 << hwc->idx);
  117. writel(val, l3c_pmu->base + L3C_EVENT_CTRL);
  118. }
  119. static void hisi_l3c_pmu_disable_counter(struct hisi_pmu *l3c_pmu,
  120. struct hw_perf_event *hwc)
  121. {
  122. u32 val;
  123. /* Clear counter index in L3C_EVENT_CTRL register */
  124. val = readl(l3c_pmu->base + L3C_EVENT_CTRL);
  125. val &= ~(1 << hwc->idx);
  126. writel(val, l3c_pmu->base + L3C_EVENT_CTRL);
  127. }
  128. static void hisi_l3c_pmu_enable_counter_int(struct hisi_pmu *l3c_pmu,
  129. struct hw_perf_event *hwc)
  130. {
  131. u32 val;
  132. val = readl(l3c_pmu->base + L3C_INT_MASK);
  133. /* Write 0 to enable interrupt */
  134. val &= ~(1 << hwc->idx);
  135. writel(val, l3c_pmu->base + L3C_INT_MASK);
  136. }
  137. static void hisi_l3c_pmu_disable_counter_int(struct hisi_pmu *l3c_pmu,
  138. struct hw_perf_event *hwc)
  139. {
  140. u32 val;
  141. val = readl(l3c_pmu->base + L3C_INT_MASK);
  142. /* Write 1 to mask interrupt */
  143. val |= (1 << hwc->idx);
  144. writel(val, l3c_pmu->base + L3C_INT_MASK);
  145. }
  146. static irqreturn_t hisi_l3c_pmu_isr(int irq, void *dev_id)
  147. {
  148. struct hisi_pmu *l3c_pmu = dev_id;
  149. struct perf_event *event;
  150. unsigned long overflown;
  151. int idx;
  152. /* Read L3C_INT_STATUS register */
  153. overflown = readl(l3c_pmu->base + L3C_INT_STATUS);
  154. if (!overflown)
  155. return IRQ_NONE;
  156. /*
  157. * Find the counter index which overflowed if the bit was set
  158. * and handle it.
  159. */
  160. for_each_set_bit(idx, &overflown, L3C_NR_COUNTERS) {
  161. /* Write 1 to clear the IRQ status flag */
  162. writel((1 << idx), l3c_pmu->base + L3C_INT_CLEAR);
  163. /* Get the corresponding event struct */
  164. event = l3c_pmu->pmu_events.hw_events[idx];
  165. if (!event)
  166. continue;
  167. hisi_uncore_pmu_event_update(event);
  168. hisi_uncore_pmu_set_event_period(event);
  169. }
  170. return IRQ_HANDLED;
  171. }
  172. static int hisi_l3c_pmu_init_irq(struct hisi_pmu *l3c_pmu,
  173. struct platform_device *pdev)
  174. {
  175. int irq, ret;
  176. /* Read and init IRQ */
  177. irq = platform_get_irq(pdev, 0);
  178. if (irq < 0) {
  179. dev_err(&pdev->dev, "L3C PMU get irq fail; irq:%d\n", irq);
  180. return irq;
  181. }
  182. ret = devm_request_irq(&pdev->dev, irq, hisi_l3c_pmu_isr,
  183. IRQF_NOBALANCING | IRQF_NO_THREAD,
  184. dev_name(&pdev->dev), l3c_pmu);
  185. if (ret < 0) {
  186. dev_err(&pdev->dev,
  187. "Fail to request IRQ:%d ret:%d\n", irq, ret);
  188. return ret;
  189. }
  190. l3c_pmu->irq = irq;
  191. return 0;
  192. }
  193. static const struct acpi_device_id hisi_l3c_pmu_acpi_match[] = {
  194. { "HISI0213", },
  195. {},
  196. };
  197. MODULE_DEVICE_TABLE(acpi, hisi_l3c_pmu_acpi_match);
  198. static int hisi_l3c_pmu_init_data(struct platform_device *pdev,
  199. struct hisi_pmu *l3c_pmu)
  200. {
  201. unsigned long long id;
  202. struct resource *res;
  203. acpi_status status;
  204. status = acpi_evaluate_integer(ACPI_HANDLE(&pdev->dev),
  205. "_UID", NULL, &id);
  206. if (ACPI_FAILURE(status))
  207. return -EINVAL;
  208. l3c_pmu->index_id = id;
  209. /*
  210. * Use the SCCL_ID and CCL_ID to identify the L3C PMU, while
  211. * SCCL_ID is in MPIDR[aff2] and CCL_ID is in MPIDR[aff1].
  212. */
  213. if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id",
  214. &l3c_pmu->sccl_id)) {
  215. dev_err(&pdev->dev, "Can not read l3c sccl-id!\n");
  216. return -EINVAL;
  217. }
  218. if (device_property_read_u32(&pdev->dev, "hisilicon,ccl-id",
  219. &l3c_pmu->ccl_id)) {
  220. dev_err(&pdev->dev, "Can not read l3c ccl-id!\n");
  221. return -EINVAL;
  222. }
  223. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  224. l3c_pmu->base = devm_ioremap_resource(&pdev->dev, res);
  225. if (IS_ERR(l3c_pmu->base)) {
  226. dev_err(&pdev->dev, "ioremap failed for l3c_pmu resource\n");
  227. return PTR_ERR(l3c_pmu->base);
  228. }
  229. return 0;
  230. }
  231. static struct attribute *hisi_l3c_pmu_format_attr[] = {
  232. HISI_PMU_FORMAT_ATTR(event, "config:0-7"),
  233. NULL,
  234. };
  235. static const struct attribute_group hisi_l3c_pmu_format_group = {
  236. .name = "format",
  237. .attrs = hisi_l3c_pmu_format_attr,
  238. };
  239. static struct attribute *hisi_l3c_pmu_events_attr[] = {
  240. HISI_PMU_EVENT_ATTR(rd_cpipe, 0x00),
  241. HISI_PMU_EVENT_ATTR(wr_cpipe, 0x01),
  242. HISI_PMU_EVENT_ATTR(rd_hit_cpipe, 0x02),
  243. HISI_PMU_EVENT_ATTR(wr_hit_cpipe, 0x03),
  244. HISI_PMU_EVENT_ATTR(victim_num, 0x04),
  245. HISI_PMU_EVENT_ATTR(rd_spipe, 0x20),
  246. HISI_PMU_EVENT_ATTR(wr_spipe, 0x21),
  247. HISI_PMU_EVENT_ATTR(rd_hit_spipe, 0x22),
  248. HISI_PMU_EVENT_ATTR(wr_hit_spipe, 0x23),
  249. HISI_PMU_EVENT_ATTR(back_invalid, 0x29),
  250. HISI_PMU_EVENT_ATTR(retry_cpu, 0x40),
  251. HISI_PMU_EVENT_ATTR(retry_ring, 0x41),
  252. HISI_PMU_EVENT_ATTR(prefetch_drop, 0x42),
  253. NULL,
  254. };
  255. static const struct attribute_group hisi_l3c_pmu_events_group = {
  256. .name = "events",
  257. .attrs = hisi_l3c_pmu_events_attr,
  258. };
  259. static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL);
  260. static struct attribute *hisi_l3c_pmu_cpumask_attrs[] = {
  261. &dev_attr_cpumask.attr,
  262. NULL,
  263. };
  264. static const struct attribute_group hisi_l3c_pmu_cpumask_attr_group = {
  265. .attrs = hisi_l3c_pmu_cpumask_attrs,
  266. };
  267. static const struct attribute_group *hisi_l3c_pmu_attr_groups[] = {
  268. &hisi_l3c_pmu_format_group,
  269. &hisi_l3c_pmu_events_group,
  270. &hisi_l3c_pmu_cpumask_attr_group,
  271. NULL,
  272. };
  273. static const struct hisi_uncore_ops hisi_uncore_l3c_ops = {
  274. .write_evtype = hisi_l3c_pmu_write_evtype,
  275. .get_event_idx = hisi_uncore_pmu_get_event_idx,
  276. .start_counters = hisi_l3c_pmu_start_counters,
  277. .stop_counters = hisi_l3c_pmu_stop_counters,
  278. .enable_counter = hisi_l3c_pmu_enable_counter,
  279. .disable_counter = hisi_l3c_pmu_disable_counter,
  280. .enable_counter_int = hisi_l3c_pmu_enable_counter_int,
  281. .disable_counter_int = hisi_l3c_pmu_disable_counter_int,
  282. .write_counter = hisi_l3c_pmu_write_counter,
  283. .read_counter = hisi_l3c_pmu_read_counter,
  284. };
  285. static int hisi_l3c_pmu_dev_probe(struct platform_device *pdev,
  286. struct hisi_pmu *l3c_pmu)
  287. {
  288. int ret;
  289. ret = hisi_l3c_pmu_init_data(pdev, l3c_pmu);
  290. if (ret)
  291. return ret;
  292. ret = hisi_l3c_pmu_init_irq(l3c_pmu, pdev);
  293. if (ret)
  294. return ret;
  295. l3c_pmu->num_counters = L3C_NR_COUNTERS;
  296. l3c_pmu->counter_bits = 48;
  297. l3c_pmu->ops = &hisi_uncore_l3c_ops;
  298. l3c_pmu->dev = &pdev->dev;
  299. l3c_pmu->on_cpu = -1;
  300. l3c_pmu->check_event = 0x59;
  301. return 0;
  302. }
  303. static int hisi_l3c_pmu_probe(struct platform_device *pdev)
  304. {
  305. struct hisi_pmu *l3c_pmu;
  306. char *name;
  307. int ret;
  308. l3c_pmu = devm_kzalloc(&pdev->dev, sizeof(*l3c_pmu), GFP_KERNEL);
  309. if (!l3c_pmu)
  310. return -ENOMEM;
  311. platform_set_drvdata(pdev, l3c_pmu);
  312. ret = hisi_l3c_pmu_dev_probe(pdev, l3c_pmu);
  313. if (ret)
  314. return ret;
  315. ret = cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE,
  316. &l3c_pmu->node);
  317. if (ret) {
  318. dev_err(&pdev->dev, "Error %d registering hotplug\n", ret);
  319. return ret;
  320. }
  321. name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%u_l3c%u",
  322. l3c_pmu->sccl_id, l3c_pmu->index_id);
  323. l3c_pmu->pmu = (struct pmu) {
  324. .name = name,
  325. .task_ctx_nr = perf_invalid_context,
  326. .event_init = hisi_uncore_pmu_event_init,
  327. .pmu_enable = hisi_uncore_pmu_enable,
  328. .pmu_disable = hisi_uncore_pmu_disable,
  329. .add = hisi_uncore_pmu_add,
  330. .del = hisi_uncore_pmu_del,
  331. .start = hisi_uncore_pmu_start,
  332. .stop = hisi_uncore_pmu_stop,
  333. .read = hisi_uncore_pmu_read,
  334. .attr_groups = hisi_l3c_pmu_attr_groups,
  335. };
  336. ret = perf_pmu_register(&l3c_pmu->pmu, name, -1);
  337. if (ret) {
  338. dev_err(l3c_pmu->dev, "L3C PMU register failed!\n");
  339. cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE,
  340. &l3c_pmu->node);
  341. }
  342. return ret;
  343. }
  344. static int hisi_l3c_pmu_remove(struct platform_device *pdev)
  345. {
  346. struct hisi_pmu *l3c_pmu = platform_get_drvdata(pdev);
  347. perf_pmu_unregister(&l3c_pmu->pmu);
  348. cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE,
  349. &l3c_pmu->node);
  350. return 0;
  351. }
  352. static struct platform_driver hisi_l3c_pmu_driver = {
  353. .driver = {
  354. .name = "hisi_l3c_pmu",
  355. .acpi_match_table = ACPI_PTR(hisi_l3c_pmu_acpi_match),
  356. },
  357. .probe = hisi_l3c_pmu_probe,
  358. .remove = hisi_l3c_pmu_remove,
  359. };
  360. static int __init hisi_l3c_pmu_module_init(void)
  361. {
  362. int ret;
  363. ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE,
  364. "AP_PERF_ARM_HISI_L3_ONLINE",
  365. hisi_uncore_pmu_online_cpu,
  366. hisi_uncore_pmu_offline_cpu);
  367. if (ret) {
  368. pr_err("L3C PMU: Error setup hotplug, ret = %d\n", ret);
  369. return ret;
  370. }
  371. ret = platform_driver_register(&hisi_l3c_pmu_driver);
  372. if (ret)
  373. cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE);
  374. return ret;
  375. }
  376. module_init(hisi_l3c_pmu_module_init);
  377. static void __exit hisi_l3c_pmu_module_exit(void)
  378. {
  379. platform_driver_unregister(&hisi_l3c_pmu_driver);
  380. cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE);
  381. }
  382. module_exit(hisi_l3c_pmu_module_exit);
  383. MODULE_DESCRIPTION("HiSilicon SoC L3C uncore PMU driver");
  384. MODULE_LICENSE("GPL v2");
  385. MODULE_AUTHOR("Anurup M <anurup.m@huawei.com>");
  386. MODULE_AUTHOR("Shaokun Zhang <zhangshaokun@hisilicon.com>");