.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * APM X-Gene SoC PMU (Performance Monitor Unit) |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (c) 2016, Applied Micro Circuits Corporation |
---|
5 | 6 | * Author: Hoan Tran <hotran@apm.com> |
---|
6 | 7 | * Tai Nguyen <ttnguyen@apm.com> |
---|
7 | | - * |
---|
8 | | - * This program is free software; you can redistribute it and/or modify it |
---|
9 | | - * under the terms of the GNU General Public License as published by the |
---|
10 | | - * Free Software Foundation; either version 2 of the License, or (at your |
---|
11 | | - * option) any later version. |
---|
12 | | - * |
---|
13 | | - * This program is distributed in the hope that it will be useful, |
---|
14 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
15 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
16 | | - * GNU General Public License for more details. |
---|
17 | | - * |
---|
18 | | - * You should have received a copy of the GNU General Public License |
---|
19 | | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
---|
20 | 8 | */ |
---|
21 | 9 | |
---|
22 | 10 | #include <linux/acpi.h> |
---|
23 | 11 | #include <linux/clk.h> |
---|
| 12 | +#include <linux/cpuhotplug.h> |
---|
24 | 13 | #include <linux/cpumask.h> |
---|
25 | 14 | #include <linux/interrupt.h> |
---|
26 | 15 | #include <linux/io.h> |
---|
.. | .. |
---|
130 | 119 | |
---|
131 | 120 | struct xgene_pmu { |
---|
132 | 121 | struct device *dev; |
---|
| 122 | + struct hlist_node node; |
---|
133 | 123 | int version; |
---|
134 | 124 | void __iomem *pcppmu_csr; |
---|
135 | 125 | u32 mcb_active_mask; |
---|
136 | 126 | u32 mc_active_mask; |
---|
137 | 127 | u32 l3c_active_mask; |
---|
138 | 128 | cpumask_t cpu; |
---|
| 129 | + int irq; |
---|
139 | 130 | raw_spinlock_t lock; |
---|
140 | 131 | const struct xgene_pmu_ops *ops; |
---|
141 | 132 | struct list_head l3cpmus; |
---|
.. | .. |
---|
914 | 905 | if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK) |
---|
915 | 906 | return -EINVAL; |
---|
916 | 907 | |
---|
917 | | - /* SOC counters do not have usr/os/guest/host bits */ |
---|
918 | | - if (event->attr.exclude_user || event->attr.exclude_kernel || |
---|
919 | | - event->attr.exclude_host || event->attr.exclude_guest) |
---|
920 | | - return -EINVAL; |
---|
921 | | - |
---|
922 | 908 | if (event->cpu < 0) |
---|
923 | 909 | return -EINVAL; |
---|
924 | 910 | /* |
---|
.. | .. |
---|
1054 | 1040 | static void xgene_perf_stop(struct perf_event *event, int flags) |
---|
1055 | 1041 | { |
---|
1056 | 1042 | struct hw_perf_event *hw = &event->hw; |
---|
1057 | | - u64 config; |
---|
1058 | 1043 | |
---|
1059 | 1044 | if (hw->state & PERF_HES_UPTODATE) |
---|
1060 | 1045 | return; |
---|
.. | .. |
---|
1066 | 1051 | if (hw->state & PERF_HES_UPTODATE) |
---|
1067 | 1052 | return; |
---|
1068 | 1053 | |
---|
1069 | | - config = hw->config; |
---|
1070 | 1054 | xgene_perf_read(event); |
---|
1071 | 1055 | hw->state |= PERF_HES_UPTODATE; |
---|
1072 | 1056 | } |
---|
.. | .. |
---|
1133 | 1117 | .start = xgene_perf_start, |
---|
1134 | 1118 | .stop = xgene_perf_stop, |
---|
1135 | 1119 | .read = xgene_perf_read, |
---|
| 1120 | + .capabilities = PERF_PMU_CAP_NO_EXCLUDE, |
---|
1136 | 1121 | }; |
---|
1137 | 1122 | |
---|
1138 | 1123 | /* Hardware counter init */ |
---|
.. | .. |
---|
1297 | 1282 | struct platform_device *pdev) |
---|
1298 | 1283 | { |
---|
1299 | 1284 | void __iomem *csw_csr, *mcba_csr, *mcbb_csr; |
---|
1300 | | - struct resource *res; |
---|
1301 | 1285 | unsigned int reg; |
---|
1302 | 1286 | |
---|
1303 | | - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
---|
1304 | | - csw_csr = devm_ioremap_resource(&pdev->dev, res); |
---|
| 1287 | + csw_csr = devm_platform_ioremap_resource(pdev, 1); |
---|
1305 | 1288 | if (IS_ERR(csw_csr)) { |
---|
1306 | 1289 | dev_err(&pdev->dev, "ioremap failed for CSW CSR resource\n"); |
---|
1307 | 1290 | return PTR_ERR(csw_csr); |
---|
1308 | 1291 | } |
---|
1309 | 1292 | |
---|
1310 | | - res = platform_get_resource(pdev, IORESOURCE_MEM, 2); |
---|
1311 | | - mcba_csr = devm_ioremap_resource(&pdev->dev, res); |
---|
| 1293 | + mcba_csr = devm_platform_ioremap_resource(pdev, 2); |
---|
1312 | 1294 | if (IS_ERR(mcba_csr)) { |
---|
1313 | 1295 | dev_err(&pdev->dev, "ioremap failed for MCBA CSR resource\n"); |
---|
1314 | 1296 | return PTR_ERR(mcba_csr); |
---|
1315 | 1297 | } |
---|
1316 | 1298 | |
---|
1317 | | - res = platform_get_resource(pdev, IORESOURCE_MEM, 3); |
---|
1318 | | - mcbb_csr = devm_ioremap_resource(&pdev->dev, res); |
---|
| 1299 | + mcbb_csr = devm_platform_ioremap_resource(pdev, 3); |
---|
1319 | 1300 | if (IS_ERR(mcbb_csr)) { |
---|
1320 | 1301 | dev_err(&pdev->dev, "ioremap failed for MCBB CSR resource\n"); |
---|
1321 | 1302 | return PTR_ERR(mcbb_csr); |
---|
.. | .. |
---|
1347 | 1328 | struct platform_device *pdev) |
---|
1348 | 1329 | { |
---|
1349 | 1330 | void __iomem *csw_csr; |
---|
1350 | | - struct resource *res; |
---|
1351 | 1331 | unsigned int reg; |
---|
1352 | 1332 | u32 mcb0routing; |
---|
1353 | 1333 | u32 mcb1routing; |
---|
1354 | 1334 | |
---|
1355 | | - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
---|
1356 | | - csw_csr = devm_ioremap_resource(&pdev->dev, res); |
---|
| 1335 | + csw_csr = devm_platform_ioremap_resource(pdev, 1); |
---|
1357 | 1336 | if (IS_ERR(csw_csr)) { |
---|
1358 | 1337 | dev_err(&pdev->dev, "ioremap failed for CSW CSR resource\n"); |
---|
1359 | 1338 | return PTR_ERR(csw_csr); |
---|
.. | .. |
---|
1808 | 1787 | MODULE_DEVICE_TABLE(acpi, xgene_pmu_acpi_match); |
---|
1809 | 1788 | #endif |
---|
1810 | 1789 | |
---|
| 1790 | +static int xgene_pmu_online_cpu(unsigned int cpu, struct hlist_node *node) |
---|
| 1791 | +{ |
---|
| 1792 | + struct xgene_pmu *xgene_pmu = hlist_entry_safe(node, struct xgene_pmu, |
---|
| 1793 | + node); |
---|
| 1794 | + |
---|
| 1795 | + if (cpumask_empty(&xgene_pmu->cpu)) |
---|
| 1796 | + cpumask_set_cpu(cpu, &xgene_pmu->cpu); |
---|
| 1797 | + |
---|
| 1798 | + /* Overflow interrupt also should use the same CPU */ |
---|
| 1799 | + WARN_ON(irq_set_affinity(xgene_pmu->irq, &xgene_pmu->cpu)); |
---|
| 1800 | + |
---|
| 1801 | + return 0; |
---|
| 1802 | +} |
---|
| 1803 | + |
---|
| 1804 | +static int xgene_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) |
---|
| 1805 | +{ |
---|
| 1806 | + struct xgene_pmu *xgene_pmu = hlist_entry_safe(node, struct xgene_pmu, |
---|
| 1807 | + node); |
---|
| 1808 | + struct xgene_pmu_dev_ctx *ctx; |
---|
| 1809 | + unsigned int target; |
---|
| 1810 | + |
---|
| 1811 | + if (!cpumask_test_and_clear_cpu(cpu, &xgene_pmu->cpu)) |
---|
| 1812 | + return 0; |
---|
| 1813 | + target = cpumask_any_but(cpu_online_mask, cpu); |
---|
| 1814 | + if (target >= nr_cpu_ids) |
---|
| 1815 | + return 0; |
---|
| 1816 | + |
---|
| 1817 | + list_for_each_entry(ctx, &xgene_pmu->mcpmus, next) { |
---|
| 1818 | + perf_pmu_migrate_context(&ctx->pmu_dev->pmu, cpu, target); |
---|
| 1819 | + } |
---|
| 1820 | + list_for_each_entry(ctx, &xgene_pmu->mcbpmus, next) { |
---|
| 1821 | + perf_pmu_migrate_context(&ctx->pmu_dev->pmu, cpu, target); |
---|
| 1822 | + } |
---|
| 1823 | + list_for_each_entry(ctx, &xgene_pmu->l3cpmus, next) { |
---|
| 1824 | + perf_pmu_migrate_context(&ctx->pmu_dev->pmu, cpu, target); |
---|
| 1825 | + } |
---|
| 1826 | + list_for_each_entry(ctx, &xgene_pmu->iobpmus, next) { |
---|
| 1827 | + perf_pmu_migrate_context(&ctx->pmu_dev->pmu, cpu, target); |
---|
| 1828 | + } |
---|
| 1829 | + |
---|
| 1830 | + cpumask_set_cpu(target, &xgene_pmu->cpu); |
---|
| 1831 | + /* Overflow interrupt also should use the same CPU */ |
---|
| 1832 | + WARN_ON(irq_set_affinity(xgene_pmu->irq, &xgene_pmu->cpu)); |
---|
| 1833 | + |
---|
| 1834 | + return 0; |
---|
| 1835 | +} |
---|
| 1836 | + |
---|
1811 | 1837 | static int xgene_pmu_probe(struct platform_device *pdev) |
---|
1812 | 1838 | { |
---|
1813 | 1839 | const struct xgene_pmu_data *dev_data; |
---|
.. | .. |
---|
1816 | 1842 | struct resource *res; |
---|
1817 | 1843 | int irq, rc; |
---|
1818 | 1844 | int version; |
---|
| 1845 | + |
---|
| 1846 | + /* Install a hook to update the reader CPU in case it goes offline */ |
---|
| 1847 | + rc = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_APM_XGENE_ONLINE, |
---|
| 1848 | + "CPUHP_AP_PERF_ARM_APM_XGENE_ONLINE", |
---|
| 1849 | + xgene_pmu_online_cpu, |
---|
| 1850 | + xgene_pmu_offline_cpu); |
---|
| 1851 | + if (rc) |
---|
| 1852 | + return rc; |
---|
1819 | 1853 | |
---|
1820 | 1854 | xgene_pmu = devm_kzalloc(&pdev->dev, sizeof(*xgene_pmu), GFP_KERNEL); |
---|
1821 | 1855 | if (!xgene_pmu) |
---|
.. | .. |
---|
1863 | 1897 | } |
---|
1864 | 1898 | |
---|
1865 | 1899 | irq = platform_get_irq(pdev, 0); |
---|
1866 | | - if (irq < 0) { |
---|
1867 | | - dev_err(&pdev->dev, "No IRQ resource\n"); |
---|
| 1900 | + if (irq < 0) |
---|
1868 | 1901 | return -EINVAL; |
---|
1869 | | - } |
---|
| 1902 | + |
---|
1870 | 1903 | rc = devm_request_irq(&pdev->dev, irq, xgene_pmu_isr, |
---|
1871 | 1904 | IRQF_NOBALANCING | IRQF_NO_THREAD, |
---|
1872 | 1905 | dev_name(&pdev->dev), xgene_pmu); |
---|
.. | .. |
---|
1874 | 1907 | dev_err(&pdev->dev, "Could not request IRQ %d\n", irq); |
---|
1875 | 1908 | return rc; |
---|
1876 | 1909 | } |
---|
| 1910 | + |
---|
| 1911 | + xgene_pmu->irq = irq; |
---|
1877 | 1912 | |
---|
1878 | 1913 | raw_spin_lock_init(&xgene_pmu->lock); |
---|
1879 | 1914 | |
---|
.. | .. |
---|
1885 | 1920 | xgene_pmu->mc_active_mask = 0x1; |
---|
1886 | 1921 | } |
---|
1887 | 1922 | |
---|
1888 | | - /* Pick one core to use for cpumask attributes */ |
---|
1889 | | - cpumask_set_cpu(smp_processor_id(), &xgene_pmu->cpu); |
---|
1890 | | - |
---|
1891 | | - /* Make sure that the overflow interrupt is handled by this CPU */ |
---|
1892 | | - rc = irq_set_affinity(irq, &xgene_pmu->cpu); |
---|
| 1923 | + /* Add this instance to the list used by the hotplug callback */ |
---|
| 1924 | + rc = cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_APM_XGENE_ONLINE, |
---|
| 1925 | + &xgene_pmu->node); |
---|
1893 | 1926 | if (rc) { |
---|
1894 | | - dev_err(&pdev->dev, "Failed to set interrupt affinity!\n"); |
---|
| 1927 | + dev_err(&pdev->dev, "Error %d registering hotplug", rc); |
---|
1895 | 1928 | return rc; |
---|
1896 | 1929 | } |
---|
1897 | 1930 | |
---|
.. | .. |
---|
1899 | 1932 | rc = xgene_pmu_probe_pmu_dev(xgene_pmu, pdev); |
---|
1900 | 1933 | if (rc) { |
---|
1901 | 1934 | dev_err(&pdev->dev, "No PMU perf devices found!\n"); |
---|
1902 | | - return rc; |
---|
| 1935 | + goto out_unregister; |
---|
1903 | 1936 | } |
---|
1904 | 1937 | |
---|
1905 | 1938 | /* Enable interrupt */ |
---|
1906 | 1939 | xgene_pmu->ops->unmask_int(xgene_pmu); |
---|
1907 | 1940 | |
---|
1908 | 1941 | return 0; |
---|
| 1942 | + |
---|
| 1943 | +out_unregister: |
---|
| 1944 | + cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_APM_XGENE_ONLINE, |
---|
| 1945 | + &xgene_pmu->node); |
---|
| 1946 | + return rc; |
---|
1909 | 1947 | } |
---|
1910 | 1948 | |
---|
1911 | 1949 | static void |
---|
.. | .. |
---|
1926 | 1964 | xgene_pmu_dev_cleanup(xgene_pmu, &xgene_pmu->iobpmus); |
---|
1927 | 1965 | xgene_pmu_dev_cleanup(xgene_pmu, &xgene_pmu->mcbpmus); |
---|
1928 | 1966 | xgene_pmu_dev_cleanup(xgene_pmu, &xgene_pmu->mcpmus); |
---|
| 1967 | + cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_APM_XGENE_ONLINE, |
---|
| 1968 | + &xgene_pmu->node); |
---|
1929 | 1969 | |
---|
1930 | 1970 | return 0; |
---|
1931 | 1971 | } |
---|
.. | .. |
---|
1937 | 1977 | .name = "xgene-pmu", |
---|
1938 | 1978 | .of_match_table = xgene_pmu_of_match, |
---|
1939 | 1979 | .acpi_match_table = ACPI_PTR(xgene_pmu_acpi_match), |
---|
| 1980 | + .suppress_bind_attrs = true, |
---|
1940 | 1981 | }, |
---|
1941 | 1982 | }; |
---|
1942 | 1983 | |
---|