This series integrates already accepted (or already posted and got acked) patches that: + Provide bug fixes for cppcc_cpufreq driver, i2c-designware driver, mailbox driver, X-Gene SlimPRO I2C driver, GIC PCI legacy interrupt polarity. + Add support for X-Gene HWMon + Add support for X-Gene SoC PMU + Update X-Gene Enet driver
The series also enables cppc_cpufreq, i2c-designware, X-Gene HWMon, X-Gene SoC PMU drivers by default.
v2 changes: - Move all config options changes to config-centos-sig
Duc Dang (11): Add cpufreq CPPC fixes Enable CONFIG_ACPI_CPPC_CPUFREQ by default Integrate upstream fix for return value of pcc_mbox_request_channel Add X-Gene HWMon support Add X-Gene SoC PMU support Integrate fix to avoid DMA buffer overrun on X-Gene SlimPRO I2C driver Integrate upstream patches for X-Gene Enet driver Integrate upstream fix for GIC default PCI IRQ polarity Add SMBus block read/write support for Designware I2C Enable I2C Designware support by default Integrate upstream fix to make X-Gene HWmon access mailbox as RAM
...C-Avoid-overflow-when-calculating-desired.patch | 31 + ...req-CPPC-Correct-desired_perf-calculation.patch | 46 + ...-Fix-return-value-of-pcc_mbox_request_cha.patch | 33 + ...on-dtb-xgene-Add-hwmon-dts-binding-docume.patch | 39 + SOURCES/1005-hwmon-Add-xgene-hwmon-driver.patch | 850 +++++++++++ ...-Fix-crash-when-alarm-occurs-before-drive.patch | 158 ++ ...Add-APM-X-Gene-SoC-Performance-Monitoring.patch | 1508 ++++++++++++++++++++ ...1008-perf-xgene-Remove-bogus-IS_ERR-check.patch | 39 + ...S-Add-entry-for-APM-X-Gene-SoC-PMU-driver.patch | 34 + .../1010-i2c-xgene-Avoid-dma_buffer-overrun.patch | 32 + ...net-xgene-fix-error-handling-during-reset.patch | 72 + ...-net-xgene-fix-backward-compatibility-fix.patch | 47 + ...t-apm-xgene-use-phydev-from-struct-net_de.patch | 150 ++ ...014-drivers-net-xgene-Fix-MSS-programming.patch | 297 ++++ ...net-xgene-fix-Use-GPIO-to-get-link-status.patch | 91 ++ ...-xgene-fix-Disable-coalescing-on-v1-hardw.patch | 61 + ...-xgene-fix-Coalescing-values-for-v2-hardw.patch | 67 + ...ix-GIC-irq-model-default-PCI-IRQ-polarity.patch | 83 ++ ...are-Implement-support-for-SMBus-block-rea.patch | 135 ++ .../1020-hwmon-xgene-access-mailbox-as-RAM.patch | 124 ++ SOURCES/config-centos-sig | 7 + SPECS/kernel-aarch64.spec | 57 +- 22 files changed, 3960 insertions(+), 1 deletion(-) create mode 100644 SOURCES/1001-cpufreq-CPPC-Avoid-overflow-when-calculating-desired.patch create mode 100644 SOURCES/1002-cpufreq-CPPC-Correct-desired_perf-calculation.patch create mode 100644 SOURCES/1003-mailbox-PCC-Fix-return-value-of-pcc_mbox_request_cha.patch create mode 100644 SOURCES/1004-Documentation-dtb-xgene-Add-hwmon-dts-binding-docume.patch create mode 100644 SOURCES/1005-hwmon-Add-xgene-hwmon-driver.patch create mode 100644 SOURCES/1006-hwmon-xgene-Fix-crash-when-alarm-occurs-before-drive.patch create mode 100644 SOURCES/1007-perf-xgene-Add-APM-X-Gene-SoC-Performance-Monitoring.patch create mode 100644 SOURCES/1008-perf-xgene-Remove-bogus-IS_ERR-check.patch create mode 100644 SOURCES/1009-MAINTAINERS-Add-entry-for-APM-X-Gene-SoC-PMU-driver.patch create mode 100644 SOURCES/1010-i2c-xgene-Avoid-dma_buffer-overrun.patch create mode 100644 SOURCES/1011-net-xgene-fix-error-handling-during-reset.patch create mode 100644 SOURCES/1012-net-xgene-fix-backward-compatibility-fix.patch create mode 100644 SOURCES/1013-net-ethernet-apm-xgene-use-phydev-from-struct-net_de.patch create mode 100644 SOURCES/1014-drivers-net-xgene-Fix-MSS-programming.patch create mode 100644 SOURCES/1015-drivers-net-xgene-fix-Use-GPIO-to-get-link-status.patch create mode 100644 SOURCES/1016-drivers-net-xgene-fix-Disable-coalescing-on-v1-hardw.patch create mode 100644 SOURCES/1017-drivers-net-xgene-fix-Coalescing-values-for-v2-hardw.patch create mode 100644 SOURCES/1018-ACPI-PCI-fix-GIC-irq-model-default-PCI-IRQ-polarity.patch create mode 100644 SOURCES/1019-i2c-designware-Implement-support-for-SMBus-block-rea.patch create mode 100644 SOURCES/1020-hwmon-xgene-access-mailbox-as-RAM.patch
Integrate 2 upstream fixes to correct the desired performance calculation in cppc_cpufreq driver.
Signed-off-by: Duc Dang dhdang@apm.com --- ...C-Avoid-overflow-when-calculating-desired.patch | 31 +++++++++++++++ ...req-CPPC-Correct-desired_perf-calculation.patch | 46 ++++++++++++++++++++++ SPECS/kernel-aarch64.spec | 13 +++++- 3 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 SOURCES/1001-cpufreq-CPPC-Avoid-overflow-when-calculating-desired.patch create mode 100644 SOURCES/1002-cpufreq-CPPC-Correct-desired_perf-calculation.patch
diff --git a/SOURCES/1001-cpufreq-CPPC-Avoid-overflow-when-calculating-desired.patch b/SOURCES/1001-cpufreq-CPPC-Avoid-overflow-when-calculating-desired.patch new file mode 100644 index 0000000..486f175 --- /dev/null +++ b/SOURCES/1001-cpufreq-CPPC-Avoid-overflow-when-calculating-desired.patch @@ -0,0 +1,31 @@ +From 8b5a7d03a65c86a4473bd10b582fc9b6fe434c31 Mon Sep 17 00:00:00 2001 +From: Hoan Tran hotran@apm.com +Date: Wed, 14 Sep 2016 16:08:28 -0700 +Subject: [PATCH 1001/1018] cpufreq: CPPC: Avoid overflow when calculating + desired_perf + +This patch fixes overflow issue when calculating the desired_perf. + +Fixes: ad38677df44b (cpufreq: CPPC: Force reporting values in KHz to fix user space interface) +Signed-off-by: Hoan Tran hotran@apm.com +Signed-off-by: Rafael J. Wysocki rafael.j.wysocki@intel.com +--- + drivers/cpufreq/cppc_cpufreq.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c +index 6fe8415..61d2ac6 100644 +--- a/drivers/cpufreq/cppc_cpufreq.c ++++ b/drivers/cpufreq/cppc_cpufreq.c +@@ -84,7 +84,7 @@ static int cppc_cpufreq_set_target(struct cpufreq_policy *policy, + + cpu = all_cpu_data[policy->cpu]; + +- cpu->perf_ctrls.desired_perf = target_freq * policy->max / cppc_dmi_max_khz; ++ cpu->perf_ctrls.desired_perf = (u64)target_freq * policy->max / cppc_dmi_max_khz; + freqs.old = policy->cur; + freqs.new = target_freq; + +-- +1.8.3.1 + diff --git a/SOURCES/1002-cpufreq-CPPC-Correct-desired_perf-calculation.patch b/SOURCES/1002-cpufreq-CPPC-Correct-desired_perf-calculation.patch new file mode 100644 index 0000000..ec16cc1 --- /dev/null +++ b/SOURCES/1002-cpufreq-CPPC-Correct-desired_perf-calculation.patch @@ -0,0 +1,46 @@ +From 3ed005a638f6790c6c80fd8ea99b1b054a55a500 Mon Sep 17 00:00:00 2001 +From: Hoan Tran hotran@apm.com +Date: Thu, 13 Oct 2016 10:33:35 -0700 +Subject: [PATCH 1002/1018] cpufreq: CPPC: Correct desired_perf calculation + +The desired_perf is an abstract performance number. Its value should +be in the range of [lowest perf, highest perf] of CPPC. +The correct calculation is + desired_perf = freq * cppc_highest_perf / cppc_dmi_max_khz + +And cppc_cpufreq_set_target() returns if desired_perf is exactly +the same with the old perf. + +Signed-off-by: Hoan Tran hotran@apm.com +Reviewed-by: Prashanth Prakash pprakash@codeaurora.org +Signed-off-by: Rafael J. Wysocki rafael.j.wysocki@intel.com +--- + drivers/cpufreq/cppc_cpufreq.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c +index 61d2ac6..c87f655 100644 +--- a/drivers/cpufreq/cppc_cpufreq.c ++++ b/drivers/cpufreq/cppc_cpufreq.c +@@ -80,11 +80,17 @@ static int cppc_cpufreq_set_target(struct cpufreq_policy *policy, + { + struct cpudata *cpu; + struct cpufreq_freqs freqs; ++ u32 desired_perf; + int ret = 0; + + cpu = all_cpu_data[policy->cpu]; + +- cpu->perf_ctrls.desired_perf = (u64)target_freq * policy->max / cppc_dmi_max_khz; ++ desired_perf = (u64)target_freq * cpu->perf_caps.highest_perf / cppc_dmi_max_khz; ++ /* Return if it is exactly the same perf */ ++ if (desired_perf == cpu->perf_ctrls.desired_perf) ++ return ret; ++ ++ cpu->perf_ctrls.desired_perf = desired_perf; + freqs.old = policy->cur; + freqs.new = target_freq; + +-- +1.8.3.1 + diff --git a/SPECS/kernel-aarch64.spec b/SPECS/kernel-aarch64.spec index ffaafe7..2aff470 100644 --- a/SPECS/kernel-aarch64.spec +++ b/SPECS/kernel-aarch64.spec @@ -12,7 +12,7 @@ Summary: The Linux kernel
%define rpmversion 4.5.0 %define pkgrelease 15.el7 -%define centupdate 16.el7 +%define centupdate 17.el7
# allow pkg_release to have configurable %{?dist} tag %define specrelease %%SPECRELEASE%% @@ -324,6 +324,10 @@ Source58: config-generic Source59: config-debug Source60: config-centos-sig
+#APM Patches +Patch1001: 1001-cpufreq-CPPC-Avoid-overflow-when-calculating-desired.patch +Patch1002: 1002-cpufreq-CPPC-Correct-desired_perf-calculation.patch + # QDF2400 Patches Patch4000: 4000-arm64-Define-Qualcomm-Technologies-ARMv8-CPU.patch Patch4001: 4001-arm64-Workaround-Falkor-E1003.patch @@ -657,6 +661,10 @@ if [ ! -d .git ]; then git commit -a -q -m "baseline" fi
+# Aplly APM patches +git am %{PATCH1001} +git am %{PATCH1002} + # Apply QDF2400 patches git am %{PATCH4000} git am %{PATCH4001} @@ -1434,6 +1442,9 @@ fi %kernel_variant_files %{with_debug} kernel-debug debug
%changelog +* Thu Nov 10 2016 Duc Dang dhdang@apm.com [4.5.0-17.el7] +- Add cpufreq CPPC bug fixes + * Mon Nov 07 2016 Christopher Covington cov@codeaurora.org [4.5.0-16.el7] - Add git machinery (Jim Perrin)
This config option will enable support for cppc_cpufreq driver by default on arm64 platforms.
Signed-off-by: Duc Dang dhdang@apm.com --- SOURCES/config-centos-sig | 1 + 1 file changed, 1 insertion(+)
diff --git a/SOURCES/config-centos-sig b/SOURCES/config-centos-sig index 1051e4b..f8fab63 100644 --- a/SOURCES/config-centos-sig +++ b/SOURCES/config-centos-sig @@ -3,3 +3,4 @@
CONFIG_QCOM_FALKOR_ERRATUM_E1003=y CONFIG_QCOM_FALKOR_ERRATUM_E1009=y +CONFIG_ACPI_CPPC_CPUFREQ=y
Signed-off-by: Duc Dang dhdang@apm.com --- ...-Fix-return-value-of-pcc_mbox_request_cha.patch | 33 ++++++++++++++++++++++ SPECS/kernel-aarch64.spec | 3 ++ 2 files changed, 36 insertions(+) create mode 100644 SOURCES/1003-mailbox-PCC-Fix-return-value-of-pcc_mbox_request_cha.patch
diff --git a/SOURCES/1003-mailbox-PCC-Fix-return-value-of-pcc_mbox_request_cha.patch b/SOURCES/1003-mailbox-PCC-Fix-return-value-of-pcc_mbox_request_cha.patch new file mode 100644 index 0000000..1f6dd33 --- /dev/null +++ b/SOURCES/1003-mailbox-PCC-Fix-return-value-of-pcc_mbox_request_cha.patch @@ -0,0 +1,33 @@ +From e96f6512d40958588ae97dfa0f06b1e66af8910d Mon Sep 17 00:00:00 2001 +From: Hoan Tran hotran@apm.com +Date: Fri, 14 Oct 2016 16:20:21 -0700 +Subject: [PATCH 1003/1018] mailbox: PCC: Fix return value of + pcc_mbox_request_channel() + +When CONFIG_PCC is disabled, pcc_mbox_request_channel() needs to +return ERR_PTR(-ENODEV), not a NULL pointer, as the callers of +this function use IS_ERR() to check for error code. + +Signed-off-by: Duc Dang dhdang@apm.com +Signed-off-by: Hoan Tran hotran@apm.com +Signed-off-by: Rafael J. Wysocki rafael.j.wysocki@intel.com +--- + include/acpi/pcc.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/acpi/pcc.h b/include/acpi/pcc.h +index 17a940a..8caa79c 100644 +--- a/include/acpi/pcc.h ++++ b/include/acpi/pcc.h +@@ -21,7 +21,7 @@ extern void pcc_mbox_free_channel(struct mbox_chan *chan); + static inline struct mbox_chan *pcc_mbox_request_channel(struct mbox_client *cl, + int subspace_id) + { +- return NULL; ++ return ERR_PTR(-ENODEV); + } + static inline void pcc_mbox_free_channel(struct mbox_chan *chan) { } + #endif +-- +1.8.3.1 + diff --git a/SPECS/kernel-aarch64.spec b/SPECS/kernel-aarch64.spec index 2aff470..1a5c885 100644 --- a/SPECS/kernel-aarch64.spec +++ b/SPECS/kernel-aarch64.spec @@ -327,6 +327,7 @@ Source60: config-centos-sig #APM Patches Patch1001: 1001-cpufreq-CPPC-Avoid-overflow-when-calculating-desired.patch Patch1002: 1002-cpufreq-CPPC-Correct-desired_perf-calculation.patch +Patch1003: 1003-mailbox-PCC-Fix-return-value-of-pcc_mbox_request_cha.patch
# QDF2400 Patches Patch4000: 4000-arm64-Define-Qualcomm-Technologies-ARMv8-CPU.patch @@ -664,6 +665,7 @@ fi # Aplly APM patches git am %{PATCH1001} git am %{PATCH1002} +git am %{PATCH1003}
# Apply QDF2400 patches git am %{PATCH4000} @@ -1443,6 +1445,7 @@ fi
%changelog * Thu Nov 10 2016 Duc Dang dhdang@apm.com [4.5.0-17.el7] +- mailbox: PCC: Fix return value of pcc_mbox_request_channel - Add cpufreq CPPC bug fixes
* Mon Nov 07 2016 Christopher Covington cov@codeaurora.org [4.5.0-16.el7]
Integrate X-Gene upstream HWMon driver and bug fixes.
Signed-off-by: Duc Dang dhdang@apm.com --- ...on-dtb-xgene-Add-hwmon-dts-binding-docume.patch | 39 + SOURCES/1005-hwmon-Add-xgene-hwmon-driver.patch | 850 +++++++++++++++++++++ ...-Fix-crash-when-alarm-occurs-before-drive.patch | 158 ++++ SOURCES/config-centos-sig | 1 + SPECS/kernel-aarch64.spec | 7 + 5 files changed, 1055 insertions(+) create mode 100644 SOURCES/1004-Documentation-dtb-xgene-Add-hwmon-dts-binding-docume.patch create mode 100644 SOURCES/1005-hwmon-Add-xgene-hwmon-driver.patch create mode 100644 SOURCES/1006-hwmon-xgene-Fix-crash-when-alarm-occurs-before-drive.patch
diff --git a/SOURCES/1004-Documentation-dtb-xgene-Add-hwmon-dts-binding-docume.patch b/SOURCES/1004-Documentation-dtb-xgene-Add-hwmon-dts-binding-docume.patch new file mode 100644 index 0000000..f042588 --- /dev/null +++ b/SOURCES/1004-Documentation-dtb-xgene-Add-hwmon-dts-binding-docume.patch @@ -0,0 +1,39 @@ +From 5ba8581eadf6b596e835392181e289d3a209569c Mon Sep 17 00:00:00 2001 +From: hotran hotran@apm.com +Date: Thu, 21 Jul 2016 13:55:55 -0700 +Subject: [PATCH 1004/1018] Documentation: dtb: xgene: Add hwmon dts binding + documentation + +This patch adds the APM X-Gene hwmon device tree node documentation. + +Signed-off-by: Hoan Tran hotran@apm.com +Acked-by: Rob Herring robh@kernel.org +Signed-off-by: Guenter Roeck linux@roeck-us.net +--- + .../devicetree/bindings/hwmon/apm-xgene-hwmon.txt | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + create mode 100644 Documentation/devicetree/bindings/hwmon/apm-xgene-hwmon.txt + +diff --git a/Documentation/devicetree/bindings/hwmon/apm-xgene-hwmon.txt b/Documentation/devicetree/bindings/hwmon/apm-xgene-hwmon.txt +new file mode 100644 +index 0000000..59b3855 +--- /dev/null ++++ b/Documentation/devicetree/bindings/hwmon/apm-xgene-hwmon.txt +@@ -0,0 +1,14 @@ ++APM X-Gene hwmon driver ++ ++APM X-Gene SOC sensors are accessed over the "SLIMpro" mailbox. ++ ++Required properties : ++ - compatible : should be "apm,xgene-slimpro-hwmon" ++ - mboxes : use the label reference for the mailbox as the first parameter. ++ The second parameter is the channel number. ++ ++Example : ++ hwmonslimpro { ++ compatible = "apm,xgene-slimpro-hwmon"; ++ mboxes = <&mailbox 7>; ++ }; +-- +1.8.3.1 + diff --git a/SOURCES/1005-hwmon-Add-xgene-hwmon-driver.patch b/SOURCES/1005-hwmon-Add-xgene-hwmon-driver.patch new file mode 100644 index 0000000..3025592 --- /dev/null +++ b/SOURCES/1005-hwmon-Add-xgene-hwmon-driver.patch @@ -0,0 +1,850 @@ +From 54bffda1db3651556ce0ebc492d47154bd5b5d2d Mon Sep 17 00:00:00 2001 +From: hotran hotran@apm.com +Date: Thu, 21 Jul 2016 15:37:32 -0700 +Subject: [PATCH 1005/1018] hwmon: Add xgene hwmon driver + +This patch adds hardware temperature and power reading support for +APM X-Gene SoC using the mailbox communication interface. + +Signed-off-by: Hoan Tran hotran@apm.com +Reviewed-by: Guenter Roeck linux@roeck-us.net +Signed-off-by: Guenter Roeck linux@roeck-us.net +--- + Documentation/hwmon/xgene-hwmon | 30 ++ + drivers/hwmon/Kconfig | 7 + + drivers/hwmon/Makefile | 1 + + drivers/hwmon/xgene-hwmon.c | 755 ++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 793 insertions(+) + create mode 100644 Documentation/hwmon/xgene-hwmon + create mode 100644 drivers/hwmon/xgene-hwmon.c + +diff --git a/Documentation/hwmon/xgene-hwmon b/Documentation/hwmon/xgene-hwmon +new file mode 100644 +index 0000000..6ec50ed +--- /dev/null ++++ b/Documentation/hwmon/xgene-hwmon +@@ -0,0 +1,30 @@ ++Kernel driver xgene-hwmon ++======================== ++ ++Supported chips: ++ * APM X-Gene SoC ++ ++Description ++----------- ++ ++This driver adds hardware temperature and power reading support for ++APM X-Gene SoC using the mailbox communication interface. ++For device tree, it is the standard DT mailbox. ++For ACPI, it is the PCC mailbox. ++ ++The following sensors are supported ++ ++ * Temperature ++ - SoC on-die temperature in milli-degree C ++ - Alarm when high/over temperature occurs ++ * Power ++ - CPU power in uW ++ - IO power in uW ++ ++sysfs-Interface ++--------------- ++ ++temp0_input - SoC on-die temperature (milli-degree C) ++temp0_critical_alarm - An 1 would indicates on-die temperature exceeded threshold ++power0_input - CPU power in (uW) ++power1_input - IO power in (uW) +diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig +index 60fb80b..293dbc6 100644 +--- a/drivers/hwmon/Kconfig ++++ b/drivers/hwmon/Kconfig +@@ -1747,6 +1747,13 @@ config SENSORS_ULTRA45 + This driver provides support for the Ultra45 workstation environmental + sensors. + ++config SENSORS_XGENE ++ tristate "APM X-Gene SoC hardware monitoring driver" ++ depends on XGENE_SLIMPRO_MBOX || PCC ++ help ++ If you say yes here you get support for the temperature ++ and power sensors for APM X-Gene SoC. ++ + if ACPI + + comment "ACPI drivers" +diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile +index 30c94df..c43aa07 100644 +--- a/drivers/hwmon/Makefile ++++ b/drivers/hwmon/Makefile +@@ -159,6 +159,7 @@ obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o + obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o + obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o + obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o ++obj-$(CONFIG_SENSORS_XGENE) += xgene-hwmon.o + + obj-$(CONFIG_PMBUS) += pmbus/ + +diff --git a/drivers/hwmon/xgene-hwmon.c b/drivers/hwmon/xgene-hwmon.c +new file mode 100644 +index 0000000..bc78a5d +--- /dev/null ++++ b/drivers/hwmon/xgene-hwmon.c +@@ -0,0 +1,755 @@ ++/* ++ * APM X-Gene SoC Hardware Monitoring Driver ++ * ++ * Copyright (c) 2016, Applied Micro Circuits Corporation ++ * Author: Loc Ho lho@apm.com ++ * Hoan Tran hotran@apm.com ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of ++ * the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see http://www.gnu.org/licenses/. ++ * ++ * This driver provides the following features: ++ * - Retrieve CPU total power (uW) ++ * - Retrieve IO total power (uW) ++ * - Retrieve SoC temperature (milli-degree C) and alarm ++ */ ++#include <linux/acpi.h> ++#include <linux/dma-mapping.h> ++#include <linux/hwmon.h> ++#include <linux/hwmon-sysfs.h> ++#include <linux/interrupt.h> ++#include <linux/kfifo.h> ++#include <linux/mailbox_controller.h> ++#include <linux/mailbox_client.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <acpi/acpi_io.h> ++#include <acpi/pcc.h> ++ ++/* SLIMpro message defines */ ++#define MSG_TYPE_DBG 0 ++#define MSG_TYPE_ERR 7 ++#define MSG_TYPE_PWRMGMT 9 ++ ++#define MSG_TYPE(v) (((v) & 0xF0000000) >> 28) ++#define MSG_TYPE_SET(v) (((v) << 28) & 0xF0000000) ++#define MSG_SUBTYPE(v) (((v) & 0x0F000000) >> 24) ++#define MSG_SUBTYPE_SET(v) (((v) << 24) & 0x0F000000) ++ ++#define DBG_SUBTYPE_SENSOR_READ 4 ++#define SENSOR_RD_MSG 0x04FFE902 ++#define SENSOR_RD_EN_ADDR(a) ((a) & 0x000FFFFF) ++#define PMD_PWR_REG 0x20 ++#define PMD_PWR_MW_REG 0x26 ++#define SOC_PWR_REG 0x21 ++#define SOC_PWR_MW_REG 0x27 ++#define SOC_TEMP_REG 0x10 ++ ++#define TEMP_NEGATIVE_BIT 8 ++#define SENSOR_INVALID_DATA BIT(15) ++ ++#define PWRMGMT_SUBTYPE_TPC 1 ++#define TPC_ALARM 2 ++#define TPC_GET_ALARM 3 ++#define TPC_CMD(v) (((v) & 0x00FF0000) >> 16) ++#define TPC_CMD_SET(v) (((v) << 16) & 0x00FF0000) ++#define TPC_EN_MSG(hndl, cmd, type) \ ++ (MSG_TYPE_SET(MSG_TYPE_PWRMGMT) | \ ++ MSG_SUBTYPE_SET(hndl) | TPC_CMD_SET(cmd) | type) ++ ++/* PCC defines */ ++#define PCC_SIGNATURE_MASK 0x50424300 ++#define PCCC_GENERATE_DB_INT BIT(15) ++#define PCCS_CMD_COMPLETE BIT(0) ++#define PCCS_SCI_DOORBEL BIT(1) ++#define PCCS_PLATFORM_NOTIFICATION BIT(3) ++/* ++ * Arbitrary retries in case the remote processor is slow to respond ++ * to PCC commands ++ */ ++#define PCC_NUM_RETRIES 500 ++ ++#define ASYNC_MSG_FIFO_SIZE 16 ++#define MBOX_OP_TIMEOUTMS 1000 ++ ++#define WATT_TO_mWATT(x) ((x) * 1000) ++#define mWATT_TO_uWATT(x) ((x) * 1000) ++#define CELSIUS_TO_mCELSIUS(x) ((x) * 1000) ++ ++#define to_xgene_hwmon_dev(cl) \ ++ container_of(cl, struct xgene_hwmon_dev, mbox_client) ++ ++struct slimpro_resp_msg { ++ u32 msg; ++ u32 param1; ++ u32 param2; ++} __packed; ++ ++struct xgene_hwmon_dev { ++ struct device *dev; ++ struct mbox_chan *mbox_chan; ++ struct mbox_client mbox_client; ++ int mbox_idx; ++ ++ spinlock_t kfifo_lock; ++ struct mutex rd_mutex; ++ struct completion rd_complete; ++ int resp_pending; ++ struct slimpro_resp_msg sync_msg; ++ ++ struct work_struct workq; ++ struct kfifo_rec_ptr_1 async_msg_fifo; ++ ++ struct device *hwmon_dev; ++ bool temp_critical_alarm; ++ ++ phys_addr_t comm_base_addr; ++ void *pcc_comm_addr; ++ u64 usecs_lat; ++}; ++ ++/* ++ * This function tests and clears a bitmask then returns its old value ++ */ ++static u16 xgene_word_tst_and_clr(u16 *addr, u16 mask) ++{ ++ u16 ret, val; ++ ++ val = readw_relaxed(addr); ++ ret = val & mask; ++ val &= ~mask; ++ writew_relaxed(val, addr); ++ ++ return ret; ++} ++ ++static int xgene_hwmon_pcc_rd(struct xgene_hwmon_dev *ctx, u32 *msg) ++{ ++ struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr; ++ void *ptr = generic_comm_base + 1; ++ int rc, i; ++ u16 val; ++ ++ mutex_lock(&ctx->rd_mutex); ++ init_completion(&ctx->rd_complete); ++ ctx->resp_pending = true; ++ ++ /* Write signature for subspace */ ++ writel_relaxed(PCC_SIGNATURE_MASK | ctx->mbox_idx, ++ &generic_comm_base->signature); ++ ++ /* Write to the shared command region */ ++ writew_relaxed(MSG_TYPE(msg[0]) | PCCC_GENERATE_DB_INT, ++ &generic_comm_base->command); ++ ++ /* Flip CMD COMPLETE bit */ ++ val = readw_relaxed(&generic_comm_base->status); ++ val &= ~PCCS_CMD_COMPLETE; ++ writew_relaxed(val, &generic_comm_base->status); ++ ++ /* Copy the message to the PCC comm space */ ++ for (i = 0; i < sizeof(struct slimpro_resp_msg) / 4; i++) ++ writel_relaxed(msg[i], ptr + i * 4); ++ ++ /* Ring the doorbell */ ++ rc = mbox_send_message(ctx->mbox_chan, msg); ++ if (rc < 0) { ++ dev_err(ctx->dev, "Mailbox send error %d\n", rc); ++ goto err; ++ } ++ if (!wait_for_completion_timeout(&ctx->rd_complete, ++ usecs_to_jiffies(ctx->usecs_lat))) { ++ dev_err(ctx->dev, "Mailbox operation timed out\n"); ++ rc = -ETIMEDOUT; ++ goto err; ++ } ++ ++ /* Check for error message */ ++ if (MSG_TYPE(ctx->sync_msg.msg) == MSG_TYPE_ERR) { ++ rc = -EINVAL; ++ goto err; ++ } ++ ++ msg[0] = ctx->sync_msg.msg; ++ msg[1] = ctx->sync_msg.param1; ++ msg[2] = ctx->sync_msg.param2; ++ ++err: ++ mbox_chan_txdone(ctx->mbox_chan, 0); ++ ctx->resp_pending = false; ++ mutex_unlock(&ctx->rd_mutex); ++ return rc; ++} ++ ++static int xgene_hwmon_rd(struct xgene_hwmon_dev *ctx, u32 *msg) ++{ ++ int rc; ++ ++ mutex_lock(&ctx->rd_mutex); ++ init_completion(&ctx->rd_complete); ++ ctx->resp_pending = true; ++ ++ rc = mbox_send_message(ctx->mbox_chan, msg); ++ if (rc < 0) { ++ dev_err(ctx->dev, "Mailbox send error %d\n", rc); ++ goto err; ++ } ++ ++ if (!wait_for_completion_timeout(&ctx->rd_complete, ++ msecs_to_jiffies(MBOX_OP_TIMEOUTMS))) { ++ dev_err(ctx->dev, "Mailbox operation timed out\n"); ++ rc = -ETIMEDOUT; ++ goto err; ++ } ++ ++ /* Check for error message */ ++ if (MSG_TYPE(ctx->sync_msg.msg) == MSG_TYPE_ERR) { ++ rc = -EINVAL; ++ goto err; ++ } ++ ++ msg[0] = ctx->sync_msg.msg; ++ msg[1] = ctx->sync_msg.param1; ++ msg[2] = ctx->sync_msg.param2; ++ ++err: ++ ctx->resp_pending = false; ++ mutex_unlock(&ctx->rd_mutex); ++ return rc; ++} ++ ++static int xgene_hwmon_reg_map_rd(struct xgene_hwmon_dev *ctx, u32 addr, ++ u32 *data) ++{ ++ u32 msg[3]; ++ int rc; ++ ++ msg[0] = SENSOR_RD_MSG; ++ msg[1] = SENSOR_RD_EN_ADDR(addr); ++ msg[2] = 0; ++ ++ if (acpi_disabled) ++ rc = xgene_hwmon_rd(ctx, msg); ++ else ++ rc = xgene_hwmon_pcc_rd(ctx, msg); ++ ++ if (rc < 0) ++ return rc; ++ ++ /* ++ * Check if sensor data is valid. ++ */ ++ if (msg[1] & SENSOR_INVALID_DATA) ++ return -ENODATA; ++ ++ *data = msg[1]; ++ ++ return rc; ++} ++ ++static int xgene_hwmon_get_notification_msg(struct xgene_hwmon_dev *ctx, ++ u32 *amsg) ++{ ++ u32 msg[3]; ++ int rc; ++ ++ msg[0] = TPC_EN_MSG(PWRMGMT_SUBTYPE_TPC, TPC_GET_ALARM, 0); ++ msg[1] = 0; ++ msg[2] = 0; ++ ++ rc = xgene_hwmon_pcc_rd(ctx, msg); ++ if (rc < 0) ++ return rc; ++ ++ amsg[0] = msg[0]; ++ amsg[1] = msg[1]; ++ amsg[2] = msg[2]; ++ ++ return rc; ++} ++ ++static int xgene_hwmon_get_cpu_pwr(struct xgene_hwmon_dev *ctx, u32 *val) ++{ ++ u32 watt, mwatt; ++ int rc; ++ ++ rc = xgene_hwmon_reg_map_rd(ctx, PMD_PWR_REG, &watt); ++ if (rc < 0) ++ return rc; ++ ++ rc = xgene_hwmon_reg_map_rd(ctx, PMD_PWR_MW_REG, &mwatt); ++ if (rc < 0) ++ return rc; ++ ++ *val = WATT_TO_mWATT(watt) + mwatt; ++ return 0; ++} ++ ++static int xgene_hwmon_get_io_pwr(struct xgene_hwmon_dev *ctx, u32 *val) ++{ ++ u32 watt, mwatt; ++ int rc; ++ ++ rc = xgene_hwmon_reg_map_rd(ctx, SOC_PWR_REG, &watt); ++ if (rc < 0) ++ return rc; ++ ++ rc = xgene_hwmon_reg_map_rd(ctx, SOC_PWR_MW_REG, &mwatt); ++ if (rc < 0) ++ return rc; ++ ++ *val = WATT_TO_mWATT(watt) + mwatt; ++ return 0; ++} ++ ++static int xgene_hwmon_get_temp(struct xgene_hwmon_dev *ctx, u32 *val) ++{ ++ return xgene_hwmon_reg_map_rd(ctx, SOC_TEMP_REG, val); ++} ++ ++/* ++ * Sensor temperature/power functions ++ */ ++static ssize_t temp1_input_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev); ++ int rc, temp; ++ u32 val; ++ ++ rc = xgene_hwmon_get_temp(ctx, &val); ++ if (rc < 0) ++ return rc; ++ ++ temp = sign_extend32(val, TEMP_NEGATIVE_BIT); ++ ++ return snprintf(buf, PAGE_SIZE, "%d\n", CELSIUS_TO_mCELSIUS(temp)); ++} ++ ++static ssize_t temp1_label_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ return snprintf(buf, PAGE_SIZE, "SoC Temperature\n"); ++} ++ ++static ssize_t temp1_critical_alarm_show(struct device *dev, ++ struct device_attribute *devattr, ++ char *buf) ++{ ++ struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev); ++ ++ return snprintf(buf, PAGE_SIZE, "%d\n", ctx->temp_critical_alarm); ++} ++ ++static ssize_t power1_label_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ return snprintf(buf, PAGE_SIZE, "CPU power\n"); ++} ++ ++static ssize_t power2_label_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ return snprintf(buf, PAGE_SIZE, "IO power\n"); ++} ++ ++static ssize_t power1_input_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev); ++ u32 val; ++ int rc; ++ ++ rc = xgene_hwmon_get_cpu_pwr(ctx, &val); ++ if (rc < 0) ++ return rc; ++ ++ return snprintf(buf, PAGE_SIZE, "%u\n", mWATT_TO_uWATT(val)); ++} ++ ++static ssize_t power2_input_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev); ++ u32 val; ++ int rc; ++ ++ rc = xgene_hwmon_get_io_pwr(ctx, &val); ++ if (rc < 0) ++ return rc; ++ ++ return snprintf(buf, PAGE_SIZE, "%u\n", mWATT_TO_uWATT(val)); ++} ++ ++static DEVICE_ATTR_RO(temp1_label); ++static DEVICE_ATTR_RO(temp1_input); ++static DEVICE_ATTR_RO(temp1_critical_alarm); ++static DEVICE_ATTR_RO(power1_label); ++static DEVICE_ATTR_RO(power1_input); ++static DEVICE_ATTR_RO(power2_label); ++static DEVICE_ATTR_RO(power2_input); ++ ++static struct attribute *xgene_hwmon_attrs[] = { ++ &dev_attr_temp1_label.attr, ++ &dev_attr_temp1_input.attr, ++ &dev_attr_temp1_critical_alarm.attr, ++ &dev_attr_power1_label.attr, ++ &dev_attr_power1_input.attr, ++ &dev_attr_power2_label.attr, ++ &dev_attr_power2_input.attr, ++ NULL, ++}; ++ ++ATTRIBUTE_GROUPS(xgene_hwmon); ++ ++static int xgene_hwmon_tpc_alarm(struct xgene_hwmon_dev *ctx, ++ struct slimpro_resp_msg *amsg) ++{ ++ ctx->temp_critical_alarm = !!amsg->param2; ++ sysfs_notify(&ctx->dev->kobj, NULL, "temp1_critical_alarm"); ++ ++ return 0; ++} ++ ++static void xgene_hwmon_process_pwrmsg(struct xgene_hwmon_dev *ctx, ++ struct slimpro_resp_msg *amsg) ++{ ++ if ((MSG_SUBTYPE(amsg->msg) == PWRMGMT_SUBTYPE_TPC) && ++ (TPC_CMD(amsg->msg) == TPC_ALARM)) ++ xgene_hwmon_tpc_alarm(ctx, amsg); ++} ++ ++/* ++ * This function is called to process async work queue ++ */ ++static void xgene_hwmon_evt_work(struct work_struct *work) ++{ ++ struct slimpro_resp_msg amsg; ++ struct xgene_hwmon_dev *ctx; ++ int ret; ++ ++ ctx = container_of(work, struct xgene_hwmon_dev, workq); ++ while (kfifo_out_spinlocked(&ctx->async_msg_fifo, &amsg, ++ sizeof(struct slimpro_resp_msg), ++ &ctx->kfifo_lock)) { ++ /* ++ * If PCC, send a consumer command to Platform to get info ++ * If Slimpro Mailbox, get message from specific FIFO ++ */ ++ if (!acpi_disabled) { ++ ret = xgene_hwmon_get_notification_msg(ctx, ++ (u32 *)&amsg); ++ if (ret < 0) ++ continue; ++ } ++ ++ if (MSG_TYPE(amsg.msg) == MSG_TYPE_PWRMGMT) ++ xgene_hwmon_process_pwrmsg(ctx, &amsg); ++ } ++} ++ ++/* ++ * This function is called when the SLIMpro Mailbox received a message ++ */ ++static void xgene_hwmon_rx_cb(struct mbox_client *cl, void *msg) ++{ ++ struct xgene_hwmon_dev *ctx = to_xgene_hwmon_dev(cl); ++ struct slimpro_resp_msg amsg; ++ ++ /* ++ * Response message format: ++ * msg[0] is the return code of the operation ++ * msg[1] is the first parameter word ++ * msg[2] is the second parameter word ++ * ++ * As message only supports dword size, just assign it. ++ */ ++ ++ /* Check for sync query */ ++ if (ctx->resp_pending && ++ ((MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_ERR) || ++ (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_DBG && ++ MSG_SUBTYPE(((u32 *)msg)[0]) == DBG_SUBTYPE_SENSOR_READ) || ++ (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_PWRMGMT && ++ MSG_SUBTYPE(((u32 *)msg)[0]) == PWRMGMT_SUBTYPE_TPC && ++ TPC_CMD(((u32 *)msg)[0]) == TPC_ALARM))) { ++ ctx->sync_msg.msg = ((u32 *)msg)[0]; ++ ctx->sync_msg.param1 = ((u32 *)msg)[1]; ++ ctx->sync_msg.param2 = ((u32 *)msg)[2]; ++ ++ /* Operation waiting for response */ ++ complete(&ctx->rd_complete); ++ ++ return; ++ } ++ ++ amsg.msg = ((u32 *)msg)[0]; ++ amsg.param1 = ((u32 *)msg)[1]; ++ amsg.param2 = ((u32 *)msg)[2]; ++ ++ /* Enqueue to the FIFO */ ++ kfifo_in_spinlocked(&ctx->async_msg_fifo, &amsg, ++ sizeof(struct slimpro_resp_msg), &ctx->kfifo_lock); ++ /* Schedule the bottom handler */ ++ schedule_work(&ctx->workq); ++} ++ ++/* ++ * This function is called when the PCC Mailbox received a message ++ */ ++static void xgene_hwmon_pcc_rx_cb(struct mbox_client *cl, void *msg) ++{ ++ struct xgene_hwmon_dev *ctx = to_xgene_hwmon_dev(cl); ++ struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr; ++ struct slimpro_resp_msg amsg; ++ ++ msg = generic_comm_base + 1; ++ /* Check if platform sends interrupt */ ++ if (!xgene_word_tst_and_clr(&generic_comm_base->status, ++ PCCS_SCI_DOORBEL)) ++ return; ++ ++ /* ++ * Response message format: ++ * msg[0] is the return code of the operation ++ * msg[1] is the first parameter word ++ * msg[2] is the second parameter word ++ * ++ * As message only supports dword size, just assign it. ++ */ ++ ++ /* Check for sync query */ ++ if (ctx->resp_pending && ++ ((MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_ERR) || ++ (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_DBG && ++ MSG_SUBTYPE(((u32 *)msg)[0]) == DBG_SUBTYPE_SENSOR_READ) || ++ (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_PWRMGMT && ++ MSG_SUBTYPE(((u32 *)msg)[0]) == PWRMGMT_SUBTYPE_TPC && ++ TPC_CMD(((u32 *)msg)[0]) == TPC_ALARM))) { ++ /* Check if platform completes command */ ++ if (xgene_word_tst_and_clr(&generic_comm_base->status, ++ PCCS_CMD_COMPLETE)) { ++ ctx->sync_msg.msg = ((u32 *)msg)[0]; ++ ctx->sync_msg.param1 = ((u32 *)msg)[1]; ++ ctx->sync_msg.param2 = ((u32 *)msg)[2]; ++ ++ /* Operation waiting for response */ ++ complete(&ctx->rd_complete); ++ ++ return; ++ } ++ } ++ ++ /* ++ * Platform notifies interrupt to OSPM. ++ * OPSM schedules a consumer command to get this information ++ * in a workqueue. Platform must wait until OSPM has issued ++ * a consumer command that serves this notification. ++ */ ++ ++ /* Enqueue to the FIFO */ ++ kfifo_in_spinlocked(&ctx->async_msg_fifo, &amsg, ++ sizeof(struct slimpro_resp_msg), &ctx->kfifo_lock); ++ /* Schedule the bottom handler */ ++ schedule_work(&ctx->workq); ++} ++ ++static void xgene_hwmon_tx_done(struct mbox_client *cl, void *msg, int ret) ++{ ++ if (ret) { ++ dev_dbg(cl->dev, "TX did not complete: CMD sent:%x, ret:%d\n", ++ *(u16 *)msg, ret); ++ } else { ++ dev_dbg(cl->dev, "TX completed. CMD sent:%x, ret:%d\n", ++ *(u16 *)msg, ret); ++ } ++} ++ ++static int xgene_hwmon_probe(struct platform_device *pdev) ++{ ++ struct xgene_hwmon_dev *ctx; ++ struct mbox_client *cl; ++ int rc; ++ ++ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); ++ if (!ctx) ++ return -ENOMEM; ++ ++ ctx->dev = &pdev->dev; ++ platform_set_drvdata(pdev, ctx); ++ cl = &ctx->mbox_client; ++ ++ /* Request mailbox channel */ ++ cl->dev = &pdev->dev; ++ cl->tx_done = xgene_hwmon_tx_done; ++ cl->tx_block = false; ++ cl->tx_tout = MBOX_OP_TIMEOUTMS; ++ cl->knows_txdone = false; ++ if (acpi_disabled) { ++ cl->rx_callback = xgene_hwmon_rx_cb; ++ ctx->mbox_chan = mbox_request_channel(cl, 0); ++ if (IS_ERR(ctx->mbox_chan)) { ++ dev_err(&pdev->dev, ++ "SLIMpro mailbox channel request failed\n"); ++ return -ENODEV; ++ } ++ } else { ++ struct acpi_pcct_hw_reduced *cppc_ss; ++ ++ if (device_property_read_u32(&pdev->dev, "pcc-channel", ++ &ctx->mbox_idx)) { ++ dev_err(&pdev->dev, "no pcc-channel property\n"); ++ return -ENODEV; ++ } ++ ++ cl->rx_callback = xgene_hwmon_pcc_rx_cb; ++ ctx->mbox_chan = pcc_mbox_request_channel(cl, ctx->mbox_idx); ++ if (IS_ERR(ctx->mbox_chan)) { ++ dev_err(&pdev->dev, ++ "PPC channel request failed\n"); ++ return -ENODEV; ++ } ++ ++ /* ++ * The PCC mailbox controller driver should ++ * have parsed the PCCT (global table of all ++ * PCC channels) and stored pointers to the ++ * subspace communication region in con_priv. ++ */ ++ cppc_ss = ctx->mbox_chan->con_priv; ++ if (!cppc_ss) { ++ dev_err(&pdev->dev, "PPC subspace not found\n"); ++ rc = -ENODEV; ++ goto out_mbox_free; ++ } ++ ++ if (!ctx->mbox_chan->mbox->txdone_irq) { ++ dev_err(&pdev->dev, "PCC IRQ not supported\n"); ++ rc = -ENODEV; ++ goto out_mbox_free; ++ } ++ ++ /* ++ * This is the shared communication region ++ * for the OS and Platform to communicate over. ++ */ ++ ctx->comm_base_addr = cppc_ss->base_address; ++ if (ctx->comm_base_addr) { ++ ctx->pcc_comm_addr = ++ acpi_os_ioremap(ctx->comm_base_addr, ++ cppc_ss->length); ++ } else { ++ dev_err(&pdev->dev, "Failed to get PCC comm region\n"); ++ rc = -ENODEV; ++ goto out_mbox_free; ++ } ++ ++ if (!ctx->pcc_comm_addr) { ++ dev_err(&pdev->dev, ++ "Failed to ioremap PCC comm region\n"); ++ rc = -ENOMEM; ++ goto out_mbox_free; ++ } ++ ++ /* ++ * cppc_ss->latency is just a Nominal value. In reality ++ * the remote processor could be much slower to reply. ++ * So add an arbitrary amount of wait on top of Nominal. ++ */ ++ ctx->usecs_lat = PCC_NUM_RETRIES * cppc_ss->latency; ++ } ++ ++ spin_lock_init(&ctx->kfifo_lock); ++ mutex_init(&ctx->rd_mutex); ++ ++ rc = kfifo_alloc(&ctx->async_msg_fifo, ++ sizeof(struct slimpro_resp_msg) * ASYNC_MSG_FIFO_SIZE, ++ GFP_KERNEL); ++ if (rc) ++ goto out_mbox_free; ++ ++ INIT_WORK(&ctx->workq, xgene_hwmon_evt_work); ++ ++ ctx->hwmon_dev = hwmon_device_register_with_groups(ctx->dev, ++ "apm_xgene", ++ ctx, ++ xgene_hwmon_groups); ++ if (IS_ERR(ctx->hwmon_dev)) { ++ dev_err(&pdev->dev, "Failed to register HW monitor device\n"); ++ rc = PTR_ERR(ctx->hwmon_dev); ++ goto out; ++ } ++ ++ dev_info(&pdev->dev, "APM X-Gene SoC HW monitor driver registered\n"); ++ ++ return 0; ++ ++out: ++ kfifo_free(&ctx->async_msg_fifo); ++out_mbox_free: ++ if (acpi_disabled) ++ mbox_free_channel(ctx->mbox_chan); ++ else ++ pcc_mbox_free_channel(ctx->mbox_chan); ++ ++ return rc; ++} ++ ++static int xgene_hwmon_remove(struct platform_device *pdev) ++{ ++ struct xgene_hwmon_dev *ctx = platform_get_drvdata(pdev); ++ ++ hwmon_device_unregister(ctx->hwmon_dev); ++ kfifo_free(&ctx->async_msg_fifo); ++ if (acpi_disabled) ++ mbox_free_channel(ctx->mbox_chan); ++ else ++ pcc_mbox_free_channel(ctx->mbox_chan); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_ACPI ++static const struct acpi_device_id xgene_hwmon_acpi_match[] = { ++ {"APMC0D29", 0}, ++ {}, ++}; ++MODULE_DEVICE_TABLE(acpi, xgene_hwmon_acpi_match); ++#endif ++ ++static const struct of_device_id xgene_hwmon_of_match[] = { ++ {.compatible = "apm,xgene-slimpro-hwmon"}, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, xgene_hwmon_of_match); ++ ++static struct platform_driver xgene_hwmon_driver __refdata = { ++ .probe = xgene_hwmon_probe, ++ .remove = xgene_hwmon_remove, ++ .driver = { ++ .name = "xgene-slimpro-hwmon", ++ .of_match_table = xgene_hwmon_of_match, ++ .acpi_match_table = ACPI_PTR(xgene_hwmon_acpi_match), ++ }, ++}; ++module_platform_driver(xgene_hwmon_driver); ++ ++MODULE_DESCRIPTION("APM X-Gene SoC hardware monitor"); ++MODULE_LICENSE("GPL"); +-- +1.8.3.1 + diff --git a/SOURCES/1006-hwmon-xgene-Fix-crash-when-alarm-occurs-before-drive.patch b/SOURCES/1006-hwmon-xgene-Fix-crash-when-alarm-occurs-before-drive.patch new file mode 100644 index 0000000..b009667 --- /dev/null +++ b/SOURCES/1006-hwmon-xgene-Fix-crash-when-alarm-occurs-before-drive.patch @@ -0,0 +1,158 @@ +From 5b839513ff79400d9f52c2f5969a428a519d7abd Mon Sep 17 00:00:00 2001 +From: hotran hotran@apm.com +Date: Thu, 8 Sep 2016 09:33:10 -0700 +Subject: [PATCH 1006/1018] hwmon: (xgene) Fix crash when alarm occurs before + driver probe + +The system crashes during probing xgene-hwmon driver when temperature +alarm interrupt occurs before. +It's because + - xgene_hwmon_probe() requests mailbox channel which also enables + the mailbox interrupt. + - As temperature alarm interrupt is pending, ISR runs and crashes when + accesses into invalid resourse as unmapped PCC shared memory. + +This patch fixes this issue by saving this alarm message and scheduling a +bottom handler after xgene_hwmon_probe() finish. + +Signed-off-by: Hoan Tran hotran@apm.com +Reported-by: Itaru Kitayama itaru.kitayama@riken.jp +Signed-off-by: Guenter Roeck linux@roeck-us.net +--- + drivers/hwmon/xgene-hwmon.c | 69 ++++++++++++++++++++++++++++++++------------- + 1 file changed, 50 insertions(+), 19 deletions(-) + +diff --git a/drivers/hwmon/xgene-hwmon.c b/drivers/hwmon/xgene-hwmon.c +index bc78a5d..aa44579 100644 +--- a/drivers/hwmon/xgene-hwmon.c ++++ b/drivers/hwmon/xgene-hwmon.c +@@ -465,13 +465,34 @@ static void xgene_hwmon_evt_work(struct work_struct *work) + } + } + ++static int xgene_hwmon_rx_ready(struct xgene_hwmon_dev *ctx, void *msg) ++{ ++ if (IS_ERR_OR_NULL(ctx->hwmon_dev) && !ctx->resp_pending) { ++ /* Enqueue to the FIFO */ ++ kfifo_in_spinlocked(&ctx->async_msg_fifo, msg, ++ sizeof(struct slimpro_resp_msg), ++ &ctx->kfifo_lock); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ + /* + * This function is called when the SLIMpro Mailbox received a message + */ + static void xgene_hwmon_rx_cb(struct mbox_client *cl, void *msg) + { + struct xgene_hwmon_dev *ctx = to_xgene_hwmon_dev(cl); +- struct slimpro_resp_msg amsg; ++ ++ /* ++ * While the driver registers with the mailbox framework, an interrupt ++ * can be pending before the probe function completes its ++ * initialization. If such condition occurs, just queue up the message ++ * as the driver is not ready for servicing the callback. ++ */ ++ if (xgene_hwmon_rx_ready(ctx, msg) < 0) ++ return; + + /* + * Response message format: +@@ -500,12 +521,8 @@ static void xgene_hwmon_rx_cb(struct mbox_client *cl, void *msg) + return; + } + +- amsg.msg = ((u32 *)msg)[0]; +- amsg.param1 = ((u32 *)msg)[1]; +- amsg.param2 = ((u32 *)msg)[2]; +- + /* Enqueue to the FIFO */ +- kfifo_in_spinlocked(&ctx->async_msg_fifo, &amsg, ++ kfifo_in_spinlocked(&ctx->async_msg_fifo, msg, + sizeof(struct slimpro_resp_msg), &ctx->kfifo_lock); + /* Schedule the bottom handler */ + schedule_work(&ctx->workq); +@@ -520,6 +537,15 @@ static void xgene_hwmon_pcc_rx_cb(struct mbox_client *cl, void *msg) + struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr; + struct slimpro_resp_msg amsg; + ++ /* ++ * While the driver registers with the mailbox framework, an interrupt ++ * can be pending before the probe function completes its ++ * initialization. If such condition occurs, just queue up the message ++ * as the driver is not ready for servicing the callback. ++ */ ++ if (xgene_hwmon_rx_ready(ctx, &amsg) < 0) ++ return; ++ + msg = generic_comm_base + 1; + /* Check if platform sends interrupt */ + if (!xgene_word_tst_and_clr(&generic_comm_base->status, +@@ -596,6 +622,17 @@ static int xgene_hwmon_probe(struct platform_device *pdev) + platform_set_drvdata(pdev, ctx); + cl = &ctx->mbox_client; + ++ spin_lock_init(&ctx->kfifo_lock); ++ mutex_init(&ctx->rd_mutex); ++ ++ rc = kfifo_alloc(&ctx->async_msg_fifo, ++ sizeof(struct slimpro_resp_msg) * ASYNC_MSG_FIFO_SIZE, ++ GFP_KERNEL); ++ if (rc) ++ goto out_mbox_free; ++ ++ INIT_WORK(&ctx->workq, xgene_hwmon_evt_work); ++ + /* Request mailbox channel */ + cl->dev = &pdev->dev; + cl->tx_done = xgene_hwmon_tx_done; +@@ -676,17 +713,6 @@ static int xgene_hwmon_probe(struct platform_device *pdev) + ctx->usecs_lat = PCC_NUM_RETRIES * cppc_ss->latency; + } + +- spin_lock_init(&ctx->kfifo_lock); +- mutex_init(&ctx->rd_mutex); +- +- rc = kfifo_alloc(&ctx->async_msg_fifo, +- sizeof(struct slimpro_resp_msg) * ASYNC_MSG_FIFO_SIZE, +- GFP_KERNEL); +- if (rc) +- goto out_mbox_free; +- +- INIT_WORK(&ctx->workq, xgene_hwmon_evt_work); +- + ctx->hwmon_dev = hwmon_device_register_with_groups(ctx->dev, + "apm_xgene", + ctx, +@@ -697,17 +723,22 @@ static int xgene_hwmon_probe(struct platform_device *pdev) + goto out; + } + ++ /* ++ * Schedule the bottom handler if there is a pending message. ++ */ ++ schedule_work(&ctx->workq); ++ + dev_info(&pdev->dev, "APM X-Gene SoC HW monitor driver registered\n"); + + return 0; + + out: +- kfifo_free(&ctx->async_msg_fifo); +-out_mbox_free: + if (acpi_disabled) + mbox_free_channel(ctx->mbox_chan); + else + pcc_mbox_free_channel(ctx->mbox_chan); ++out_mbox_free: ++ kfifo_free(&ctx->async_msg_fifo); + + return rc; + } +-- +1.8.3.1 + diff --git a/SOURCES/config-centos-sig b/SOURCES/config-centos-sig index f8fab63..9d17fb7 100644 --- a/SOURCES/config-centos-sig +++ b/SOURCES/config-centos-sig @@ -4,3 +4,4 @@ CONFIG_QCOM_FALKOR_ERRATUM_E1003=y CONFIG_QCOM_FALKOR_ERRATUM_E1009=y CONFIG_ACPI_CPPC_CPUFREQ=y +CONFIG_SENSORS_XGENE=y diff --git a/SPECS/kernel-aarch64.spec b/SPECS/kernel-aarch64.spec index 1a5c885..2dcb885 100644 --- a/SPECS/kernel-aarch64.spec +++ b/SPECS/kernel-aarch64.spec @@ -328,6 +328,9 @@ Source60: config-centos-sig Patch1001: 1001-cpufreq-CPPC-Avoid-overflow-when-calculating-desired.patch Patch1002: 1002-cpufreq-CPPC-Correct-desired_perf-calculation.patch Patch1003: 1003-mailbox-PCC-Fix-return-value-of-pcc_mbox_request_cha.patch +Patch1004: 1004-Documentation-dtb-xgene-Add-hwmon-dts-binding-docume.patch +Patch1005: 1005-hwmon-Add-xgene-hwmon-driver.patch +Patch1006: 1006-hwmon-xgene-Fix-crash-when-alarm-occurs-before-drive.patch
# QDF2400 Patches Patch4000: 4000-arm64-Define-Qualcomm-Technologies-ARMv8-CPU.patch @@ -666,6 +669,9 @@ fi git am %{PATCH1001} git am %{PATCH1002} git am %{PATCH1003} +git am %{PATCH1004} +git am %{PATCH1005} +git am %{PATCH1006}
# Apply QDF2400 patches git am %{PATCH4000} @@ -1445,6 +1451,7 @@ fi
%changelog * Thu Nov 10 2016 Duc Dang dhdang@apm.com [4.5.0-17.el7] +- Add X-Gene HWMon support - mailbox: PCC: Fix return value of pcc_mbox_request_channel - Add cpufreq CPPC bug fixes
Integrate upstream support for APM X-Gene SoC PMU and bug fixes.
Signed-off-by: Duc Dang dhdang@apm.com --- ...Add-APM-X-Gene-SoC-Performance-Monitoring.patch | 1508 ++++++++++++++++++++ ...1008-perf-xgene-Remove-bogus-IS_ERR-check.patch | 39 + ...S-Add-entry-for-APM-X-Gene-SoC-PMU-driver.patch | 34 + SOURCES/config-centos-sig | 1 + SPECS/kernel-aarch64.spec | 7 + 5 files changed, 1589 insertions(+) create mode 100644 SOURCES/1007-perf-xgene-Add-APM-X-Gene-SoC-Performance-Monitoring.patch create mode 100644 SOURCES/1008-perf-xgene-Remove-bogus-IS_ERR-check.patch create mode 100644 SOURCES/1009-MAINTAINERS-Add-entry-for-APM-X-Gene-SoC-PMU-driver.patch
diff --git a/SOURCES/1007-perf-xgene-Add-APM-X-Gene-SoC-Performance-Monitoring.patch b/SOURCES/1007-perf-xgene-Add-APM-X-Gene-SoC-Performance-Monitoring.patch new file mode 100644 index 0000000..3e35a53 --- /dev/null +++ b/SOURCES/1007-perf-xgene-Add-APM-X-Gene-SoC-Performance-Monitoring.patch @@ -0,0 +1,1508 @@ +From c807faa271d0983fa8f8e0dc327b111f01ffa012 Mon Sep 17 00:00:00 2001 +From: Tai Nguyen ttnguyen@apm.com +Date: Wed, 9 Nov 2016 17:31:06 -0600 +Subject: [PATCH 1007/1018] perf: xgene: Add APM X-Gene SoC Performance + Monitoring Unit driver + +This patch adds a driver for the SoC-wide (AKA uncore) PMU hardware +found in APM X-Gene SoCs. + +Signed-off-by: Tai Nguyen ttnguyen@apm.com +Reviewed-by: Mark Rutland mark.rutland@arm.com + +[Apply from upstream commit 4.8 to CentOS 7.3 AltArch] +Signed-off-by: Duc Dang dhdang@apm.com +--- + Documentation/perf/xgene-pmu.txt | 48 ++ + drivers/perf/Kconfig | 7 + + drivers/perf/Makefile | 1 + + drivers/perf/xgene_pmu.c | 1398 ++++++++++++++++++++++++++++++++++++++ + 4 files changed, 1454 insertions(+) + create mode 100644 Documentation/perf/xgene-pmu.txt + create mode 100644 drivers/perf/xgene_pmu.c + +diff --git a/Documentation/perf/xgene-pmu.txt b/Documentation/perf/xgene-pmu.txt +new file mode 100644 +index 0000000..d7cff44 +--- /dev/null ++++ b/Documentation/perf/xgene-pmu.txt +@@ -0,0 +1,48 @@ ++APM X-Gene SoC Performance Monitoring Unit (PMU) ++================================================ ++ ++X-Gene SoC PMU consists of various independent system device PMUs such as ++L3 cache(s), I/O bridge(s), memory controller bridge(s) and memory ++controller(s). These PMU devices are loosely architected to follow the ++same model as the PMU for ARM cores. The PMUs share the same top level ++interrupt and status CSR region. ++ ++PMU (perf) driver ++----------------- ++ ++The xgene-pmu driver registers several perf PMU drivers. Each of the perf ++driver provides description of its available events and configuration options ++in sysfs, see /sys/devices/<l3cX/iobX/mcbX/mcX>/. ++ ++The "format" directory describes format of the config (event ID), ++config1 (agent ID) fields of the perf_event_attr structure. The "events" ++directory provides configuration templates for all supported event types that ++can be used with perf tool. For example, "l3c0/bank-fifo-full/" is an ++equivalent of "l3c0/config=0x0b/". ++ ++Most of the SoC PMU has a specific list of agent ID used for monitoring ++performance of a specific datapath. For example, agents of a L3 cache can be ++a specific CPU or an I/O bridge. Each PMU has a set of 2 registers capable of ++masking the agents from which the request come from. If the bit with ++the bit number corresponding to the agent is set, the event is counted only if ++it is caused by a request from that agent. Each agent ID bit is inversely mapped ++to a corresponding bit in "config1" field. By default, the event will be ++counted for all agent requests (config1 = 0x0). For all the supported agents of ++each PMU, please refer to APM X-Gene User Manual. ++ ++Each perf driver also provides a "cpumask" sysfs attribute, which contains a ++single CPU ID of the processor which will be used to handle all the PMU events. ++ ++Example for perf tool use: ++ ++ / # perf list | grep -e l3c -e iob -e mcb -e mc ++ l3c0/ackq-full/ [Kernel PMU event] ++ <...> ++ mcb1/mcb-csw-stall/ [Kernel PMU event] ++ ++ / # perf stat -a -e l3c0/read-miss/,mcb1/csw-write-request/ sleep 1 ++ ++ / # perf stat -a -e l3c0/read-miss,config1=0xfffffffffffffffe/ sleep 1 ++ ++The driver does not support sampling, therefore "perf record" will ++not work. Per-task (without "-a") perf sessions are not supported. +diff --git a/drivers/perf/Kconfig b/drivers/perf/Kconfig +index 818fa3b..a2f63f7 100644 +--- a/drivers/perf/Kconfig ++++ b/drivers/perf/Kconfig +@@ -16,4 +16,11 @@ config ARM_PMU_ACPI + def_bool y + depends on ARM_PMU && ACPI + ++config XGENE_PMU ++ depends on PERF_EVENTS && ARCH_XGENE ++ bool "APM X-Gene SoC PMU" ++ default n ++ help ++ Say y if you want to use APM X-Gene SoC performance monitors. ++ + endmenu +diff --git a/drivers/perf/Makefile b/drivers/perf/Makefile +index fd8090d..d1d7762 100644 +--- a/drivers/perf/Makefile ++++ b/drivers/perf/Makefile +@@ -1,2 +1,3 @@ + obj-$(CONFIG_ARM_PMU) += arm_pmu.o ++obj-$(CONFIG_XGENE_PMU) += xgene_pmu.o + obj-$(CONFIG_ARM_PMU_ACPI) += arm_pmu_acpi.o +diff --git a/drivers/perf/xgene_pmu.c b/drivers/perf/xgene_pmu.c +new file mode 100644 +index 0000000..c2ac764 +--- /dev/null ++++ b/drivers/perf/xgene_pmu.c +@@ -0,0 +1,1398 @@ ++/* ++ * APM X-Gene SoC PMU (Performance Monitor Unit) ++ * ++ * Copyright (c) 2016, Applied Micro Circuits Corporation ++ * Author: Hoan Tran hotran@apm.com ++ * Tai Nguyen ttnguyen@apm.com ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see http://www.gnu.org/licenses/. ++ */ ++ ++#include <linux/acpi.h> ++#include <linux/clk.h> ++#include <linux/cpumask.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/mfd/syscon.h> ++#include <linux/of_address.h> ++#include <linux/of_fdt.h> ++#include <linux/of_irq.h> ++#include <linux/of_platform.h> ++#include <linux/perf_event.h> ++#include <linux/platform_device.h> ++#include <linux/regmap.h> ++#include <linux/slab.h> ++ ++#define CSW_CSWCR 0x0000 ++#define CSW_CSWCR_DUALMCB_MASK BIT(0) ++#define MCBADDRMR 0x0000 ++#define MCBADDRMR_DUALMCU_MODE_MASK BIT(2) ++ ++#define PCPPMU_INTSTATUS_REG 0x000 ++#define PCPPMU_INTMASK_REG 0x004 ++#define PCPPMU_INTMASK 0x0000000F ++#define PCPPMU_INTENMASK 0xFFFFFFFF ++#define PCPPMU_INTCLRMASK 0xFFFFFFF0 ++#define PCPPMU_INT_MCU BIT(0) ++#define PCPPMU_INT_MCB BIT(1) ++#define PCPPMU_INT_L3C BIT(2) ++#define PCPPMU_INT_IOB BIT(3) ++ ++#define PMU_MAX_COUNTERS 4 ++#define PMU_CNT_MAX_PERIOD 0x100000000ULL ++#define PMU_OVERFLOW_MASK 0xF ++#define PMU_PMCR_E BIT(0) ++#define PMU_PMCR_P BIT(1) ++ ++#define PMU_PMEVCNTR0 0x000 ++#define PMU_PMEVCNTR1 0x004 ++#define PMU_PMEVCNTR2 0x008 ++#define PMU_PMEVCNTR3 0x00C ++#define PMU_PMEVTYPER0 0x400 ++#define PMU_PMEVTYPER1 0x404 ++#define PMU_PMEVTYPER2 0x408 ++#define PMU_PMEVTYPER3 0x40C ++#define PMU_PMAMR0 0xA00 ++#define PMU_PMAMR1 0xA04 ++#define PMU_PMCNTENSET 0xC00 ++#define PMU_PMCNTENCLR 0xC20 ++#define PMU_PMINTENSET 0xC40 ++#define PMU_PMINTENCLR 0xC60 ++#define PMU_PMOVSR 0xC80 ++#define PMU_PMCR 0xE04 ++ ++#define to_pmu_dev(p) container_of(p, struct xgene_pmu_dev, pmu) ++#define GET_CNTR(ev) (ev->hw.idx) ++#define GET_EVENTID(ev) (ev->hw.config & 0xFFULL) ++#define GET_AGENTID(ev) (ev->hw.config_base & 0xFFFFFFFFUL) ++#define GET_AGENT1ID(ev) ((ev->hw.config_base >> 32) & 0xFFFFFFFFUL) ++ ++struct hw_pmu_info { ++ u32 type; ++ u32 enable_mask; ++ void __iomem *csr; ++}; ++ ++struct xgene_pmu_dev { ++ struct hw_pmu_info *inf; ++ struct xgene_pmu *parent; ++ struct pmu pmu; ++ u8 max_counters; ++ DECLARE_BITMAP(cntr_assign_mask, PMU_MAX_COUNTERS); ++ u64 max_period; ++ const struct attribute_group **attr_groups; ++ struct perf_event *pmu_counter_event[PMU_MAX_COUNTERS]; ++}; ++ ++struct xgene_pmu { ++ struct device *dev; ++ int version; ++ void __iomem *pcppmu_csr; ++ u32 mcb_active_mask; ++ u32 mc_active_mask; ++ cpumask_t cpu; ++ raw_spinlock_t lock; ++ struct list_head l3cpmus; ++ struct list_head iobpmus; ++ struct list_head mcbpmus; ++ struct list_head mcpmus; ++}; ++ ++struct xgene_pmu_dev_ctx { ++ char *name; ++ struct list_head next; ++ struct xgene_pmu_dev *pmu_dev; ++ struct hw_pmu_info inf; ++}; ++ ++struct xgene_pmu_data { ++ int id; ++ u32 data; ++}; ++ ++enum xgene_pmu_version { ++ PCP_PMU_V1 = 1, ++ PCP_PMU_V2, ++}; ++ ++enum xgene_pmu_dev_type { ++ PMU_TYPE_L3C = 0, ++ PMU_TYPE_IOB, ++ PMU_TYPE_MCB, ++ PMU_TYPE_MC, ++}; ++ ++/* ++ * sysfs format attributes ++ */ ++static ssize_t xgene_pmu_format_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct dev_ext_attribute *eattr; ++ ++ eattr = container_of(attr, struct dev_ext_attribute, attr); ++ return sprintf(buf, "%s\n", (char *) eattr->var); ++} ++ ++#define XGENE_PMU_FORMAT_ATTR(_name, _config) \ ++ (&((struct dev_ext_attribute[]) { \ ++ { .attr = __ATTR(_name, S_IRUGO, xgene_pmu_format_show, NULL), \ ++ .var = (void *) _config, } \ ++ })[0].attr.attr) ++ ++static struct attribute *l3c_pmu_format_attrs[] = { ++ XGENE_PMU_FORMAT_ATTR(l3c_eventid, "config:0-7"), ++ XGENE_PMU_FORMAT_ATTR(l3c_agentid, "config1:0-9"), ++ NULL, ++}; ++ ++static struct attribute *iob_pmu_format_attrs[] = { ++ XGENE_PMU_FORMAT_ATTR(iob_eventid, "config:0-7"), ++ XGENE_PMU_FORMAT_ATTR(iob_agentid, "config1:0-63"), ++ NULL, ++}; ++ ++static struct attribute *mcb_pmu_format_attrs[] = { ++ XGENE_PMU_FORMAT_ATTR(mcb_eventid, "config:0-5"), ++ XGENE_PMU_FORMAT_ATTR(mcb_agentid, "config1:0-9"), ++ NULL, ++}; ++ ++static struct attribute *mc_pmu_format_attrs[] = { ++ XGENE_PMU_FORMAT_ATTR(mc_eventid, "config:0-28"), ++ NULL, ++}; ++ ++static const struct attribute_group l3c_pmu_format_attr_group = { ++ .name = "format", ++ .attrs = l3c_pmu_format_attrs, ++}; ++ ++static const struct attribute_group iob_pmu_format_attr_group = { ++ .name = "format", ++ .attrs = iob_pmu_format_attrs, ++}; ++ ++static const struct attribute_group mcb_pmu_format_attr_group = { ++ .name = "format", ++ .attrs = mcb_pmu_format_attrs, ++}; ++ ++static const struct attribute_group mc_pmu_format_attr_group = { ++ .name = "format", ++ .attrs = mc_pmu_format_attrs, ++}; ++ ++/* ++ * sysfs event attributes ++ */ ++static ssize_t xgene_pmu_event_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct dev_ext_attribute *eattr; ++ ++ eattr = container_of(attr, struct dev_ext_attribute, attr); ++ return sprintf(buf, "config=0x%lx\n", (unsigned long) eattr->var); ++} ++ ++#define XGENE_PMU_EVENT_ATTR(_name, _config) \ ++ (&((struct dev_ext_attribute[]) { \ ++ { .attr = __ATTR(_name, S_IRUGO, xgene_pmu_event_show, NULL), \ ++ .var = (void *) _config, } \ ++ })[0].attr.attr) ++ ++static struct attribute *l3c_pmu_events_attrs[] = { ++ XGENE_PMU_EVENT_ATTR(cycle-count, 0x00), ++ XGENE_PMU_EVENT_ATTR(cycle-count-div-64, 0x01), ++ XGENE_PMU_EVENT_ATTR(read-hit, 0x02), ++ XGENE_PMU_EVENT_ATTR(read-miss, 0x03), ++ XGENE_PMU_EVENT_ATTR(write-need-replacement, 0x06), ++ XGENE_PMU_EVENT_ATTR(write-not-need-replacement, 0x07), ++ XGENE_PMU_EVENT_ATTR(tq-full, 0x08), ++ XGENE_PMU_EVENT_ATTR(ackq-full, 0x09), ++ XGENE_PMU_EVENT_ATTR(wdb-full, 0x0a), ++ XGENE_PMU_EVENT_ATTR(bank-fifo-full, 0x0b), ++ XGENE_PMU_EVENT_ATTR(odb-full, 0x0c), ++ XGENE_PMU_EVENT_ATTR(wbq-full, 0x0d), ++ XGENE_PMU_EVENT_ATTR(bank-conflict-fifo-issue, 0x0e), ++ XGENE_PMU_EVENT_ATTR(bank-fifo-issue, 0x0f), ++ NULL, ++}; ++ ++static struct attribute *iob_pmu_events_attrs[] = { ++ XGENE_PMU_EVENT_ATTR(cycle-count, 0x00), ++ XGENE_PMU_EVENT_ATTR(cycle-count-div-64, 0x01), ++ XGENE_PMU_EVENT_ATTR(axi0-read, 0x02), ++ XGENE_PMU_EVENT_ATTR(axi0-read-partial, 0x03), ++ XGENE_PMU_EVENT_ATTR(axi1-read, 0x04), ++ XGENE_PMU_EVENT_ATTR(axi1-read-partial, 0x05), ++ XGENE_PMU_EVENT_ATTR(csw-read-block, 0x06), ++ XGENE_PMU_EVENT_ATTR(csw-read-partial, 0x07), ++ XGENE_PMU_EVENT_ATTR(axi0-write, 0x10), ++ XGENE_PMU_EVENT_ATTR(axi0-write-partial, 0x11), ++ XGENE_PMU_EVENT_ATTR(axi1-write, 0x13), ++ XGENE_PMU_EVENT_ATTR(axi1-write-partial, 0x14), ++ XGENE_PMU_EVENT_ATTR(csw-inbound-dirty, 0x16), ++ NULL, ++}; ++ ++static struct attribute *mcb_pmu_events_attrs[] = { ++ XGENE_PMU_EVENT_ATTR(cycle-count, 0x00), ++ XGENE_PMU_EVENT_ATTR(cycle-count-div-64, 0x01), ++ XGENE_PMU_EVENT_ATTR(csw-read, 0x02), ++ XGENE_PMU_EVENT_ATTR(csw-write-request, 0x03), ++ XGENE_PMU_EVENT_ATTR(mcb-csw-stall, 0x04), ++ XGENE_PMU_EVENT_ATTR(cancel-read-gack, 0x05), ++ NULL, ++}; ++ ++static struct attribute *mc_pmu_events_attrs[] = { ++ XGENE_PMU_EVENT_ATTR(cycle-count, 0x00), ++ XGENE_PMU_EVENT_ATTR(cycle-count-div-64, 0x01), ++ XGENE_PMU_EVENT_ATTR(act-cmd-sent, 0x02), ++ XGENE_PMU_EVENT_ATTR(pre-cmd-sent, 0x03), ++ XGENE_PMU_EVENT_ATTR(rd-cmd-sent, 0x04), ++ XGENE_PMU_EVENT_ATTR(rda-cmd-sent, 0x05), ++ XGENE_PMU_EVENT_ATTR(wr-cmd-sent, 0x06), ++ XGENE_PMU_EVENT_ATTR(wra-cmd-sent, 0x07), ++ XGENE_PMU_EVENT_ATTR(pde-cmd-sent, 0x08), ++ XGENE_PMU_EVENT_ATTR(sre-cmd-sent, 0x09), ++ XGENE_PMU_EVENT_ATTR(prea-cmd-sent, 0x0a), ++ XGENE_PMU_EVENT_ATTR(ref-cmd-sent, 0x0b), ++ XGENE_PMU_EVENT_ATTR(rd-rda-cmd-sent, 0x0c), ++ XGENE_PMU_EVENT_ATTR(wr-wra-cmd-sent, 0x0d), ++ XGENE_PMU_EVENT_ATTR(in-rd-collision, 0x0e), ++ XGENE_PMU_EVENT_ATTR(in-wr-collision, 0x0f), ++ XGENE_PMU_EVENT_ATTR(collision-queue-not-empty, 0x10), ++ XGENE_PMU_EVENT_ATTR(collision-queue-full, 0x11), ++ XGENE_PMU_EVENT_ATTR(mcu-request, 0x12), ++ XGENE_PMU_EVENT_ATTR(mcu-rd-request, 0x13), ++ XGENE_PMU_EVENT_ATTR(mcu-hp-rd-request, 0x14), ++ XGENE_PMU_EVENT_ATTR(mcu-wr-request, 0x15), ++ XGENE_PMU_EVENT_ATTR(mcu-rd-proceed-all, 0x16), ++ XGENE_PMU_EVENT_ATTR(mcu-rd-proceed-cancel, 0x17), ++ XGENE_PMU_EVENT_ATTR(mcu-rd-response, 0x18), ++ XGENE_PMU_EVENT_ATTR(mcu-rd-proceed-speculative-all, 0x19), ++ XGENE_PMU_EVENT_ATTR(mcu-rd-proceed-speculative-cancel, 0x1a), ++ XGENE_PMU_EVENT_ATTR(mcu-wr-proceed-all, 0x1b), ++ XGENE_PMU_EVENT_ATTR(mcu-wr-proceed-cancel, 0x1c), ++ NULL, ++}; ++ ++static const struct attribute_group l3c_pmu_events_attr_group = { ++ .name = "events", ++ .attrs = l3c_pmu_events_attrs, ++}; ++ ++static const struct attribute_group iob_pmu_events_attr_group = { ++ .name = "events", ++ .attrs = iob_pmu_events_attrs, ++}; ++ ++static const struct attribute_group mcb_pmu_events_attr_group = { ++ .name = "events", ++ .attrs = mcb_pmu_events_attrs, ++}; ++ ++static const struct attribute_group mc_pmu_events_attr_group = { ++ .name = "events", ++ .attrs = mc_pmu_events_attrs, ++}; ++ ++/* ++ * sysfs cpumask attributes ++ */ ++static ssize_t xgene_pmu_cpumask_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct xgene_pmu_dev *pmu_dev = to_pmu_dev(dev_get_drvdata(dev)); ++ ++ return cpumap_print_to_pagebuf(true, buf, &pmu_dev->parent->cpu); ++} ++ ++static DEVICE_ATTR(cpumask, S_IRUGO, xgene_pmu_cpumask_show, NULL); ++ ++static struct attribute *xgene_pmu_cpumask_attrs[] = { ++ &dev_attr_cpumask.attr, ++ NULL, ++}; ++ ++static const struct attribute_group pmu_cpumask_attr_group = { ++ .attrs = xgene_pmu_cpumask_attrs, ++}; ++ ++/* ++ * Per PMU device attribute groups ++ */ ++static const struct attribute_group *l3c_pmu_attr_groups[] = { ++ &l3c_pmu_format_attr_group, ++ &pmu_cpumask_attr_group, ++ &l3c_pmu_events_attr_group, ++ NULL ++}; ++ ++static const struct attribute_group *iob_pmu_attr_groups[] = { ++ &iob_pmu_format_attr_group, ++ &pmu_cpumask_attr_group, ++ &iob_pmu_events_attr_group, ++ NULL ++}; ++ ++static const struct attribute_group *mcb_pmu_attr_groups[] = { ++ &mcb_pmu_format_attr_group, ++ &pmu_cpumask_attr_group, ++ &mcb_pmu_events_attr_group, ++ NULL ++}; ++ ++static const struct attribute_group *mc_pmu_attr_groups[] = { ++ &mc_pmu_format_attr_group, ++ &pmu_cpumask_attr_group, ++ &mc_pmu_events_attr_group, ++ NULL ++}; ++ ++static int get_next_avail_cntr(struct xgene_pmu_dev *pmu_dev) ++{ ++ int cntr; ++ ++ cntr = find_first_zero_bit(pmu_dev->cntr_assign_mask, ++ pmu_dev->max_counters); ++ if (cntr == pmu_dev->max_counters) ++ return -ENOSPC; ++ set_bit(cntr, pmu_dev->cntr_assign_mask); ++ ++ return cntr; ++} ++ ++static void clear_avail_cntr(struct xgene_pmu_dev *pmu_dev, int cntr) ++{ ++ clear_bit(cntr, pmu_dev->cntr_assign_mask); ++} ++ ++static inline void xgene_pmu_mask_int(struct xgene_pmu *xgene_pmu) ++{ ++ writel(PCPPMU_INTENMASK, xgene_pmu->pcppmu_csr + PCPPMU_INTMASK_REG); ++} ++ ++static inline void xgene_pmu_unmask_int(struct xgene_pmu *xgene_pmu) ++{ ++ writel(PCPPMU_INTCLRMASK, xgene_pmu->pcppmu_csr + PCPPMU_INTMASK_REG); ++} ++ ++static inline u32 xgene_pmu_read_counter(struct xgene_pmu_dev *pmu_dev, int idx) ++{ ++ return readl(pmu_dev->inf->csr + PMU_PMEVCNTR0 + (4 * idx)); ++} ++ ++static inline void ++xgene_pmu_write_counter(struct xgene_pmu_dev *pmu_dev, int idx, u32 val) ++{ ++ writel(val, pmu_dev->inf->csr + PMU_PMEVCNTR0 + (4 * idx)); ++} ++ ++static inline void ++xgene_pmu_write_evttype(struct xgene_pmu_dev *pmu_dev, int idx, u32 val) ++{ ++ writel(val, pmu_dev->inf->csr + PMU_PMEVTYPER0 + (4 * idx)); ++} ++ ++static inline void ++xgene_pmu_write_agentmsk(struct xgene_pmu_dev *pmu_dev, u32 val) ++{ ++ writel(val, pmu_dev->inf->csr + PMU_PMAMR0); ++} ++ ++static inline void ++xgene_pmu_write_agent1msk(struct xgene_pmu_dev *pmu_dev, u32 val) ++{ ++ writel(val, pmu_dev->inf->csr + PMU_PMAMR1); ++} ++ ++static inline void ++xgene_pmu_enable_counter(struct xgene_pmu_dev *pmu_dev, int idx) ++{ ++ u32 val; ++ ++ val = readl(pmu_dev->inf->csr + PMU_PMCNTENSET); ++ val |= 1 << idx; ++ writel(val, pmu_dev->inf->csr + PMU_PMCNTENSET); ++} ++ ++static inline void ++xgene_pmu_disable_counter(struct xgene_pmu_dev *pmu_dev, int idx) ++{ ++ u32 val; ++ ++ val = readl(pmu_dev->inf->csr + PMU_PMCNTENCLR); ++ val |= 1 << idx; ++ writel(val, pmu_dev->inf->csr + PMU_PMCNTENCLR); ++} ++ ++static inline void ++xgene_pmu_enable_counter_int(struct xgene_pmu_dev *pmu_dev, int idx) ++{ ++ u32 val; ++ ++ val = readl(pmu_dev->inf->csr + PMU_PMINTENSET); ++ val |= 1 << idx; ++ writel(val, pmu_dev->inf->csr + PMU_PMINTENSET); ++} ++ ++static inline void ++xgene_pmu_disable_counter_int(struct xgene_pmu_dev *pmu_dev, int idx) ++{ ++ u32 val; ++ ++ val = readl(pmu_dev->inf->csr + PMU_PMINTENCLR); ++ val |= 1 << idx; ++ writel(val, pmu_dev->inf->csr + PMU_PMINTENCLR); ++} ++ ++static inline void xgene_pmu_reset_counters(struct xgene_pmu_dev *pmu_dev) ++{ ++ u32 val; ++ ++ val = readl(pmu_dev->inf->csr + PMU_PMCR); ++ val |= PMU_PMCR_P; ++ writel(val, pmu_dev->inf->csr + PMU_PMCR); ++} ++ ++static inline void xgene_pmu_start_counters(struct xgene_pmu_dev *pmu_dev) ++{ ++ u32 val; ++ ++ val = readl(pmu_dev->inf->csr + PMU_PMCR); ++ val |= PMU_PMCR_E; ++ writel(val, pmu_dev->inf->csr + PMU_PMCR); ++} ++ ++static inline void xgene_pmu_stop_counters(struct xgene_pmu_dev *pmu_dev) ++{ ++ u32 val; ++ ++ val = readl(pmu_dev->inf->csr + PMU_PMCR); ++ val &= ~PMU_PMCR_E; ++ writel(val, pmu_dev->inf->csr + PMU_PMCR); ++} ++ ++static void xgene_perf_pmu_enable(struct pmu *pmu) ++{ ++ struct xgene_pmu_dev *pmu_dev = to_pmu_dev(pmu); ++ int enabled = bitmap_weight(pmu_dev->cntr_assign_mask, ++ pmu_dev->max_counters); ++ ++ if (!enabled) ++ return; ++ ++ xgene_pmu_start_counters(pmu_dev); ++} ++ ++static void xgene_perf_pmu_disable(struct pmu *pmu) ++{ ++ struct xgene_pmu_dev *pmu_dev = to_pmu_dev(pmu); ++ ++ xgene_pmu_stop_counters(pmu_dev); ++} ++ ++static int xgene_perf_event_init(struct perf_event *event) ++{ ++ struct xgene_pmu_dev *pmu_dev = to_pmu_dev(event->pmu); ++ struct hw_perf_event *hw = &event->hw; ++ struct perf_event *sibling; ++ ++ /* Test the event attr type check for PMU enumeration */ ++ if (event->attr.type != event->pmu->type) ++ return -ENOENT; ++ ++ /* ++ * SOC PMU counters are shared across all cores. ++ * Therefore, it does not support per-process mode. ++ * Also, it does not support event sampling mode. ++ */ ++ if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK) ++ return -EINVAL; ++ ++ /* SOC counters do not have usr/os/guest/host bits */ ++ if (event->attr.exclude_user || event->attr.exclude_kernel || ++ event->attr.exclude_host || event->attr.exclude_guest) ++ return -EINVAL; ++ ++ if (event->cpu < 0) ++ return -EINVAL; ++ /* ++ * Many perf core operations (eg. events rotation) operate on a ++ * single CPU context. This is obvious for CPU PMUs, where one ++ * expects the same sets of events being observed on all CPUs, ++ * but can lead to issues for off-core PMUs, where each ++ * event could be theoretically assigned to a different CPU. To ++ * mitigate this, we enforce CPU assignment to one, selected ++ * processor (the one described in the "cpumask" attribute). ++ */ ++ event->cpu = cpumask_first(&pmu_dev->parent->cpu); ++ ++ hw->config = event->attr.config; ++ /* ++ * Each bit of the config1 field represents an agent from which the ++ * request of the event come. The event is counted only if it's caused ++ * by a request of an agent has the bit cleared. ++ * By default, the event is counted for all agents. ++ */ ++ hw->config_base = event->attr.config1; ++ ++ /* ++ * We must NOT create groups containing mixed PMUs, although software ++ * events are acceptable ++ */ ++ if (event->group_leader->pmu != event->pmu && ++ !is_software_event(event->group_leader)) ++ return -EINVAL; ++ ++ list_for_each_entry(sibling, &event->group_leader->sibling_list, ++ group_entry) ++ if (sibling->pmu != event->pmu && ++ !is_software_event(sibling)) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static void xgene_perf_enable_event(struct perf_event *event) ++{ ++ struct xgene_pmu_dev *pmu_dev = to_pmu_dev(event->pmu); ++ ++ xgene_pmu_write_evttype(pmu_dev, GET_CNTR(event), GET_EVENTID(event)); ++ xgene_pmu_write_agentmsk(pmu_dev, ~((u32)GET_AGENTID(event))); ++ if (pmu_dev->inf->type == PMU_TYPE_IOB) ++ xgene_pmu_write_agent1msk(pmu_dev, ~((u32)GET_AGENT1ID(event))); ++ ++ xgene_pmu_enable_counter(pmu_dev, GET_CNTR(event)); ++ xgene_pmu_enable_counter_int(pmu_dev, GET_CNTR(event)); ++} ++ ++static void xgene_perf_disable_event(struct perf_event *event) ++{ ++ struct xgene_pmu_dev *pmu_dev = to_pmu_dev(event->pmu); ++ ++ xgene_pmu_disable_counter(pmu_dev, GET_CNTR(event)); ++ xgene_pmu_disable_counter_int(pmu_dev, GET_CNTR(event)); ++} ++ ++static void xgene_perf_event_set_period(struct perf_event *event) ++{ ++ struct xgene_pmu_dev *pmu_dev = to_pmu_dev(event->pmu); ++ struct hw_perf_event *hw = &event->hw; ++ /* ++ * The X-Gene PMU counters have a period of 2^32. To account for the ++ * possiblity of extreme interrupt latency we program for a period of ++ * half that. Hopefully we can handle the interrupt before another 2^31 ++ * events occur and the counter overtakes its previous value. ++ */ ++ u64 val = 1ULL << 31; ++ ++ local64_set(&hw->prev_count, val); ++ xgene_pmu_write_counter(pmu_dev, hw->idx, (u32) val); ++} ++ ++static void xgene_perf_event_update(struct perf_event *event) ++{ ++ struct xgene_pmu_dev *pmu_dev = to_pmu_dev(event->pmu); ++ struct hw_perf_event *hw = &event->hw; ++ u64 delta, prev_raw_count, new_raw_count; ++ ++again: ++ prev_raw_count = local64_read(&hw->prev_count); ++ new_raw_count = xgene_pmu_read_counter(pmu_dev, GET_CNTR(event)); ++ ++ if (local64_cmpxchg(&hw->prev_count, prev_raw_count, ++ new_raw_count) != prev_raw_count) ++ goto again; ++ ++ delta = (new_raw_count - prev_raw_count) & pmu_dev->max_period; ++ ++ local64_add(delta, &event->count); ++} ++ ++static void xgene_perf_read(struct perf_event *event) ++{ ++ xgene_perf_event_update(event); ++} ++ ++static void xgene_perf_start(struct perf_event *event, int flags) ++{ ++ struct xgene_pmu_dev *pmu_dev = to_pmu_dev(event->pmu); ++ struct hw_perf_event *hw = &event->hw; ++ ++ if (WARN_ON_ONCE(!(hw->state & PERF_HES_STOPPED))) ++ return; ++ ++ WARN_ON_ONCE(!(hw->state & PERF_HES_UPTODATE)); ++ hw->state = 0; ++ ++ xgene_perf_event_set_period(event); ++ ++ if (flags & PERF_EF_RELOAD) { ++ u64 prev_raw_count = local64_read(&hw->prev_count); ++ ++ xgene_pmu_write_counter(pmu_dev, GET_CNTR(event), ++ (u32) prev_raw_count); ++ } ++ ++ xgene_perf_enable_event(event); ++ perf_event_update_userpage(event); ++} ++ ++static void xgene_perf_stop(struct perf_event *event, int flags) ++{ ++ struct hw_perf_event *hw = &event->hw; ++ u64 config; ++ ++ if (hw->state & PERF_HES_UPTODATE) ++ return; ++ ++ xgene_perf_disable_event(event); ++ WARN_ON_ONCE(hw->state & PERF_HES_STOPPED); ++ hw->state |= PERF_HES_STOPPED; ++ ++ if (hw->state & PERF_HES_UPTODATE) ++ return; ++ ++ config = hw->config; ++ xgene_perf_read(event); ++ hw->state |= PERF_HES_UPTODATE; ++} ++ ++static int xgene_perf_add(struct perf_event *event, int flags) ++{ ++ struct xgene_pmu_dev *pmu_dev = to_pmu_dev(event->pmu); ++ struct hw_perf_event *hw = &event->hw; ++ ++ hw->state = PERF_HES_UPTODATE | PERF_HES_STOPPED; ++ ++ /* Allocate an event counter */ ++ hw->idx = get_next_avail_cntr(pmu_dev); ++ if (hw->idx < 0) ++ return -EAGAIN; ++ ++ /* Update counter event pointer for Interrupt handler */ ++ pmu_dev->pmu_counter_event[hw->idx] = event; ++ ++ if (flags & PERF_EF_START) ++ xgene_perf_start(event, PERF_EF_RELOAD); ++ ++ return 0; ++} ++ ++static void xgene_perf_del(struct perf_event *event, int flags) ++{ ++ struct xgene_pmu_dev *pmu_dev = to_pmu_dev(event->pmu); ++ struct hw_perf_event *hw = &event->hw; ++ ++ xgene_perf_stop(event, PERF_EF_UPDATE); ++ ++ /* clear the assigned counter */ ++ clear_avail_cntr(pmu_dev, GET_CNTR(event)); ++ ++ perf_event_update_userpage(event); ++ pmu_dev->pmu_counter_event[hw->idx] = NULL; ++} ++ ++static int xgene_init_perf(struct xgene_pmu_dev *pmu_dev, char *name) ++{ ++ struct xgene_pmu *xgene_pmu; ++ ++ pmu_dev->max_period = PMU_CNT_MAX_PERIOD - 1; ++ /* First version PMU supports only single event counter */ ++ xgene_pmu = pmu_dev->parent; ++ if (xgene_pmu->version == PCP_PMU_V1) ++ pmu_dev->max_counters = 1; ++ else ++ pmu_dev->max_counters = PMU_MAX_COUNTERS; ++ ++ /* Perf driver registration */ ++ pmu_dev->pmu = (struct pmu) { ++ .attr_groups = pmu_dev->attr_groups, ++ .task_ctx_nr = perf_invalid_context, ++ .pmu_enable = xgene_perf_pmu_enable, ++ .pmu_disable = xgene_perf_pmu_disable, ++ .event_init = xgene_perf_event_init, ++ .add = xgene_perf_add, ++ .del = xgene_perf_del, ++ .start = xgene_perf_start, ++ .stop = xgene_perf_stop, ++ .read = xgene_perf_read, ++ }; ++ ++ /* Hardware counter init */ ++ xgene_pmu_stop_counters(pmu_dev); ++ xgene_pmu_reset_counters(pmu_dev); ++ ++ return perf_pmu_register(&pmu_dev->pmu, name, -1); ++} ++ ++static int ++xgene_pmu_dev_add(struct xgene_pmu *xgene_pmu, struct xgene_pmu_dev_ctx *ctx) ++{ ++ struct device *dev = xgene_pmu->dev; ++ struct xgene_pmu_dev *pmu; ++ int rc; ++ ++ pmu = devm_kzalloc(dev, sizeof(*pmu), GFP_KERNEL); ++ if (!pmu) ++ return -ENOMEM; ++ pmu->parent = xgene_pmu; ++ pmu->inf = &ctx->inf; ++ ctx->pmu_dev = pmu; ++ ++ switch (pmu->inf->type) { ++ case PMU_TYPE_L3C: ++ pmu->attr_groups = l3c_pmu_attr_groups; ++ break; ++ case PMU_TYPE_IOB: ++ pmu->attr_groups = iob_pmu_attr_groups; ++ break; ++ case PMU_TYPE_MCB: ++ if (!(xgene_pmu->mcb_active_mask & pmu->inf->enable_mask)) ++ goto dev_err; ++ pmu->attr_groups = mcb_pmu_attr_groups; ++ break; ++ case PMU_TYPE_MC: ++ if (!(xgene_pmu->mc_active_mask & pmu->inf->enable_mask)) ++ goto dev_err; ++ pmu->attr_groups = mc_pmu_attr_groups; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ rc = xgene_init_perf(pmu, ctx->name); ++ if (rc) { ++ dev_err(dev, "%s PMU: Failed to init perf driver\n", ctx->name); ++ goto dev_err; ++ } ++ ++ dev_info(dev, "%s PMU registered\n", ctx->name); ++ ++ return rc; ++ ++dev_err: ++ devm_kfree(dev, pmu); ++ return -ENODEV; ++} ++ ++static void _xgene_pmu_isr(int irq, struct xgene_pmu_dev *pmu_dev) ++{ ++ struct xgene_pmu *xgene_pmu = pmu_dev->parent; ++ u32 pmovsr; ++ int idx; ++ ++ pmovsr = readl(pmu_dev->inf->csr + PMU_PMOVSR) & PMU_OVERFLOW_MASK; ++ if (!pmovsr) ++ return; ++ ++ /* Clear interrupt flag */ ++ if (xgene_pmu->version == PCP_PMU_V1) ++ writel(0x0, pmu_dev->inf->csr + PMU_PMOVSR); ++ else ++ writel(pmovsr, pmu_dev->inf->csr + PMU_PMOVSR); ++ ++ for (idx = 0; idx < PMU_MAX_COUNTERS; idx++) { ++ struct perf_event *event = pmu_dev->pmu_counter_event[idx]; ++ int overflowed = pmovsr & BIT(idx); ++ ++ /* Ignore if we don't have an event. */ ++ if (!event || !overflowed) ++ continue; ++ xgene_perf_event_update(event); ++ xgene_perf_event_set_period(event); ++ } ++} ++ ++static irqreturn_t xgene_pmu_isr(int irq, void *dev_id) ++{ ++ struct xgene_pmu_dev_ctx *ctx; ++ struct xgene_pmu *xgene_pmu = dev_id; ++ unsigned long flags; ++ u32 val; ++ ++ raw_spin_lock_irqsave(&xgene_pmu->lock, flags); ++ ++ /* Get Interrupt PMU source */ ++ val = readl(xgene_pmu->pcppmu_csr + PCPPMU_INTSTATUS_REG); ++ if (val & PCPPMU_INT_MCU) { ++ list_for_each_entry(ctx, &xgene_pmu->mcpmus, next) { ++ _xgene_pmu_isr(irq, ctx->pmu_dev); ++ } ++ } ++ if (val & PCPPMU_INT_MCB) { ++ list_for_each_entry(ctx, &xgene_pmu->mcbpmus, next) { ++ _xgene_pmu_isr(irq, ctx->pmu_dev); ++ } ++ } ++ if (val & PCPPMU_INT_L3C) { ++ list_for_each_entry(ctx, &xgene_pmu->l3cpmus, next) { ++ _xgene_pmu_isr(irq, ctx->pmu_dev); ++ } ++ } ++ if (val & PCPPMU_INT_IOB) { ++ list_for_each_entry(ctx, &xgene_pmu->iobpmus, next) { ++ _xgene_pmu_isr(irq, ctx->pmu_dev); ++ } ++ } ++ ++ raw_spin_unlock_irqrestore(&xgene_pmu->lock, flags); ++ ++ return IRQ_HANDLED; ++} ++ ++static int acpi_pmu_probe_active_mcb_mcu(struct xgene_pmu *xgene_pmu, ++ struct platform_device *pdev) ++{ ++ void __iomem *csw_csr, *mcba_csr, *mcbb_csr; ++ struct resource *res; ++ unsigned int reg; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ csw_csr = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(csw_csr)) { ++ dev_err(&pdev->dev, "ioremap failed for CSW CSR resource\n"); ++ return PTR_ERR(csw_csr); ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 2); ++ mcba_csr = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(mcba_csr)) { ++ dev_err(&pdev->dev, "ioremap failed for MCBA CSR resource\n"); ++ return PTR_ERR(mcba_csr); ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 3); ++ mcbb_csr = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(mcbb_csr)) { ++ dev_err(&pdev->dev, "ioremap failed for MCBB CSR resource\n"); ++ return PTR_ERR(mcbb_csr); ++ } ++ ++ reg = readl(csw_csr + CSW_CSWCR); ++ if (reg & CSW_CSWCR_DUALMCB_MASK) { ++ /* Dual MCB active */ ++ xgene_pmu->mcb_active_mask = 0x3; ++ /* Probe all active MC(s) */ ++ reg = readl(mcbb_csr + CSW_CSWCR); ++ xgene_pmu->mc_active_mask = ++ (reg & MCBADDRMR_DUALMCU_MODE_MASK) ? 0xF : 0x5; ++ } else { ++ /* Single MCB active */ ++ xgene_pmu->mcb_active_mask = 0x1; ++ /* Probe all active MC(s) */ ++ reg = readl(mcba_csr + CSW_CSWCR); ++ xgene_pmu->mc_active_mask = ++ (reg & MCBADDRMR_DUALMCU_MODE_MASK) ? 0x3 : 0x1; ++ } ++ ++ return 0; ++} ++ ++static int fdt_pmu_probe_active_mcb_mcu(struct xgene_pmu *xgene_pmu, ++ struct platform_device *pdev) ++{ ++ struct regmap *csw_map, *mcba_map, *mcbb_map; ++ struct device_node *np = pdev->dev.of_node; ++ unsigned int reg; ++ ++ csw_map = syscon_regmap_lookup_by_phandle(np, "regmap-csw"); ++ if (IS_ERR(csw_map)) { ++ dev_err(&pdev->dev, "unable to get syscon regmap csw\n"); ++ return PTR_ERR(csw_map); ++ } ++ ++ mcba_map = syscon_regmap_lookup_by_phandle(np, "regmap-mcba"); ++ if (IS_ERR(mcba_map)) { ++ dev_err(&pdev->dev, "unable to get syscon regmap mcba\n"); ++ return PTR_ERR(mcba_map); ++ } ++ ++ mcbb_map = syscon_regmap_lookup_by_phandle(np, "regmap-mcbb"); ++ if (IS_ERR(mcbb_map)) { ++ dev_err(&pdev->dev, "unable to get syscon regmap mcbb\n"); ++ return PTR_ERR(mcbb_map); ++ } ++ ++ if (regmap_read(csw_map, CSW_CSWCR, ®)) ++ return -EINVAL; ++ ++ if (reg & CSW_CSWCR_DUALMCB_MASK) { ++ /* Dual MCB active */ ++ xgene_pmu->mcb_active_mask = 0x3; ++ /* Probe all active MC(s) */ ++ if (regmap_read(mcbb_map, MCBADDRMR, ®)) ++ return 0; ++ xgene_pmu->mc_active_mask = ++ (reg & MCBADDRMR_DUALMCU_MODE_MASK) ? 0xF : 0x5; ++ } else { ++ /* Single MCB active */ ++ xgene_pmu->mcb_active_mask = 0x1; ++ /* Probe all active MC(s) */ ++ if (regmap_read(mcba_map, MCBADDRMR, ®)) ++ return 0; ++ xgene_pmu->mc_active_mask = ++ (reg & MCBADDRMR_DUALMCU_MODE_MASK) ? 0x3 : 0x1; ++ } ++ ++ return 0; ++} ++ ++static int xgene_pmu_probe_active_mcb_mcu(struct xgene_pmu *xgene_pmu, ++ struct platform_device *pdev) ++{ ++ if (has_acpi_companion(&pdev->dev)) ++ return acpi_pmu_probe_active_mcb_mcu(xgene_pmu, pdev); ++ return fdt_pmu_probe_active_mcb_mcu(xgene_pmu, pdev); ++} ++ ++static char *xgene_pmu_dev_name(struct device *dev, u32 type, int id) ++{ ++ switch (type) { ++ case PMU_TYPE_L3C: ++ return devm_kasprintf(dev, GFP_KERNEL, "l3c%d", id); ++ case PMU_TYPE_IOB: ++ return devm_kasprintf(dev, GFP_KERNEL, "iob%d", id); ++ case PMU_TYPE_MCB: ++ return devm_kasprintf(dev, GFP_KERNEL, "mcb%d", id); ++ case PMU_TYPE_MC: ++ return devm_kasprintf(dev, GFP_KERNEL, "mc%d", id); ++ default: ++ return devm_kasprintf(dev, GFP_KERNEL, "unknown"); ++ } ++} ++ ++#if defined(CONFIG_ACPI) ++static int acpi_pmu_dev_add_resource(struct acpi_resource *ares, void *data) ++{ ++ struct resource *res = data; ++ ++ if (ares->type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) ++ acpi_dev_resource_memory(ares, res); ++ ++ /* Always tell the ACPI core to skip this resource */ ++ return 1; ++} ++ ++static struct ++xgene_pmu_dev_ctx *acpi_get_pmu_hw_inf(struct xgene_pmu *xgene_pmu, ++ struct acpi_device *adev, u32 type) ++{ ++ struct device *dev = xgene_pmu->dev; ++ struct list_head resource_list; ++ struct xgene_pmu_dev_ctx *ctx; ++ const union acpi_object *obj; ++ struct hw_pmu_info *inf; ++ void __iomem *dev_csr; ++ struct resource res; ++ int enable_bit; ++ int rc; ++ ++ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); ++ if (!ctx) ++ return NULL; ++ ++ INIT_LIST_HEAD(&resource_list); ++ rc = acpi_dev_get_resources(adev, &resource_list, ++ acpi_pmu_dev_add_resource, &res); ++ acpi_dev_free_resource_list(&resource_list); ++ if (rc < 0 || IS_ERR(&res)) { ++ dev_err(dev, "PMU type %d: No resource address found\n", type); ++ goto err; ++ } ++ ++ dev_csr = devm_ioremap_resource(dev, &res); ++ if (IS_ERR(dev_csr)) { ++ dev_err(dev, "PMU type %d: Fail to map resource\n", type); ++ goto err; ++ } ++ ++ /* A PMU device node without enable-bit-index is always enabled */ ++ rc = acpi_dev_get_property(adev, "enable-bit-index", ++ ACPI_TYPE_INTEGER, &obj); ++ if (rc < 0) ++ enable_bit = 0; ++ else ++ enable_bit = (int) obj->integer.value; ++ ++ ctx->name = xgene_pmu_dev_name(dev, type, enable_bit); ++ if (!ctx->name) { ++ dev_err(dev, "PMU type %d: Fail to get device name\n", type); ++ goto err; ++ } ++ inf = &ctx->inf; ++ inf->type = type; ++ inf->csr = dev_csr; ++ inf->enable_mask = 1 << enable_bit; ++ ++ return ctx; ++err: ++ devm_kfree(dev, ctx); ++ return NULL; ++} ++ ++static acpi_status acpi_pmu_dev_add(acpi_handle handle, u32 level, ++ void *data, void **return_value) ++{ ++ struct xgene_pmu *xgene_pmu = data; ++ struct xgene_pmu_dev_ctx *ctx; ++ struct acpi_device *adev; ++ ++ if (acpi_bus_get_device(handle, &adev)) ++ return AE_OK; ++ if (acpi_bus_get_status(adev) || !adev->status.present) ++ return AE_OK; ++ ++ if (!strcmp(acpi_device_hid(adev), "APMC0D5D")) ++ ctx = acpi_get_pmu_hw_inf(xgene_pmu, adev, PMU_TYPE_L3C); ++ else if (!strcmp(acpi_device_hid(adev), "APMC0D5E")) ++ ctx = acpi_get_pmu_hw_inf(xgene_pmu, adev, PMU_TYPE_IOB); ++ else if (!strcmp(acpi_device_hid(adev), "APMC0D5F")) ++ ctx = acpi_get_pmu_hw_inf(xgene_pmu, adev, PMU_TYPE_MCB); ++ else if (!strcmp(acpi_device_hid(adev), "APMC0D60")) ++ ctx = acpi_get_pmu_hw_inf(xgene_pmu, adev, PMU_TYPE_MC); ++ else ++ ctx = NULL; ++ ++ if (!ctx) ++ return AE_OK; ++ ++ if (xgene_pmu_dev_add(xgene_pmu, ctx)) { ++ /* Can't add the PMU device, skip it */ ++ devm_kfree(xgene_pmu->dev, ctx); ++ return AE_OK; ++ } ++ ++ switch (ctx->inf.type) { ++ case PMU_TYPE_L3C: ++ list_add(&ctx->next, &xgene_pmu->l3cpmus); ++ break; ++ case PMU_TYPE_IOB: ++ list_add(&ctx->next, &xgene_pmu->iobpmus); ++ break; ++ case PMU_TYPE_MCB: ++ list_add(&ctx->next, &xgene_pmu->mcbpmus); ++ break; ++ case PMU_TYPE_MC: ++ list_add(&ctx->next, &xgene_pmu->mcpmus); ++ break; ++ } ++ return AE_OK; ++} ++ ++static int acpi_pmu_probe_pmu_dev(struct xgene_pmu *xgene_pmu, ++ struct platform_device *pdev) ++{ ++ struct device *dev = xgene_pmu->dev; ++ acpi_handle handle; ++ acpi_status status; ++ ++ handle = ACPI_HANDLE(dev); ++ if (!handle) ++ return -EINVAL; ++ ++ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, ++ acpi_pmu_dev_add, NULL, xgene_pmu, NULL); ++ if (ACPI_FAILURE(status)) { ++ dev_err(dev, "failed to probe PMU devices\n"); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++#else ++static int acpi_pmu_probe_pmu_dev(struct xgene_pmu *xgene_pmu, ++ struct platform_device *pdev) ++{ ++ return 0; ++} ++#endif ++ ++static struct ++xgene_pmu_dev_ctx *fdt_get_pmu_hw_inf(struct xgene_pmu *xgene_pmu, ++ struct device_node *np, u32 type) ++{ ++ struct device *dev = xgene_pmu->dev; ++ struct xgene_pmu_dev_ctx *ctx; ++ struct hw_pmu_info *inf; ++ void __iomem *dev_csr; ++ struct resource res; ++ int enable_bit; ++ int rc; ++ ++ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); ++ if (!ctx) ++ return NULL; ++ rc = of_address_to_resource(np, 0, &res); ++ if (rc < 0) { ++ dev_err(dev, "PMU type %d: No resource address found\n", type); ++ goto err; ++ } ++ dev_csr = devm_ioremap_resource(dev, &res); ++ if (IS_ERR(dev_csr)) { ++ dev_err(dev, "PMU type %d: Fail to map resource\n", type); ++ goto err; ++ } ++ ++ /* A PMU device node without enable-bit-index is always enabled */ ++ if (of_property_read_u32(np, "enable-bit-index", &enable_bit)) ++ enable_bit = 0; ++ ++ ctx->name = xgene_pmu_dev_name(dev, type, enable_bit); ++ if (!ctx->name) { ++ dev_err(dev, "PMU type %d: Fail to get device name\n", type); ++ goto err; ++ } ++ inf = &ctx->inf; ++ inf->type = type; ++ inf->csr = dev_csr; ++ inf->enable_mask = 1 << enable_bit; ++ ++ return ctx; ++err: ++ devm_kfree(dev, ctx); ++ return NULL; ++} ++ ++static int fdt_pmu_probe_pmu_dev(struct xgene_pmu *xgene_pmu, ++ struct platform_device *pdev) ++{ ++ struct xgene_pmu_dev_ctx *ctx; ++ struct device_node *np; ++ ++ for_each_child_of_node(pdev->dev.of_node, np) { ++ if (!of_device_is_available(np)) ++ continue; ++ ++ if (of_device_is_compatible(np, "apm,xgene-pmu-l3c")) ++ ctx = fdt_get_pmu_hw_inf(xgene_pmu, np, PMU_TYPE_L3C); ++ else if (of_device_is_compatible(np, "apm,xgene-pmu-iob")) ++ ctx = fdt_get_pmu_hw_inf(xgene_pmu, np, PMU_TYPE_IOB); ++ else if (of_device_is_compatible(np, "apm,xgene-pmu-mcb")) ++ ctx = fdt_get_pmu_hw_inf(xgene_pmu, np, PMU_TYPE_MCB); ++ else if (of_device_is_compatible(np, "apm,xgene-pmu-mc")) ++ ctx = fdt_get_pmu_hw_inf(xgene_pmu, np, PMU_TYPE_MC); ++ else ++ ctx = NULL; ++ ++ if (!ctx) ++ continue; ++ ++ if (xgene_pmu_dev_add(xgene_pmu, ctx)) { ++ /* Can't add the PMU device, skip it */ ++ devm_kfree(xgene_pmu->dev, ctx); ++ continue; ++ } ++ ++ switch (ctx->inf.type) { ++ case PMU_TYPE_L3C: ++ list_add(&ctx->next, &xgene_pmu->l3cpmus); ++ break; ++ case PMU_TYPE_IOB: ++ list_add(&ctx->next, &xgene_pmu->iobpmus); ++ break; ++ case PMU_TYPE_MCB: ++ list_add(&ctx->next, &xgene_pmu->mcbpmus); ++ break; ++ case PMU_TYPE_MC: ++ list_add(&ctx->next, &xgene_pmu->mcpmus); ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++static int xgene_pmu_probe_pmu_dev(struct xgene_pmu *xgene_pmu, ++ struct platform_device *pdev) ++{ ++ if (has_acpi_companion(&pdev->dev)) ++ return acpi_pmu_probe_pmu_dev(xgene_pmu, pdev); ++ return fdt_pmu_probe_pmu_dev(xgene_pmu, pdev); ++} ++ ++static const struct xgene_pmu_data xgene_pmu_data = { ++ .id = PCP_PMU_V1, ++}; ++ ++static const struct xgene_pmu_data xgene_pmu_v2_data = { ++ .id = PCP_PMU_V2, ++}; ++ ++static const struct of_device_id xgene_pmu_of_match[] = { ++ { .compatible = "apm,xgene-pmu", .data = &xgene_pmu_data }, ++ { .compatible = "apm,xgene-pmu-v2", .data = &xgene_pmu_v2_data }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, xgene_pmu_of_match); ++#ifdef CONFIG_ACPI ++static const struct acpi_device_id xgene_pmu_acpi_match[] = { ++ {"APMC0D5B", PCP_PMU_V1}, ++ {"APMC0D5C", PCP_PMU_V2}, ++ {}, ++}; ++MODULE_DEVICE_TABLE(acpi, xgene_pmu_acpi_match); ++#endif ++ ++static int xgene_pmu_probe(struct platform_device *pdev) ++{ ++ const struct xgene_pmu_data *dev_data; ++ const struct of_device_id *of_id; ++ struct xgene_pmu *xgene_pmu; ++ struct resource *res; ++ int irq, rc; ++ int version; ++ ++ xgene_pmu = devm_kzalloc(&pdev->dev, sizeof(*xgene_pmu), GFP_KERNEL); ++ if (!xgene_pmu) ++ return -ENOMEM; ++ xgene_pmu->dev = &pdev->dev; ++ platform_set_drvdata(pdev, xgene_pmu); ++ ++ version = -EINVAL; ++ of_id = of_match_device(xgene_pmu_of_match, &pdev->dev); ++ if (of_id) { ++ dev_data = (const struct xgene_pmu_data *) of_id->data; ++ version = dev_data->id; ++ } ++ ++#ifdef CONFIG_ACPI ++ if (ACPI_COMPANION(&pdev->dev)) { ++ const struct acpi_device_id *acpi_id; ++ ++ acpi_id = acpi_match_device(xgene_pmu_acpi_match, &pdev->dev); ++ if (acpi_id) ++ version = (int) acpi_id->driver_data; ++ } ++#endif ++ if (version < 0) ++ return -ENODEV; ++ ++ INIT_LIST_HEAD(&xgene_pmu->l3cpmus); ++ INIT_LIST_HEAD(&xgene_pmu->iobpmus); ++ INIT_LIST_HEAD(&xgene_pmu->mcbpmus); ++ INIT_LIST_HEAD(&xgene_pmu->mcpmus); ++ ++ xgene_pmu->version = version; ++ dev_info(&pdev->dev, "X-Gene PMU version %d\n", xgene_pmu->version); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ xgene_pmu->pcppmu_csr = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(xgene_pmu->pcppmu_csr)) { ++ dev_err(&pdev->dev, "ioremap failed for PCP PMU resource\n"); ++ rc = PTR_ERR(xgene_pmu->pcppmu_csr); ++ goto err; ++ } ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ dev_err(&pdev->dev, "No IRQ resource\n"); ++ rc = -EINVAL; ++ goto err; ++ } ++ rc = devm_request_irq(&pdev->dev, irq, xgene_pmu_isr, ++ IRQF_NOBALANCING | IRQF_NO_THREAD, ++ dev_name(&pdev->dev), xgene_pmu); ++ if (rc) { ++ dev_err(&pdev->dev, "Could not request IRQ %d\n", irq); ++ goto err; ++ } ++ ++ raw_spin_lock_init(&xgene_pmu->lock); ++ ++ /* Check for active MCBs and MCUs */ ++ rc = xgene_pmu_probe_active_mcb_mcu(xgene_pmu, pdev); ++ if (rc) { ++ dev_warn(&pdev->dev, "Unknown MCB/MCU active status\n"); ++ xgene_pmu->mcb_active_mask = 0x1; ++ xgene_pmu->mc_active_mask = 0x1; ++ } ++ ++ /* Pick one core to use for cpumask attributes */ ++ cpumask_set_cpu(smp_processor_id(), &xgene_pmu->cpu); ++ ++ /* Make sure that the overflow interrupt is handled by this CPU */ ++ rc = irq_set_affinity(irq, &xgene_pmu->cpu); ++ if (rc) { ++ dev_err(&pdev->dev, "Failed to set interrupt affinity!\n"); ++ goto err; ++ } ++ ++ /* Walk through the tree for all PMU perf devices */ ++ rc = xgene_pmu_probe_pmu_dev(xgene_pmu, pdev); ++ if (rc) { ++ dev_err(&pdev->dev, "No PMU perf devices found!\n"); ++ goto err; ++ } ++ ++ /* Enable interrupt */ ++ xgene_pmu_unmask_int(xgene_pmu); ++ ++ return 0; ++ ++err: ++ if (xgene_pmu->pcppmu_csr) ++ devm_iounmap(&pdev->dev, xgene_pmu->pcppmu_csr); ++ devm_kfree(&pdev->dev, xgene_pmu); ++ ++ return rc; ++} ++ ++static void ++xgene_pmu_dev_cleanup(struct xgene_pmu *xgene_pmu, struct list_head *pmus) ++{ ++ struct xgene_pmu_dev_ctx *ctx; ++ struct device *dev = xgene_pmu->dev; ++ struct xgene_pmu_dev *pmu_dev; ++ ++ list_for_each_entry(ctx, pmus, next) { ++ pmu_dev = ctx->pmu_dev; ++ if (pmu_dev->inf->csr) ++ devm_iounmap(dev, pmu_dev->inf->csr); ++ devm_kfree(dev, ctx); ++ devm_kfree(dev, pmu_dev); ++ } ++} ++ ++static int xgene_pmu_remove(struct platform_device *pdev) ++{ ++ struct xgene_pmu *xgene_pmu = dev_get_drvdata(&pdev->dev); ++ ++ xgene_pmu_dev_cleanup(xgene_pmu, &xgene_pmu->l3cpmus); ++ xgene_pmu_dev_cleanup(xgene_pmu, &xgene_pmu->iobpmus); ++ xgene_pmu_dev_cleanup(xgene_pmu, &xgene_pmu->mcbpmus); ++ xgene_pmu_dev_cleanup(xgene_pmu, &xgene_pmu->mcpmus); ++ ++ if (xgene_pmu->pcppmu_csr) ++ devm_iounmap(&pdev->dev, xgene_pmu->pcppmu_csr); ++ devm_kfree(&pdev->dev, xgene_pmu); ++ ++ return 0; ++} ++ ++static struct platform_driver xgene_pmu_driver = { ++ .probe = xgene_pmu_probe, ++ .remove = xgene_pmu_remove, ++ .driver = { ++ .name = "xgene-pmu", ++ .of_match_table = xgene_pmu_of_match, ++ .acpi_match_table = ACPI_PTR(xgene_pmu_acpi_match), ++ }, ++}; ++ ++builtin_platform_driver(xgene_pmu_driver); +-- +1.8.3.1 + diff --git a/SOURCES/1008-perf-xgene-Remove-bogus-IS_ERR-check.patch b/SOURCES/1008-perf-xgene-Remove-bogus-IS_ERR-check.patch new file mode 100644 index 0000000..5d7b0d1 --- /dev/null +++ b/SOURCES/1008-perf-xgene-Remove-bogus-IS_ERR-check.patch @@ -0,0 +1,39 @@ +From da17db043d86fe330c0d437a3ea22e6b30834d35 Mon Sep 17 00:00:00 2001 +From: Tai Nguyen ttnguyen@apm.com +Date: Thu, 13 Oct 2016 11:09:16 -0700 +Subject: [PATCH 1008/1018] perf: xgene: Remove bogus IS_ERR() check + +In acpi_get_pmu_hw_inf we pass the address of a local variable to IS_ERR(), +which doesn't make sense, as the pointer must be a real, valid pointer. +This doesn't cause a functional problem, as IS_ERR() will evaluate as +false, but the check is bogus and causes static checkers to complain. + +Remove the bogus check. + +The bug is reported by Dan Carpenter dan.carpenter@oracle.com in [1] + +[1] https://www.spinics.net/lists/arm-kernel/msg535957.html + +Signed-off-by: Tai Nguyen ttnguyen@apm.com +Acked-by: Mark Rutland mark.rutland@arm.com +Signed-off-by: Will Deacon will.deacon@arm.com +--- + drivers/perf/xgene_pmu.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/perf/xgene_pmu.c b/drivers/perf/xgene_pmu.c +index c2ac764..a8ac4bc 100644 +--- a/drivers/perf/xgene_pmu.c ++++ b/drivers/perf/xgene_pmu.c +@@ -1011,7 +1011,7 @@ xgene_pmu_dev_ctx *acpi_get_pmu_hw_inf(struct xgene_pmu *xgene_pmu, + rc = acpi_dev_get_resources(adev, &resource_list, + acpi_pmu_dev_add_resource, &res); + acpi_dev_free_resource_list(&resource_list); +- if (rc < 0 || IS_ERR(&res)) { ++ if (rc < 0) { + dev_err(dev, "PMU type %d: No resource address found\n", type); + goto err; + } +-- +1.8.3.1 + diff --git a/SOURCES/1009-MAINTAINERS-Add-entry-for-APM-X-Gene-SoC-PMU-driver.patch b/SOURCES/1009-MAINTAINERS-Add-entry-for-APM-X-Gene-SoC-PMU-driver.patch new file mode 100644 index 0000000..323d81a --- /dev/null +++ b/SOURCES/1009-MAINTAINERS-Add-entry-for-APM-X-Gene-SoC-PMU-driver.patch @@ -0,0 +1,34 @@ +From da178c12d0a9acfe1123b6c4c9339e1d86d7d25b Mon Sep 17 00:00:00 2001 +From: Tai Nguyen ttnguyen@apm.com +Date: Fri, 15 Jul 2016 10:38:02 -0700 +Subject: [PATCH 1009/1018] MAINTAINERS: Add entry for APM X-Gene SoC PMU + driver + +This patch adds the MAINTAINERS entry for APM X-Gene SoC PMU driver. + +Signed-off-by: Tai Nguyen ttnguyen@apm.com +--- + MAINTAINERS | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/MAINTAINERS b/MAINTAINERS +index 7167fb4..5e20026 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -810,6 +810,13 @@ F: drivers/net/phy/mdio-xgene.c + F: Documentation/devicetree/bindings/net/apm-xgene-enet.txt + F: Documentation/devicetree/bindings/net/apm-xgene-mdio.txt + ++APPLIED MICRO (APM) X-GENE SOC PMU ++M: Tai Nguyen ttnguyen@apm.com ++S: Supported ++F: drivers/perf/xgene_pmu.c ++F: Documentation/perf/xgene-pmu.txt ++F: Documentation/devicetree/bindings/perf/apm-xgene-pmu.txt ++ + APTINA CAMERA SENSOR PLL + M: Laurent Pinchart Laurent.pinchart@ideasonboard.com + L: linux-media@vger.kernel.org +-- +1.8.3.1 + diff --git a/SOURCES/config-centos-sig b/SOURCES/config-centos-sig index 9d17fb7..4cdaedb 100644 --- a/SOURCES/config-centos-sig +++ b/SOURCES/config-centos-sig @@ -5,3 +5,4 @@ CONFIG_QCOM_FALKOR_ERRATUM_E1003=y CONFIG_QCOM_FALKOR_ERRATUM_E1009=y CONFIG_ACPI_CPPC_CPUFREQ=y CONFIG_SENSORS_XGENE=y +CONFIG_XGENE_PMU=y diff --git a/SPECS/kernel-aarch64.spec b/SPECS/kernel-aarch64.spec index 2dcb885..626eba4 100644 --- a/SPECS/kernel-aarch64.spec +++ b/SPECS/kernel-aarch64.spec @@ -331,6 +331,9 @@ Patch1003: 1003-mailbox-PCC-Fix-return-value-of-pcc_mbox_request_cha.patch Patch1004: 1004-Documentation-dtb-xgene-Add-hwmon-dts-binding-docume.patch Patch1005: 1005-hwmon-Add-xgene-hwmon-driver.patch Patch1006: 1006-hwmon-xgene-Fix-crash-when-alarm-occurs-before-drive.patch +Patch1007: 1007-perf-xgene-Add-APM-X-Gene-SoC-Performance-Monitoring.patch +Patch1008: 1008-perf-xgene-Remove-bogus-IS_ERR-check.patch +Patch1009: 1009-MAINTAINERS-Add-entry-for-APM-X-Gene-SoC-PMU-driver.patch
# QDF2400 Patches Patch4000: 4000-arm64-Define-Qualcomm-Technologies-ARMv8-CPU.patch @@ -672,6 +675,9 @@ git am %{PATCH1003} git am %{PATCH1004} git am %{PATCH1005} git am %{PATCH1006} +git am %{PATCH1007} +git am %{PATCH1008} +git am %{PATCH1009}
# Apply QDF2400 patches git am %{PATCH4000} @@ -1451,6 +1457,7 @@ fi
%changelog * Thu Nov 10 2016 Duc Dang dhdang@apm.com [4.5.0-17.el7] +- Add X-Gene SoC PMU support - Add X-Gene HWMon support - mailbox: PCC: Fix return value of pcc_mbox_request_channel - Add cpufreq CPPC bug fixes
Also reorder the X-Gene change log so that the newest change is on the top.
Signed-off-by: Duc Dang dhdang@apm.com --- .../1010-i2c-xgene-Avoid-dma_buffer-overrun.patch | 32 ++++++++++++++++++++++ SPECS/kernel-aarch64.spec | 3 ++ 2 files changed, 35 insertions(+) create mode 100644 SOURCES/1010-i2c-xgene-Avoid-dma_buffer-overrun.patch
diff --git a/SOURCES/1010-i2c-xgene-Avoid-dma_buffer-overrun.patch b/SOURCES/1010-i2c-xgene-Avoid-dma_buffer-overrun.patch new file mode 100644 index 0000000..d0744ea --- /dev/null +++ b/SOURCES/1010-i2c-xgene-Avoid-dma_buffer-overrun.patch @@ -0,0 +1,32 @@ +From 24d0e4ddc132c6fc502c1d2465808fc007eb4dd1 Mon Sep 17 00:00:00 2001 +From: Hoan Tran hotran@apm.com +Date: Mon, 10 Oct 2016 10:13:10 -0700 +Subject: [PATCH 1010/1018] i2c: xgene: Avoid dma_buffer overrun + +SMBus block command uses the first byte of buffer for the data length. +The dma_buffer should be increased by 1 to avoid the overrun issue. + +Reported-by: Phil Endecott phil_gjouf_endecott@chezphil.org +Signed-off-by: Hoan Tran hotran@apm.com +Signed-off-by: Wolfram Sang wsa@the-dreams.de +Cc: stable@kernel.org +--- + drivers/i2c/busses/i2c-xgene-slimpro.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/i2c/busses/i2c-xgene-slimpro.c b/drivers/i2c/busses/i2c-xgene-slimpro.c +index 4233f56..3c38029 100644 +--- a/drivers/i2c/busses/i2c-xgene-slimpro.c ++++ b/drivers/i2c/busses/i2c-xgene-slimpro.c +@@ -105,7 +105,7 @@ struct slimpro_i2c_dev { + struct mbox_chan *mbox_chan; + struct mbox_client mbox_client; + struct completion rd_complete; +- u8 dma_buffer[I2C_SMBUS_BLOCK_MAX]; ++ u8 dma_buffer[I2C_SMBUS_BLOCK_MAX + 1]; /* dma_buffer[0] is used for length */ + u32 *resp_msg; + }; + +-- +1.8.3.1 + diff --git a/SPECS/kernel-aarch64.spec b/SPECS/kernel-aarch64.spec index 626eba4..84374b8 100644 --- a/SPECS/kernel-aarch64.spec +++ b/SPECS/kernel-aarch64.spec @@ -334,6 +334,7 @@ Patch1006: 1006-hwmon-xgene-Fix-crash-when-alarm-occurs-before-drive.patch Patch1007: 1007-perf-xgene-Add-APM-X-Gene-SoC-Performance-Monitoring.patch Patch1008: 1008-perf-xgene-Remove-bogus-IS_ERR-check.patch Patch1009: 1009-MAINTAINERS-Add-entry-for-APM-X-Gene-SoC-PMU-driver.patch +Patch1010: 1010-i2c-xgene-Avoid-dma_buffer-overrun.patch
# QDF2400 Patches Patch4000: 4000-arm64-Define-Qualcomm-Technologies-ARMv8-CPU.patch @@ -678,6 +679,7 @@ git am %{PATCH1006} git am %{PATCH1007} git am %{PATCH1008} git am %{PATCH1009} +git am %{PATCH1010}
# Apply QDF2400 patches git am %{PATCH4000} @@ -1457,6 +1459,7 @@ fi
%changelog * Thu Nov 10 2016 Duc Dang dhdang@apm.com [4.5.0-17.el7] +- Avoid dma_buffer overrun for SlimPRO I2C driver - Add X-Gene SoC PMU support - Add X-Gene HWMon support - mailbox: PCC: Fix return value of pcc_mbox_request_channel
Signed-off-by: Duc Dang dhdang@apm.com --- ...net-xgene-fix-error-handling-during-reset.patch | 72 +++++ ...-net-xgene-fix-backward-compatibility-fix.patch | 47 ++++ ...t-apm-xgene-use-phydev-from-struct-net_de.patch | 150 +++++++++++ ...014-drivers-net-xgene-Fix-MSS-programming.patch | 297 +++++++++++++++++++++ ...net-xgene-fix-Use-GPIO-to-get-link-status.patch | 91 +++++++ ...-xgene-fix-Disable-coalescing-on-v1-hardw.patch | 61 +++++ ...-xgene-fix-Coalescing-values-for-v2-hardw.patch | 67 +++++ SPECS/kernel-aarch64.spec | 15 ++ 8 files changed, 800 insertions(+) create mode 100644 SOURCES/1011-net-xgene-fix-error-handling-during-reset.patch create mode 100644 SOURCES/1012-net-xgene-fix-backward-compatibility-fix.patch create mode 100644 SOURCES/1013-net-ethernet-apm-xgene-use-phydev-from-struct-net_de.patch create mode 100644 SOURCES/1014-drivers-net-xgene-Fix-MSS-programming.patch create mode 100644 SOURCES/1015-drivers-net-xgene-fix-Use-GPIO-to-get-link-status.patch create mode 100644 SOURCES/1016-drivers-net-xgene-fix-Disable-coalescing-on-v1-hardw.patch create mode 100644 SOURCES/1017-drivers-net-xgene-fix-Coalescing-values-for-v2-hardw.patch
diff --git a/SOURCES/1011-net-xgene-fix-error-handling-during-reset.patch b/SOURCES/1011-net-xgene-fix-error-handling-during-reset.patch new file mode 100644 index 0000000..e10b74f --- /dev/null +++ b/SOURCES/1011-net-xgene-fix-error-handling-during-reset.patch @@ -0,0 +1,72 @@ +From 98ccf422fa923fe6d9288980faef4c1257387282 Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann arnd@arndb.de +Date: Fri, 26 Aug 2016 17:25:46 +0200 +Subject: [PATCH 1011/1018] net/xgene: fix error handling during reset + +The newly added reset logic uses helper functions for the MMIO that +may fail. However, when the read operation fails, we end up writing +back uninitialized data to the register, as gcc warns: + +drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c: In function 'xgene_enet_link_state': +drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c:213:2: error: 'data' may be used uninitialized in this function [-Werror=maybe-uninitialized] +drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c:209:6: note: 'data' was declared here + u32 data; + +We already print a warning to the console log if that happens, +the best alternative that I can see is skip the rest of the reset +sequence if the register value cannot be read: Most likely the +write would fail as well, and if it succeeded, worse things could +happen. + +Signed-off-by: Arnd Bergmann arnd@arndb.de +Fixes: 3eb7cb9dc946 ("drivers: net: xgene: XFI PCS reset when link is down") +Cc: Fushen Chen fchen@apm.com +Signed-off-by: David S. Miller davem@davemloft.net +--- + drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c +index d672e71..279ee27 100644 +--- a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c ++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c +@@ -155,19 +155,23 @@ static void xgene_enet_rd_mac(struct xgene_enet_pdata *pdata, + rd_addr); + } + +-static void xgene_enet_rd_pcs(struct xgene_enet_pdata *pdata, ++static bool xgene_enet_rd_pcs(struct xgene_enet_pdata *pdata, + u32 rd_addr, u32 *rd_data) + { + void __iomem *addr, *rd, *cmd, *cmd_done; ++ bool success; + + addr = pdata->pcs_addr + PCS_ADDR_REG_OFFSET; + rd = pdata->pcs_addr + PCS_READ_REG_OFFSET; + cmd = pdata->pcs_addr + PCS_COMMAND_REG_OFFSET; + cmd_done = pdata->pcs_addr + PCS_COMMAND_DONE_REG_OFFSET; + +- if (!xgene_enet_rd_indirect(addr, rd, cmd, cmd_done, rd_addr, rd_data)) ++ success = xgene_enet_rd_indirect(addr, rd, cmd, cmd_done, rd_addr, rd_data); ++ if (!success) + netdev_err(pdata->ndev, "PCS read failed, addr: %04x\n", + rd_addr); ++ ++ return success; + } + + static int xgene_enet_ecc_init(struct xgene_enet_pdata *pdata) +@@ -208,7 +212,9 @@ static void xgene_pcs_reset(struct xgene_enet_pdata *pdata) + { + u32 data; + +- xgene_enet_rd_pcs(pdata, PCS_CONTROL_1, &data); ++ if (!xgene_enet_rd_pcs(pdata, PCS_CONTROL_1, &data)) ++ return; ++ + xgene_enet_wr_pcs(pdata, PCS_CONTROL_1, data | PCS_CTRL_PCS_RST); + xgene_enet_wr_pcs(pdata, PCS_CONTROL_1, data & ~PCS_CTRL_PCS_RST); + } +-- +1.8.3.1 + diff --git a/SOURCES/1012-net-xgene-fix-backward-compatibility-fix.patch b/SOURCES/1012-net-xgene-fix-backward-compatibility-fix.patch new file mode 100644 index 0000000..c2a3712 --- /dev/null +++ b/SOURCES/1012-net-xgene-fix-backward-compatibility-fix.patch @@ -0,0 +1,47 @@ +From 76ba1e7db4a58d90d8ec9a81df4506913535da5a Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann arnd@arndb.de +Date: Mon, 29 Aug 2016 14:37:14 +0200 +Subject: [PATCH 1012/1018] net: xgene: fix backward compatibility fix + +A bugfix for backward compatibility handling introduced undefined +behavior for the case that of_parse_phandle() does not return +a valid entry, as "gcc -Wmaybe-unused" reports: + +drivers/net/ethernet/apm/xgene/xgene_enet_hw.c: In function 'xgene_enet_phy_connect': +drivers/net/ethernet/apm/xgene/xgene_enet_hw.c:776:6: error: 'phy_dev' may be used uninitialized in this function [-Werror=maybe-uninitialized] +drivers/net/ethernet/apm/xgene/xgene_enet_hw.c: In function 'xgene_enet_mdio_config': +drivers/net/ethernet/apm/xgene/xgene_enet_hw.c:776:6: error: 'phy_dev' may be used uninitialized in this function [-Werror=maybe-uninitialized] + +We can work around this by removing the check for zero "np", as +of_phy_connect() will correctly handle a NULL argument so we fall +back into the normal error handling case. + +Note that I had previously fixed another bug that resulted in the +exact same warning, but this is a different problem that was +introduced after my original fix. + +Signed-off-by: Arnd Bergmann arnd@arndb.de +Fixes: 03377e381bf4 ("drivers: net: xgene: Fix backward compatibility") +Signed-off-by: David S. Miller davem@davemloft.net +--- + drivers/net/ethernet/apm/xgene/xgene_enet_hw.c | 4 ---- + 1 file changed, 4 deletions(-) + +diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +index 126f631..8352334 100644 +--- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c ++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +@@ -762,10 +762,6 @@ int xgene_enet_phy_connect(struct net_device *ndev) + if (dev->of_node) { + for (i = 0 ; i < 2; i++) { + np = of_parse_phandle(dev->of_node, "phy-handle", i); +- +- if (!np) +- continue; +- + phy_dev = of_phy_connect(ndev, np, + &xgene_enet_adjust_link, + 0, pdata->phy_mode); +-- +1.8.3.1 + diff --git a/SOURCES/1013-net-ethernet-apm-xgene-use-phydev-from-struct-net_de.patch b/SOURCES/1013-net-ethernet-apm-xgene-use-phydev-from-struct-net_de.patch new file mode 100644 index 0000000..b057d0f --- /dev/null +++ b/SOURCES/1013-net-ethernet-apm-xgene-use-phydev-from-struct-net_de.patch @@ -0,0 +1,150 @@ +From a5088e0b96469357d3ccc4ae729f174b7dc9f671 Mon Sep 17 00:00:00 2001 +From: Philippe Reynes tremyfr@gmail.com +Date: Wed, 9 Nov 2016 18:19:05 -0600 +Subject: [PATCH 1013/1018] net: ethernet: apm: xgene: use phydev from struct + net_device + +The private structure contain a pointer to phydev, but the structure +net_device already contain such pointer. So we can remove the pointer +phy_dev in the private structure, and update the driver to use the +one contained in struct net_device. + +Signed-off-by: Philippe Reynes tremyfr@gmail.com +Signed-off-by: David S. Miller davem@davemloft.net + +[Apply from upstream commit 4.8 to CentOS 7.3 AltArch] +Signed-off-by: Duc Dang dhdang@apm.com +--- + .../net/ethernet/apm/xgene/xgene_enet_ethtool.c | 4 ++-- + drivers/net/ethernet/apm/xgene/xgene_enet_hw.c | 24 +++++++++++----------- + drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 8 ++++---- + drivers/net/ethernet/apm/xgene/xgene_enet_main.h | 1 - + 4 files changed, 18 insertions(+), 19 deletions(-) + +diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_ethtool.c b/drivers/net/ethernet/apm/xgene/xgene_enet_ethtool.c +index 22a7b26..e1f44ae 100644 +--- a/drivers/net/ethernet/apm/xgene/xgene_enet_ethtool.c ++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_ethtool.c +@@ -57,7 +57,7 @@ static void xgene_get_drvinfo(struct net_device *ndev, + static int xgene_get_settings(struct net_device *ndev, struct ethtool_cmd *cmd) + { + struct xgene_enet_pdata *pdata = netdev_priv(ndev); +- struct phy_device *phydev = pdata->phy_dev; ++ struct phy_device *phydev = ndev->phydev; + + if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII) { + if (phydev == NULL) +@@ -96,7 +96,7 @@ static int xgene_get_settings(struct net_device *ndev, struct ethtool_cmd *cmd) + static int xgene_set_settings(struct net_device *ndev, struct ethtool_cmd *cmd) + { + struct xgene_enet_pdata *pdata = netdev_priv(ndev); +- struct phy_device *phydev = pdata->phy_dev; ++ struct phy_device *phydev = ndev->phydev; + + if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII) { + if (!phydev) +diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +index 8352334..36b478f 100644 +--- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c ++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +@@ -713,7 +713,7 @@ static void xgene_enet_adjust_link(struct net_device *ndev) + { + struct xgene_enet_pdata *pdata = netdev_priv(ndev); + const struct xgene_mac_ops *mac_ops = pdata->mac_ops; +- struct phy_device *phydev = pdata->phy_dev; ++ struct phy_device *phydev = ndev->phydev; + + if (phydev->link) { + if (pdata->phy_speed != phydev->speed) { +@@ -774,15 +774,13 @@ int xgene_enet_phy_connect(struct net_device *ndev) + netdev_err(ndev, "Could not connect to PHY\n"); + return -ENODEV; + } +- +- pdata->phy_dev = phy_dev; + } else { + #ifdef CONFIG_ACPI + adev = acpi_phy_find_device(dev); + if (adev) +- pdata->phy_dev = adev->driver_data; +- +- phy_dev = pdata->phy_dev; ++ phy_dev = adev->driver_data; ++ else ++ phy_dev = NULL; + + if (!phy_dev || + phy_connect_direct(ndev, phy_dev, &xgene_enet_adjust_link, +@@ -848,8 +846,6 @@ static int xgene_mdiobus_register(struct xgene_enet_pdata *pdata, + if (!phy) + return -EIO; + +- pdata->phy_dev = phy; +- + return ret; + } + +@@ -889,14 +885,18 @@ int xgene_enet_mdio_config(struct xgene_enet_pdata *pdata) + + void xgene_enet_phy_disconnect(struct xgene_enet_pdata *pdata) + { +- if (pdata->phy_dev) +- phy_disconnect(pdata->phy_dev); ++ struct net_device *ndev = pdata->ndev; ++ ++ if (ndev->phydev) ++ phy_disconnect(ndev->phydev); + } + + void xgene_enet_mdio_remove(struct xgene_enet_pdata *pdata) + { +- if (pdata->phy_dev) +- phy_disconnect(pdata->phy_dev); ++ struct net_device *ndev = pdata->ndev; ++ ++ if (ndev->phydev) ++ phy_disconnect(ndev->phydev); + + mdiobus_unregister(pdata->mdio_bus); + mdiobus_free(pdata->mdio_bus); +diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +index b8b9495..522ba92 100644 +--- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c ++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +@@ -748,8 +748,8 @@ static int xgene_enet_open(struct net_device *ndev) + if (ret) + return ret; + +- if (pdata->phy_dev) { +- phy_start(pdata->phy_dev); ++ if (ndev->phydev) { ++ phy_start(ndev->phydev); + } else { + schedule_delayed_work(&pdata->link_work, PHY_POLL_LINK_OFF); + netif_carrier_off(ndev); +@@ -772,8 +772,8 @@ static int xgene_enet_close(struct net_device *ndev) + mac_ops->tx_disable(pdata); + mac_ops->rx_disable(pdata); + +- if (pdata->phy_dev) +- phy_stop(pdata->phy_dev); ++ if (ndev->phydev) ++ phy_stop(ndev->phydev); + else + cancel_delayed_work_sync(&pdata->link_work); + +diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h +index b339fc1..7735371 100644 +--- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h ++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h +@@ -174,7 +174,6 @@ struct xgene_cle_ops { + struct xgene_enet_pdata { + struct net_device *ndev; + struct mii_bus *mdio_bus; +- struct phy_device *phy_dev; + int phy_speed; + struct clk *clk; + struct platform_device *pdev; +-- +1.8.3.1 + diff --git a/SOURCES/1014-drivers-net-xgene-Fix-MSS-programming.patch b/SOURCES/1014-drivers-net-xgene-Fix-MSS-programming.patch new file mode 100644 index 0000000..2954784 --- /dev/null +++ b/SOURCES/1014-drivers-net-xgene-Fix-MSS-programming.patch @@ -0,0 +1,297 @@ +From 96703b9e54070f674bb14001c6928d19537c33d1 Mon Sep 17 00:00:00 2001 +From: Iyappan Subramanian isubramanian@apm.com +Date: Thu, 22 Sep 2016 15:47:33 -0700 +Subject: [PATCH 1014/1018] drivers: net: xgene: Fix MSS programming + +Current driver programs static value of MSS in hardware register for TSO +offload engine to segment the TCP payload regardless the MSS value +provided by network stack. + +This patch fixes this by programming hardware registers with the +stack provided MSS value. + +Since the hardware has the limitation of having only 4 MSS registers, +this patch uses reference count of mss values being used. + +Signed-off-by: Iyappan Subramanian isubramanian@apm.com +Signed-off-by: Toan Le toanle@apm.com +Signed-off-by: David S. Miller davem@davemloft.net +--- + drivers/net/ethernet/apm/xgene/xgene_enet_hw.h | 7 ++ + drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 90 ++++++++++++++++++----- + drivers/net/ethernet/apm/xgene/xgene_enet_main.h | 8 +- + drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c | 18 ++++- + 4 files changed, 100 insertions(+), 23 deletions(-) + +diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h +index 8a8d055..8456337 100644 +--- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h ++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h +@@ -237,6 +237,8 @@ enum xgene_enet_rm { + #define TCPHDR_LEN 6 + #define IPHDR_POS 6 + #define IPHDR_LEN 6 ++#define MSS_POS 20 ++#define MSS_LEN 2 + #define EC_POS 22 /* Enable checksum */ + #define EC_LEN 1 + #define ET_POS 23 /* Enable TSO */ +@@ -253,6 +255,11 @@ enum xgene_enet_rm { + + #define LAST_BUFFER (0x7800ULL << BUFDATALEN_POS) + ++#define TSO_MSS0_POS 0 ++#define TSO_MSS0_LEN 14 ++#define TSO_MSS1_POS 16 ++#define TSO_MSS1_LEN 14 ++ + struct xgene_enet_raw_desc { + __le64 m0; + __le64 m1; +diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +index 522ba92..429f18f 100644 +--- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c ++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +@@ -137,6 +137,7 @@ static irqreturn_t xgene_enet_rx_irq(const int irq, void *data) + static int xgene_enet_tx_completion(struct xgene_enet_desc_ring *cp_ring, + struct xgene_enet_raw_desc *raw_desc) + { ++ struct xgene_enet_pdata *pdata = netdev_priv(cp_ring->ndev); + struct sk_buff *skb; + struct device *dev; + skb_frag_t *frag; +@@ -144,6 +145,7 @@ static int xgene_enet_tx_completion(struct xgene_enet_desc_ring *cp_ring, + u16 skb_index; + u8 status; + int i, ret = 0; ++ u8 mss_index; + + skb_index = GET_VAL(USERINFO, le64_to_cpu(raw_desc->m0)); + skb = cp_ring->cp_skb[skb_index]; +@@ -160,6 +162,13 @@ static int xgene_enet_tx_completion(struct xgene_enet_desc_ring *cp_ring, + DMA_TO_DEVICE); + } + ++ if (GET_BIT(ET, le64_to_cpu(raw_desc->m3))) { ++ mss_index = GET_VAL(MSS, le64_to_cpu(raw_desc->m3)); ++ spin_lock(&pdata->mss_lock); ++ pdata->mss_refcnt[mss_index]--; ++ spin_unlock(&pdata->mss_lock); ++ } ++ + /* Checking for error */ + status = GET_VAL(LERR, le64_to_cpu(raw_desc->m0)); + if (unlikely(status > 2)) { +@@ -178,15 +187,53 @@ static int xgene_enet_tx_completion(struct xgene_enet_desc_ring *cp_ring, + return ret; + } + +-static u64 xgene_enet_work_msg(struct sk_buff *skb) ++static int xgene_enet_setup_mss(struct net_device *ndev, u32 mss) ++{ ++ struct xgene_enet_pdata *pdata = netdev_priv(ndev); ++ bool mss_index_found = false; ++ int mss_index; ++ int i; ++ ++ spin_lock(&pdata->mss_lock); ++ ++ /* Reuse the slot if MSS matches */ ++ for (i = 0; !mss_index_found && i < NUM_MSS_REG; i++) { ++ if (pdata->mss[i] == mss) { ++ pdata->mss_refcnt[i]++; ++ mss_index = i; ++ mss_index_found = true; ++ } ++ } ++ ++ /* Overwrite the slot with ref_count = 0 */ ++ for (i = 0; !mss_index_found && i < NUM_MSS_REG; i++) { ++ if (!pdata->mss_refcnt[i]) { ++ pdata->mss_refcnt[i]++; ++ pdata->mac_ops->set_mss(pdata, mss, i); ++ pdata->mss[i] = mss; ++ mss_index = i; ++ mss_index_found = true; ++ } ++ } ++ ++ spin_unlock(&pdata->mss_lock); ++ ++ /* No slots with ref_count = 0 available, return busy */ ++ if (!mss_index_found) ++ return -EBUSY; ++ ++ return mss_index; ++} ++ ++static int xgene_enet_work_msg(struct sk_buff *skb, u64 *hopinfo) + { + struct net_device *ndev = skb->dev; + struct iphdr *iph; + u8 l3hlen = 0, l4hlen = 0; + u8 ethhdr, proto = 0, csum_enable = 0; +- u64 hopinfo = 0; + u32 hdr_len, mss = 0; + u32 i, len, nr_frags; ++ int mss_index; + + ethhdr = xgene_enet_hdr_len(skb->data); + +@@ -226,7 +273,11 @@ static u64 xgene_enet_work_msg(struct sk_buff *skb) + if (!mss || ((skb->len - hdr_len) <= mss)) + goto out; + +- hopinfo |= SET_BIT(ET); ++ mss_index = xgene_enet_setup_mss(ndev, mss); ++ if (unlikely(mss_index < 0)) ++ return -EBUSY; ++ ++ *hopinfo |= SET_BIT(ET) | SET_VAL(MSS, mss_index); + } + } else if (iph->protocol == IPPROTO_UDP) { + l4hlen = UDP_HDR_SIZE; +@@ -234,15 +285,15 @@ static u64 xgene_enet_work_msg(struct sk_buff *skb) + } + out: + l3hlen = ip_hdrlen(skb) >> 2; +- hopinfo |= SET_VAL(TCPHDR, l4hlen) | +- SET_VAL(IPHDR, l3hlen) | +- SET_VAL(ETHHDR, ethhdr) | +- SET_VAL(EC, csum_enable) | +- SET_VAL(IS, proto) | +- SET_BIT(IC) | +- SET_BIT(TYPE_ETH_WORK_MESSAGE); +- +- return hopinfo; ++ *hopinfo |= SET_VAL(TCPHDR, l4hlen) | ++ SET_VAL(IPHDR, l3hlen) | ++ SET_VAL(ETHHDR, ethhdr) | ++ SET_VAL(EC, csum_enable) | ++ SET_VAL(IS, proto) | ++ SET_BIT(IC) | ++ SET_BIT(TYPE_ETH_WORK_MESSAGE); ++ ++ return 0; + } + + static u16 xgene_enet_encode_len(u16 len) +@@ -282,20 +333,22 @@ static int xgene_enet_setup_tx_desc(struct xgene_enet_desc_ring *tx_ring, + dma_addr_t dma_addr, pbuf_addr, *frag_dma_addr; + skb_frag_t *frag; + u16 tail = tx_ring->tail; +- u64 hopinfo; ++ u64 hopinfo = 0; + u32 len, hw_len; + u8 ll = 0, nv = 0, idx = 0; + bool split = false; + u32 size, offset, ell_bytes = 0; + u32 i, fidx, nr_frags, count = 1; ++ int ret; + + raw_desc = &tx_ring->raw_desc[tail]; + tail = (tail + 1) & (tx_ring->slots - 1); + memset(raw_desc, 0, sizeof(struct xgene_enet_raw_desc)); + +- hopinfo = xgene_enet_work_msg(skb); +- if (!hopinfo) +- return -EINVAL; ++ ret = xgene_enet_work_msg(skb, &hopinfo); ++ if (ret) ++ return ret; ++ + raw_desc->m3 = cpu_to_le64(SET_VAL(HENQNUM, tx_ring->dst_ring_num) | + hopinfo); + +@@ -435,6 +488,9 @@ static netdev_tx_t xgene_enet_start_xmit(struct sk_buff *skb, + return NETDEV_TX_OK; + + count = xgene_enet_setup_tx_desc(tx_ring, skb); ++ if (count == -EBUSY) ++ return NETDEV_TX_BUSY; ++ + if (count <= 0) { + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; +@@ -1669,7 +1725,7 @@ static int xgene_enet_probe(struct platform_device *pdev) + + if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII) { + ndev->features |= NETIF_F_TSO; +- pdata->mss = XGENE_ENET_MSS; ++ spin_lock_init(&pdata->mss_lock); + } + ndev->hw_features = ndev->features; + +diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h +index 7735371..0cda58f 100644 +--- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h ++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h +@@ -47,7 +47,7 @@ + #define NUM_PKT_BUF 64 + #define NUM_BUFPOOL 32 + #define MAX_EXP_BUFFS 256 +-#define XGENE_ENET_MSS 1448 ++#define NUM_MSS_REG 4 + #define XGENE_MIN_ENET_FRAME_SIZE 60 + + #define XGENE_MAX_ENET_IRQ 16 +@@ -143,7 +143,7 @@ struct xgene_mac_ops { + void (*rx_disable)(struct xgene_enet_pdata *pdata); + void (*set_speed)(struct xgene_enet_pdata *pdata); + void (*set_mac_addr)(struct xgene_enet_pdata *pdata); +- void (*set_mss)(struct xgene_enet_pdata *pdata); ++ void (*set_mss)(struct xgene_enet_pdata *pdata, u16 mss, u8 index); + void (*link_state)(struct work_struct *work); + }; + +@@ -212,7 +212,9 @@ struct xgene_enet_pdata { + u8 eth_bufnum; + u8 bp_bufnum; + u16 ring_num; +- u32 mss; ++ u32 mss[NUM_MSS_REG]; ++ u32 mss_refcnt[NUM_MSS_REG]; ++ spinlock_t mss_lock; /* mss lock */ + u8 tx_delay; + u8 rx_delay; + bool mdio_driver; +diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c +index 279ee27..6475f38 100644 +--- a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c ++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c +@@ -232,9 +232,22 @@ static void xgene_xgmac_set_mac_addr(struct xgene_enet_pdata *pdata) + xgene_enet_wr_mac(pdata, HSTMACADR_MSW_ADDR, addr1); + } + +-static void xgene_xgmac_set_mss(struct xgene_enet_pdata *pdata) ++static void xgene_xgmac_set_mss(struct xgene_enet_pdata *pdata, ++ u16 mss, u8 index) + { +- xgene_enet_wr_csr(pdata, XG_TSIF_MSS_REG0_ADDR, pdata->mss); ++ u8 offset; ++ u32 data; ++ ++ offset = (index < 2) ? 0 : 4; ++ xgene_enet_rd_csr(pdata, XG_TSIF_MSS_REG0_ADDR + offset, &data); ++ ++ if (!(index & 0x1)) ++ data = SET_VAL(TSO_MSS1, data >> TSO_MSS1_POS) | ++ SET_VAL(TSO_MSS0, mss); ++ else ++ data = SET_VAL(TSO_MSS1, mss) | SET_VAL(TSO_MSS0, data); ++ ++ xgene_enet_wr_csr(pdata, XG_TSIF_MSS_REG0_ADDR + offset, data); + } + + static u32 xgene_enet_link_status(struct xgene_enet_pdata *pdata) +@@ -258,7 +271,6 @@ static void xgene_xgmac_init(struct xgene_enet_pdata *pdata) + xgene_enet_wr_mac(pdata, AXGMAC_CONFIG_1, data); + + xgene_xgmac_set_mac_addr(pdata); +- xgene_xgmac_set_mss(pdata); + + xgene_enet_rd_csr(pdata, XG_RSIF_CONFIG_REG_ADDR, &data); + data |= CFG_RSIF_FPBUFF_TIMEOUT_EN; +-- +1.8.3.1 + diff --git a/SOURCES/1015-drivers-net-xgene-fix-Use-GPIO-to-get-link-status.patch b/SOURCES/1015-drivers-net-xgene-fix-Use-GPIO-to-get-link-status.patch new file mode 100644 index 0000000..5588ac5 --- /dev/null +++ b/SOURCES/1015-drivers-net-xgene-fix-Use-GPIO-to-get-link-status.patch @@ -0,0 +1,91 @@ +From 410b195f44efaaa3a42f6ff0fc5ffaa02ce934d5 Mon Sep 17 00:00:00 2001 +From: Iyappan Subramanian isubramanian@apm.com +Date: Thu, 6 Oct 2016 14:35:57 -0700 +Subject: [PATCH 1015/1018] drivers: net: xgene: fix: Use GPIO to get link + status + +The link value reported by the link status register is not +reliable when no SPF module inserted. This patchset fixes this +issue by using GPIO to determine the link status. + +Signed-off-by: Iyappan Subramanian isubramanian@apm.com +Signed-off-by: Quan Nguyen qnguyen@apm.com +Signed-off-by: David S. Miller davem@davemloft.net +--- + drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 6 +++++- + drivers/net/ethernet/apm/xgene/xgene_enet_main.h | 1 + + drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c | 19 +++++++++++++++++-- + 3 files changed, 23 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +index 429f18f..f75d955 100644 +--- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c ++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +@@ -1381,9 +1381,13 @@ static void xgene_enet_gpiod_get(struct xgene_enet_pdata *pdata) + { + struct device *dev = &pdata->pdev->dev; + +- if (pdata->phy_mode != PHY_INTERFACE_MODE_XGMII) ++ pdata->sfp_gpio_en = false; ++ if (pdata->phy_mode != PHY_INTERFACE_MODE_XGMII || ++ (!device_property_present(dev, "sfp-gpios") && ++ !device_property_present(dev, "rxlos-gpios"))) + return; + ++ pdata->sfp_gpio_en = true; + pdata->sfp_rdy = gpiod_get(dev, "rxlos", GPIOD_IN); + if (IS_ERR(pdata->sfp_rdy)) + pdata->sfp_rdy = gpiod_get(dev, "sfp", GPIOD_IN); +diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h +index 0cda58f..011965b 100644 +--- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h ++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h +@@ -219,6 +219,7 @@ struct xgene_enet_pdata { + u8 rx_delay; + bool mdio_driver; + struct gpio_desc *sfp_rdy; ++ bool sfp_gpio_en; + }; + + struct xgene_indirect_ctl { +diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c +index 6475f38..d1758b0 100644 +--- a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c ++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c +@@ -415,16 +415,31 @@ static void xgene_enet_clear(struct xgene_enet_pdata *pdata, + xgene_enet_wr_ring_if(pdata, addr, data); + } + ++static int xgene_enet_gpio_lookup(struct xgene_enet_pdata *pdata) ++{ ++ struct device *dev = &pdata->pdev->dev; ++ ++ pdata->sfp_rdy = gpiod_get(dev, "rxlos", GPIOD_IN); ++ if (IS_ERR(pdata->sfp_rdy)) ++ pdata->sfp_rdy = gpiod_get(dev, "sfp", GPIOD_IN); ++ ++ if (IS_ERR(pdata->sfp_rdy)) ++ return -ENODEV; ++ ++ return 0; ++} ++ + static void xgene_enet_link_state(struct work_struct *work) + { + struct xgene_enet_pdata *pdata = container_of(to_delayed_work(work), + struct xgene_enet_pdata, link_work); +- struct gpio_desc *sfp_rdy = pdata->sfp_rdy; + struct net_device *ndev = pdata->ndev; + u32 link_status, poll_interval; + + link_status = xgene_enet_link_status(pdata); +- if (link_status && !IS_ERR(sfp_rdy) && !gpiod_get_value(sfp_rdy)) ++ if (pdata->sfp_gpio_en && link_status && ++ (!IS_ERR(pdata->sfp_rdy) || !xgene_enet_gpio_lookup(pdata)) && ++ !gpiod_get_value(pdata->sfp_rdy)) + link_status = 0; + + if (link_status) { +-- +1.8.3.1 + diff --git a/SOURCES/1016-drivers-net-xgene-fix-Disable-coalescing-on-v1-hardw.patch b/SOURCES/1016-drivers-net-xgene-fix-Disable-coalescing-on-v1-hardw.patch new file mode 100644 index 0000000..5da6d49 --- /dev/null +++ b/SOURCES/1016-drivers-net-xgene-fix-Disable-coalescing-on-v1-hardw.patch @@ -0,0 +1,61 @@ +From b125c9d6439cce38fa6938c7b31ab2ed0e17dee6 Mon Sep 17 00:00:00 2001 +From: Iyappan Subramanian isubramanian@apm.com +Date: Mon, 31 Oct 2016 16:00:26 -0700 +Subject: [PATCH 1016/1018] drivers: net: xgene: fix: Disable coalescing on v1 + hardware + +Since ethernet v1 hardware has a bug related to coalescing, disabling +this feature. + +Signed-off-by: Iyappan Subramanian isubramanian@apm.com +Signed-off-by: Toan Le toanle@apm.com +--- + drivers/net/ethernet/apm/xgene/xgene_enet_hw.c | 12 ------------ + drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 3 ++- + 2 files changed, 2 insertions(+), 13 deletions(-) + +diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +index 36b478f..85ed9a1 100644 +--- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c ++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +@@ -204,17 +204,6 @@ static u32 xgene_enet_ring_len(struct xgene_enet_desc_ring *ring) + return num_msgs; + } + +-static void xgene_enet_setup_coalescing(struct xgene_enet_desc_ring *ring) +-{ +- u32 data = 0x7777; +- +- xgene_enet_ring_wr32(ring, CSR_PBM_COAL, 0x8e); +- xgene_enet_ring_wr32(ring, CSR_PBM_CTICK1, data); +- xgene_enet_ring_wr32(ring, CSR_PBM_CTICK2, data << 16); +- xgene_enet_ring_wr32(ring, CSR_THRESHOLD0_SET1, 0x40); +- xgene_enet_ring_wr32(ring, CSR_THRESHOLD1_SET1, 0x80); +-} +- + void xgene_enet_parse_error(struct xgene_enet_desc_ring *ring, + struct xgene_enet_pdata *pdata, + enum xgene_enet_err_code status) +@@ -928,5 +917,4 @@ struct xgene_ring_ops xgene_ring1_ops = { + .clear = xgene_enet_clear_ring, + .wr_cmd = xgene_enet_wr_cmd, + .len = xgene_enet_ring_len, +- .coalesce = xgene_enet_setup_coalescing, + }; +diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +index f75d955..fe34403 100644 +--- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c ++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +@@ -1188,7 +1188,8 @@ static int xgene_enet_create_desc_rings(struct net_device *ndev) + tx_ring->dst_ring_num = xgene_enet_dst_ring_num(cp_ring); + } + +- pdata->ring_ops->coalesce(pdata->tx_ring[0]); ++ if (pdata->ring_ops->coalesce) ++ pdata->ring_ops->coalesce(pdata->tx_ring[0]); + pdata->tx_qcnt_hi = pdata->tx_ring[0]->slots - 128; + + return 0; +-- +1.8.3.1 + diff --git a/SOURCES/1017-drivers-net-xgene-fix-Coalescing-values-for-v2-hardw.patch b/SOURCES/1017-drivers-net-xgene-fix-Coalescing-values-for-v2-hardw.patch new file mode 100644 index 0000000..79203da --- /dev/null +++ b/SOURCES/1017-drivers-net-xgene-fix-Coalescing-values-for-v2-hardw.patch @@ -0,0 +1,67 @@ +From da20928c43a4f6b9390a0fb027dda7d947011686 Mon Sep 17 00:00:00 2001 +From: Iyappan Subramanian isubramanian@apm.com +Date: Mon, 31 Oct 2016 16:00:27 -0700 +Subject: [PATCH 1017/1018] drivers: net: xgene: fix: Coalescing values for v2 + hardware + +Changing the interrupt trigger region id to 2 and the +corresponding threshold set0/set1 values to 8/16. + +Signed-off-by: Iyappan Subramanian isubramanian@apm.com +Signed-off-by: Toan Le toanle@apm.com +--- + drivers/net/ethernet/apm/xgene/xgene_enet_hw.h | 2 ++ + drivers/net/ethernet/apm/xgene/xgene_enet_ring2.c | 12 +++++++----- + 2 files changed, 9 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h +index 8456337..06e598c 100644 +--- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h ++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h +@@ -55,8 +55,10 @@ enum xgene_enet_rm { + #define PREFETCH_BUF_EN BIT(21) + #define CSR_RING_ID_BUF 0x000c + #define CSR_PBM_COAL 0x0014 ++#define CSR_PBM_CTICK0 0x0018 + #define CSR_PBM_CTICK1 0x001c + #define CSR_PBM_CTICK2 0x0020 ++#define CSR_PBM_CTICK3 0x0024 + #define CSR_THRESHOLD0_SET1 0x0030 + #define CSR_THRESHOLD1_SET1 0x0034 + #define CSR_RING_NE_INT_MODE 0x017c +diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_ring2.c b/drivers/net/ethernet/apm/xgene/xgene_enet_ring2.c +index 2b76732..af51dd5 100644 +--- a/drivers/net/ethernet/apm/xgene/xgene_enet_ring2.c ++++ b/drivers/net/ethernet/apm/xgene/xgene_enet_ring2.c +@@ -30,7 +30,7 @@ static void xgene_enet_ring_init(struct xgene_enet_desc_ring *ring) + ring_cfg[0] |= SET_VAL(X2_INTLINE, ring->id & RING_BUFNUM_MASK); + ring_cfg[3] |= SET_BIT(X2_DEQINTEN); + } +- ring_cfg[0] |= SET_VAL(X2_CFGCRID, 1); ++ ring_cfg[0] |= SET_VAL(X2_CFGCRID, 2); + + addr >>= 8; + ring_cfg[2] |= QCOHERENT | SET_VAL(RINGADDRL, addr); +@@ -192,13 +192,15 @@ static u32 xgene_enet_ring_len(struct xgene_enet_desc_ring *ring) + + static void xgene_enet_setup_coalescing(struct xgene_enet_desc_ring *ring) + { +- u32 data = 0x7777; ++ u32 data = 0x77777777; + + xgene_enet_ring_wr32(ring, CSR_PBM_COAL, 0x8e); ++ xgene_enet_ring_wr32(ring, CSR_PBM_CTICK0, data); + xgene_enet_ring_wr32(ring, CSR_PBM_CTICK1, data); +- xgene_enet_ring_wr32(ring, CSR_PBM_CTICK2, data << 16); +- xgene_enet_ring_wr32(ring, CSR_THRESHOLD0_SET1, 0x40); +- xgene_enet_ring_wr32(ring, CSR_THRESHOLD1_SET1, 0x80); ++ xgene_enet_ring_wr32(ring, CSR_PBM_CTICK2, data); ++ xgene_enet_ring_wr32(ring, CSR_PBM_CTICK3, data); ++ xgene_enet_ring_wr32(ring, CSR_THRESHOLD0_SET1, 0x08); ++ xgene_enet_ring_wr32(ring, CSR_THRESHOLD1_SET1, 0x10); + } + + struct xgene_ring_ops xgene_ring2_ops = { +-- +1.8.3.1 + diff --git a/SPECS/kernel-aarch64.spec b/SPECS/kernel-aarch64.spec index 84374b8..c44bdc7 100644 --- a/SPECS/kernel-aarch64.spec +++ b/SPECS/kernel-aarch64.spec @@ -335,6 +335,13 @@ Patch1007: 1007-perf-xgene-Add-APM-X-Gene-SoC-Performance-Monitoring.patch Patch1008: 1008-perf-xgene-Remove-bogus-IS_ERR-check.patch Patch1009: 1009-MAINTAINERS-Add-entry-for-APM-X-Gene-SoC-PMU-driver.patch Patch1010: 1010-i2c-xgene-Avoid-dma_buffer-overrun.patch +Patch1011: 1011-net-xgene-fix-error-handling-during-reset.patch +Patch1012: 1012-net-xgene-fix-backward-compatibility-fix.patch +Patch1013: 1013-net-ethernet-apm-xgene-use-phydev-from-struct-net_de.patch +Patch1014: 1014-drivers-net-xgene-Fix-MSS-programming.patch +Patch1015: 1015-drivers-net-xgene-fix-Use-GPIO-to-get-link-status.patch +Patch1016: 1016-drivers-net-xgene-fix-Disable-coalescing-on-v1-hardw.patch +Patch1017: 1017-drivers-net-xgene-fix-Coalescing-values-for-v2-hardw.patch
# QDF2400 Patches Patch4000: 4000-arm64-Define-Qualcomm-Technologies-ARMv8-CPU.patch @@ -680,6 +687,13 @@ git am %{PATCH1007} git am %{PATCH1008} git am %{PATCH1009} git am %{PATCH1010} +git am %{PATCH1011} +git am %{PATCH1012} +git am %{PATCH1013} +git am %{PATCH1014} +git am %{PATCH1015} +git am %{PATCH1016} +git am %{PATCH1017}
# Apply QDF2400 patches git am %{PATCH4000} @@ -1459,6 +1473,7 @@ fi
%changelog * Thu Nov 10 2016 Duc Dang dhdang@apm.com [4.5.0-17.el7] +- Integrate upstream updates for X-Gene Enet driver - Avoid dma_buffer overrun for SlimPRO I2C driver - Add X-Gene SoC PMU support - Add X-Gene HWMon support
Signed-off-by: Duc Dang dhdang@apm.com --- ...ix-GIC-irq-model-default-PCI-IRQ-polarity.patch | 83 ++++++++++++++++++++++ SPECS/kernel-aarch64.spec | 3 + 2 files changed, 86 insertions(+) create mode 100644 SOURCES/1018-ACPI-PCI-fix-GIC-irq-model-default-PCI-IRQ-polarity.patch
diff --git a/SOURCES/1018-ACPI-PCI-fix-GIC-irq-model-default-PCI-IRQ-polarity.patch b/SOURCES/1018-ACPI-PCI-fix-GIC-irq-model-default-PCI-IRQ-polarity.patch new file mode 100644 index 0000000..22ebebe --- /dev/null +++ b/SOURCES/1018-ACPI-PCI-fix-GIC-irq-model-default-PCI-IRQ-polarity.patch @@ -0,0 +1,83 @@ +From 40cbfb67ef9e9f8dc1813447aebb860572a8d414 Mon Sep 17 00:00:00 2001 +From: Lorenzo Pieralisi lorenzo.pieralisi@arm.com +Date: Mon, 5 Sep 2016 15:12:38 +0100 +Subject: [PATCH 1018/1018] ACPI / PCI: fix GIC irq model default PCI IRQ + polarity + +On ACPI ARM based systems the GIC interrupt controller +and corresponding interrupt model permit only the high +polarity for level interrupts. + +ACPI firmware describes PCI legacy IRQs through entries +in the _PRT objects. Entries in the _PRT can be of two types: + +- Static: not configurable, trigger/polarity default to level-low, + _PRT entry defines the global GSI interrupt number +- Configurable: _PRT interrupt entry contains a reference to the + corresponding PCI interrupt link device (that in turn provides the + interrupt descriptor through its _CRS/_PRS methods) + +Configurable IRQ entries are not currently allowed by the ACPI +specification on ARM since they can only be used for interrupt pins that +are routable, as per ACPI specifications (version 6.1, 6.2.13): + +"[...] There are two ways that _PRT can be used. Typically, the +interrupt input that a given PCI interrupt is on is configurable. For +example, a given PCI interrupt might be configured for either IRQ 10 or +11 on an 8259 interrupt controller. In this model, each interrupt is +represented in the ACPI namespace as a PCI Interrupt Link Device. [...]" + +ARM platforms GIC configurations do not allow dynamic IRQ routing, +since routing is statically laid out at synthesis time; therefore PCI +interrupt links cannot be used for PCI legacy IRQ descriptions in the +_PRT on ARM systems. + +On the other hand, current core ACPI code handling PCI legacy IRQs +consider IRQ trigger/polarity for static _PRT entries as level-low. + +On ARM systems with a GIC interrupt controller and corresponding +ACPI interrupt model this does not work in that GIC interrupt +controller is only capable of handling level interrupts whose +polarity is high (for PCI legacy IRQs - that are level-low by +specification - this means that the legacy IRQs are inverted before +reaching the interrupt controller pin), resulting in IRQ allocation +failures such as: + +genirq: Setting trigger mode 8 for irq 18 failed (gic_set_type+0x0/0x48) + +Change the default polarity for PCI legacy IRQs to high on systems +booting wth ACPI on platforms with a GIC interrupt controller model, +fixing the discrepancy between specification and HW behaviour. + +Signed-off-by: Lorenzo Pieralisi lorenzo.pieralisi@arm.com +Acked-by: Marc Zyngier marc.zyngier@arm.com +Tested-by: Duc Dang dhdang@apm.com +Signed-off-by: Rafael J. Wysocki rafael.j.wysocki@intel.com +--- + drivers/acpi/pci_irq.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c +index c8e169e..a40b8dd 100644 +--- a/drivers/acpi/pci_irq.c ++++ b/drivers/acpi/pci_irq.c +@@ -393,7 +393,15 @@ int acpi_pci_irq_enable(struct pci_dev *dev) + int gsi; + u8 pin; + int triggering = ACPI_LEVEL_SENSITIVE; +- int polarity = ACPI_ACTIVE_LOW; ++ /* ++ * On ARM systems with the GIC interrupt model, level interrupts ++ * are always polarity high by specification; PCI legacy ++ * IRQs lines are inverted before reaching the interrupt ++ * controller and must therefore be considered active high ++ * as default. ++ */ ++ int polarity = acpi_irq_model == ACPI_IRQ_MODEL_GIC ? ++ ACPI_ACTIVE_HIGH : ACPI_ACTIVE_LOW; + char *link = NULL; + char link_desc[16]; + int rc; +-- +1.8.3.1 + diff --git a/SPECS/kernel-aarch64.spec b/SPECS/kernel-aarch64.spec index c44bdc7..4c20b21 100644 --- a/SPECS/kernel-aarch64.spec +++ b/SPECS/kernel-aarch64.spec @@ -342,6 +342,7 @@ Patch1014: 1014-drivers-net-xgene-Fix-MSS-programming.patch Patch1015: 1015-drivers-net-xgene-fix-Use-GPIO-to-get-link-status.patch Patch1016: 1016-drivers-net-xgene-fix-Disable-coalescing-on-v1-hardw.patch Patch1017: 1017-drivers-net-xgene-fix-Coalescing-values-for-v2-hardw.patch +Patch1018: 1018-ACPI-PCI-fix-GIC-irq-model-default-PCI-IRQ-polarity.patch
# QDF2400 Patches Patch4000: 4000-arm64-Define-Qualcomm-Technologies-ARMv8-CPU.patch @@ -694,6 +695,7 @@ git am %{PATCH1014} git am %{PATCH1015} git am %{PATCH1016} git am %{PATCH1017} +git am %{PATCH1018}
# Apply QDF2400 patches git am %{PATCH4000} @@ -1473,6 +1475,7 @@ fi
%changelog * Thu Nov 10 2016 Duc Dang dhdang@apm.com [4.5.0-17.el7] +- Integrate upstream fix for GIC default PCI IRQ polarity - Integrate upstream updates for X-Gene Enet driver - Avoid dma_buffer overrun for SlimPRO I2C driver - Add X-Gene SoC PMU support
The posted upstream patch (v4, https://lkml.org/lkml/2016/11/9/930) already got an ack by Jarkko Nikula (https://lkml.org/lkml/2016/11/10/193)
Signed-off-by: Duc Dang dhdang@apm.com --- ...are-Implement-support-for-SMBus-block-rea.patch | 135 +++++++++++++++++++++ SPECS/kernel-aarch64.spec | 3 + 2 files changed, 138 insertions(+) create mode 100644 SOURCES/1019-i2c-designware-Implement-support-for-SMBus-block-rea.patch
diff --git a/SOURCES/1019-i2c-designware-Implement-support-for-SMBus-block-rea.patch b/SOURCES/1019-i2c-designware-Implement-support-for-SMBus-block-rea.patch new file mode 100644 index 0000000..eb732e3 --- /dev/null +++ b/SOURCES/1019-i2c-designware-Implement-support-for-SMBus-block-rea.patch @@ -0,0 +1,135 @@ +From 4211222134d5758e53f667c6ebdf1e0c55d9137b Mon Sep 17 00:00:00 2001 +From: Tin Huynh tnhuynh@apm.com +Date: Thu, 10 Nov 2016 17:33:00 -0600 +Subject: [PATCH] i2c: designware: Implement support for SMBus block read and + write + +Free and Open IPMI use SMBUS BLOCK Read/Write to support SSIF protocol. +However, I2C Designware Core Driver doesn't handle the case at the moment. +The below patch supports this feature. + +Signed-off-by: Tin Huynh tnhuynh@apm.com +Acked-by: Jarkko Nikula jarkko.nikula@linux.intel.com + +[Apply from posted v4 patch (already acked-by Jarkko Nikula, +https://lkml.org/lkml/2016/11/10/193) to CentOS 7.3 AltArch] +Signed-off-by: Duc Dang dhdang@apm.com +--- + drivers/i2c/busses/i2c-designware-core.c | 46 +++++++++++++++++++++++++++-- + drivers/i2c/busses/i2c-designware-pcidrv.c | 1 + + drivers/i2c/busses/i2c-designware-platdrv.c | 1 + + 3 files changed, 45 insertions(+), 3 deletions(-) + +diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c +index 10fbd6d..927849b 100644 +--- a/drivers/i2c/busses/i2c-designware-core.c ++++ b/drivers/i2c/busses/i2c-designware-core.c +@@ -470,6 +470,8 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev) + intr_mask = DW_IC_INTR_DEFAULT_MASK; + + for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) { ++ u32 flags = msgs[dev->msg_write_idx].flags; ++ + /* + * if target address has changed, we need to + * reprogram the target address in the i2c +@@ -515,8 +517,15 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev) + * detected from the registers so we set it always + * when writing/reading the last byte. + */ ++ ++ /* ++ * i2c-core.c always sets the buffer length of ++ * I2C_FUNC_SMBUS_BLOCK_DATA to 1. The length will ++ * be adjusted when receiving the first byte. ++ * Thus we can't stop the transaction here. ++ */ + if (dev->msg_write_idx == dev->msgs_num - 1 && +- buf_len == 1) ++ buf_len == 1 && !(flags & I2C_M_RECV_LEN)) + cmd |= BIT(9); + + if (need_restart) { +@@ -541,7 +550,12 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev) + dev->tx_buf = buf; + dev->tx_buf_len = buf_len; + +- if (buf_len > 0) { ++ /* ++ * Because we don't know the buffer length in the ++ * I2C_FUNC_SMBUS_BLOCK_DATA case, we can't stop ++ * the transaction here. ++ */ ++ if (buf_len > 0 || flags & I2C_M_RECV_LEN) { + /* more bytes to be written */ + dev->status |= STATUS_WRITE_IN_PROGRESS; + break; +@@ -562,6 +576,24 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev) + dw_writel(dev, intr_mask, DW_IC_INTR_MASK); + } + ++static u8 ++i2c_dw_recv_len(struct dw_i2c_dev *dev, u8 len) ++{ ++ struct i2c_msg *msgs = dev->msgs; ++ u32 flags = msgs[dev->msg_read_idx].flags; ++ ++ /* ++ * Adjust the buffer length and mask the flag ++ * after receiving the first byte. ++ */ ++ len += (flags & I2C_CLIENT_PEC) ? 2 : 1; ++ dev->tx_buf_len = len - min_t(u8, len, dev->rx_outstanding); ++ msgs[dev->msg_read_idx].len = len; ++ msgs[dev->msg_read_idx].flags &= ~I2C_M_RECV_LEN; ++ ++ return len; ++} ++ + static void + i2c_dw_read(struct dw_i2c_dev *dev) + { +@@ -586,7 +618,15 @@ i2c_dw_read(struct dw_i2c_dev *dev) + rx_valid = dw_readl(dev, DW_IC_RXFLR); + + for (; len > 0 && rx_valid > 0; len--, rx_valid--) { +- *buf++ = dw_readl(dev, DW_IC_DATA_CMD); ++ u32 flags = msgs[dev->msg_read_idx].flags; ++ ++ *buf = dw_readl(dev, DW_IC_DATA_CMD); ++ /* Ensure length byte is a valid value */ ++ if (flags & I2C_M_RECV_LEN && ++ *buf <= I2C_SMBUS_BLOCK_MAX && *buf > 0) { ++ len = i2c_dw_recv_len(dev, *buf); ++ } ++ buf++; + dev->rx_outstanding--; + } + +diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c +index 7368be0..13578d9 100644 +--- a/drivers/i2c/busses/i2c-designware-pcidrv.c ++++ b/drivers/i2c/busses/i2c-designware-pcidrv.c +@@ -78,6 +78,7 @@ struct dw_pci_controller { + I2C_FUNC_SMBUS_BYTE | \ + I2C_FUNC_SMBUS_BYTE_DATA | \ + I2C_FUNC_SMBUS_WORD_DATA | \ ++ I2C_FUNC_SMBUS_BLOCK_DATA | \ + I2C_FUNC_SMBUS_I2C_BLOCK) + + /* BayTrail HCNT/LCNT/SDA hold time */ +diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c +index 438f1b4..f67aaed 100644 +--- a/drivers/i2c/busses/i2c-designware-platdrv.c ++++ b/drivers/i2c/busses/i2c-designware-platdrv.c +@@ -210,6 +210,7 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | ++ I2C_FUNC_SMBUS_BLOCK_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK; + if (clk_freq == 100000) + dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | +-- +1.8.3.1 + diff --git a/SPECS/kernel-aarch64.spec b/SPECS/kernel-aarch64.spec index 4c20b21..de899ca 100644 --- a/SPECS/kernel-aarch64.spec +++ b/SPECS/kernel-aarch64.spec @@ -343,6 +343,7 @@ Patch1015: 1015-drivers-net-xgene-fix-Use-GPIO-to-get-link-status.patch Patch1016: 1016-drivers-net-xgene-fix-Disable-coalescing-on-v1-hardw.patch Patch1017: 1017-drivers-net-xgene-fix-Coalescing-values-for-v2-hardw.patch Patch1018: 1018-ACPI-PCI-fix-GIC-irq-model-default-PCI-IRQ-polarity.patch +Patch1019: 1019-i2c-designware-Implement-support-for-SMBus-block-rea.patch
# QDF2400 Patches Patch4000: 4000-arm64-Define-Qualcomm-Technologies-ARMv8-CPU.patch @@ -696,6 +697,7 @@ git am %{PATCH1015} git am %{PATCH1016} git am %{PATCH1017} git am %{PATCH1018} +git am %{PATCH1019}
# Apply QDF2400 patches git am %{PATCH4000} @@ -1475,6 +1477,7 @@ fi
%changelog * Thu Nov 10 2016 Duc Dang dhdang@apm.com [4.5.0-17.el7] +- Integrated posted patch to support SMBus block read/write for Designware I2C - Integrate upstream fix for GIC default PCI IRQ polarity - Integrate upstream updates for X-Gene Enet driver - Avoid dma_buffer overrun for SlimPRO I2C driver
Signed-off-by: Duc Dang dhdang@apm.com --- SOURCES/config-centos-sig | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/SOURCES/config-centos-sig b/SOURCES/config-centos-sig index 4cdaedb..b109be7 100644 --- a/SOURCES/config-centos-sig +++ b/SOURCES/config-centos-sig @@ -6,3 +6,7 @@ CONFIG_QCOM_FALKOR_ERRATUM_E1009=y CONFIG_ACPI_CPPC_CPUFREQ=y CONFIG_SENSORS_XGENE=y CONFIG_XGENE_PMU=y +CONFIG_I2C_DESIGNWARE_CORE=m +CONFIG_I2C_DESIGNWARE_PLATFORM=m +CONFIG_I2C_DESIGNWARE_PCI=m +
This patch also help avoid an eror when built as module, so enable X-Gene HWMon driver as module as well.
Signed-off-by: Duc Dang dhdang@apm.com --- .../1020-hwmon-xgene-access-mailbox-as-RAM.patch | 124 +++++++++++++++++++++ SOURCES/config-centos-sig | 2 +- SPECS/kernel-aarch64.spec | 3 + 3 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 SOURCES/1020-hwmon-xgene-access-mailbox-as-RAM.patch
diff --git a/SOURCES/1020-hwmon-xgene-access-mailbox-as-RAM.patch b/SOURCES/1020-hwmon-xgene-access-mailbox-as-RAM.patch new file mode 100644 index 0000000..3047a06 --- /dev/null +++ b/SOURCES/1020-hwmon-xgene-access-mailbox-as-RAM.patch @@ -0,0 +1,124 @@ +From c7cefce03e691270c0e5e117248e14661e9c9cad Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann arnd@arndb.de +Date: Fri, 9 Sep 2016 22:10:45 +0200 +Subject: [PATCH] hwmon: (xgene) access mailbox as RAM + +The newly added hwmon driver fails to build in an allmodconfig +kernel: + + ERROR: "memblock_is_memory" [drivers/hwmon/xgene-hwmon.ko] undefined! + +According to comments in the code, the mailbox is a shared memory region, +not a set of MMIO registers, so we should use memremap() for mapping it +instead of ioremap or acpi_os_ioremap, and pointer dereferences instead +of readl/writel. + +The driver already uses plain kernel pointers, so it's a bit unusual +to work with functions that operate on __iomem pointers, and this +fixes that part too. + +I'm using READ_ONCE/WRITE_ONCE here to keep the existing behavior +regarding the ordering of the accesses from the CPU, but note that +there are no barriers (also unchanged from before). + +I'm also keeping the endianness behavior, though I'm unsure whether +the message data was supposed to be in LE32 format in the first +place, it's possible this was meant to be interpreted as a byte +stream instead. + +Signed-off-by: Arnd Bergmann arnd@arndb.de +Acked-by: Hoan Tran hotran@apm.com +Tested-by: Hoan Tran hotran@apm.com +Signed-off-by: Guenter Roeck linux@roeck-us.net +--- + drivers/hwmon/xgene-hwmon.c | 29 +++++++++++++++-------------- + 1 file changed, 15 insertions(+), 14 deletions(-) + +diff --git a/drivers/hwmon/xgene-hwmon.c b/drivers/hwmon/xgene-hwmon.c +index aa44579..9c0dbb8 100644 +--- a/drivers/hwmon/xgene-hwmon.c ++++ b/drivers/hwmon/xgene-hwmon.c +@@ -27,6 +27,7 @@ + #include <linux/dma-mapping.h> + #include <linux/hwmon.h> + #include <linux/hwmon-sysfs.h> ++#include <linux/io.h> + #include <linux/interrupt.h> + #include <linux/kfifo.h> + #include <linux/mailbox_controller.h> +@@ -34,7 +35,7 @@ + #include <linux/module.h> + #include <linux/of.h> + #include <linux/platform_device.h> +-#include <acpi/acpi_io.h> ++ + #include <acpi/pcc.h> + + /* SLIMpro message defines */ +@@ -126,10 +127,10 @@ static u16 xgene_word_tst_and_clr(u16 *addr, u16 mask) + { + u16 ret, val; + +- val = readw_relaxed(addr); ++ val = le16_to_cpu(READ_ONCE(*addr)); + ret = val & mask; + val &= ~mask; +- writew_relaxed(val, addr); ++ WRITE_ONCE(*addr, cpu_to_le16(val)); + + return ret; + } +@@ -137,7 +138,7 @@ static u16 xgene_word_tst_and_clr(u16 *addr, u16 mask) + static int xgene_hwmon_pcc_rd(struct xgene_hwmon_dev *ctx, u32 *msg) + { + struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr; +- void *ptr = generic_comm_base + 1; ++ u32 *ptr = (void *)(generic_comm_base + 1); + int rc, i; + u16 val; + +@@ -146,21 +147,21 @@ static int xgene_hwmon_pcc_rd(struct xgene_hwmon_dev *ctx, u32 *msg) + ctx->resp_pending = true; + + /* Write signature for subspace */ +- writel_relaxed(PCC_SIGNATURE_MASK | ctx->mbox_idx, +- &generic_comm_base->signature); ++ WRITE_ONCE(generic_comm_base->signature, ++ cpu_to_le32(PCC_SIGNATURE_MASK | ctx->mbox_idx)); + + /* Write to the shared command region */ +- writew_relaxed(MSG_TYPE(msg[0]) | PCCC_GENERATE_DB_INT, +- &generic_comm_base->command); ++ WRITE_ONCE(generic_comm_base->command, ++ cpu_to_le16(MSG_TYPE(msg[0]) | PCCC_GENERATE_DB_INT)); + + /* Flip CMD COMPLETE bit */ +- val = readw_relaxed(&generic_comm_base->status); ++ val = le16_to_cpu(READ_ONCE(generic_comm_base->status)); + val &= ~PCCS_CMD_COMPLETE; +- writew_relaxed(val, &generic_comm_base->status); ++ WRITE_ONCE(generic_comm_base->status, cpu_to_le16(val)); + + /* Copy the message to the PCC comm space */ + for (i = 0; i < sizeof(struct slimpro_resp_msg) / 4; i++) +- writel_relaxed(msg[i], ptr + i * 4); ++ WRITE_ONCE(ptr[i], cpu_to_le32(msg[i])); + + /* Ring the doorbell */ + rc = mbox_send_message(ctx->mbox_chan, msg); +@@ -689,9 +690,9 @@ static int xgene_hwmon_probe(struct platform_device *pdev) + */ + ctx->comm_base_addr = cppc_ss->base_address; + if (ctx->comm_base_addr) { +- ctx->pcc_comm_addr = +- acpi_os_ioremap(ctx->comm_base_addr, +- cppc_ss->length); ++ ctx->pcc_comm_addr = memremap(ctx->comm_base_addr, ++ cppc_ss->length, ++ MEMREMAP_WB); + } else { + dev_err(&pdev->dev, "Failed to get PCC comm region\n"); + rc = -ENODEV; +-- +1.8.3.1 + diff --git a/SOURCES/config-centos-sig b/SOURCES/config-centos-sig index b109be7..15f60f4 100644 --- a/SOURCES/config-centos-sig +++ b/SOURCES/config-centos-sig @@ -4,7 +4,7 @@ CONFIG_QCOM_FALKOR_ERRATUM_E1003=y CONFIG_QCOM_FALKOR_ERRATUM_E1009=y CONFIG_ACPI_CPPC_CPUFREQ=y -CONFIG_SENSORS_XGENE=y +CONFIG_SENSORS_XGENE=m CONFIG_XGENE_PMU=y CONFIG_I2C_DESIGNWARE_CORE=m CONFIG_I2C_DESIGNWARE_PLATFORM=m diff --git a/SPECS/kernel-aarch64.spec b/SPECS/kernel-aarch64.spec index de899ca..88a35a6 100644 --- a/SPECS/kernel-aarch64.spec +++ b/SPECS/kernel-aarch64.spec @@ -344,6 +344,7 @@ Patch1016: 1016-drivers-net-xgene-fix-Disable-coalescing-on-v1-hardw.patch Patch1017: 1017-drivers-net-xgene-fix-Coalescing-values-for-v2-hardw.patch Patch1018: 1018-ACPI-PCI-fix-GIC-irq-model-default-PCI-IRQ-polarity.patch Patch1019: 1019-i2c-designware-Implement-support-for-SMBus-block-rea.patch +Patch1020: 1020-hwmon-xgene-access-mailbox-as-RAM.patch
# QDF2400 Patches Patch4000: 4000-arm64-Define-Qualcomm-Technologies-ARMv8-CPU.patch @@ -698,6 +699,7 @@ git am %{PATCH1016} git am %{PATCH1017} git am %{PATCH1018} git am %{PATCH1019} +git am %{PATCH1020}
# Apply QDF2400 patches git am %{PATCH4000} @@ -1477,6 +1479,7 @@ fi
%changelog * Thu Nov 10 2016 Duc Dang dhdang@apm.com [4.5.0-17.el7] +- Integrate upstream fix to make X-Gene HWmon access mailbox as RAM and avoid eror when built as module - Integrated posted patch to support SMBus block read/write for Designware I2C - Integrate upstream fix for GIC default PCI IRQ polarity - Integrate upstream updates for X-Gene Enet driver