* [PATCH] ARM: dts: sunxi: Convert to CCU index macros for HDMI controller
From: Maxime Ripard @ 2017-12-05 8:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171204084401.2646-1-wens@csie.org>
Hi,
On Mon, Dec 04, 2017 at 04:44:01PM +0800, Chen-Yu Tsai wrote:
> When the HDMI controller device node was added, the needed PLL clock
> macros were not exported. A separate patch addresses that, but it is
> merged through a different tree.
>
> Now that both patches are in mainline proper, we can convert the raw
> numbers to proper macros.
>
> Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Applied, thanks!
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20171205/5353fceb/attachment.sig>
^ permalink raw reply
* [PATCH 6/8] mfd: axp20x: add battery power supply cell for AXP813
From: Lee Jones @ 2017-12-05 8:24 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <9b5e9ed623165d0586f086343a7c67bc5c18bbb5.1512396054.git-series.quentin.schulz@free-electrons.com>
On Mon, 04 Dec 2017, Quentin Schulz wrote:
> As axp20x-battery-power-supply now supports AXP813, add a cell for it.
>
> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
> ---
> drivers/mfd/axp20x.c | 3 +++
> 1 file changed, 3 insertions(+)
For my own reference:
Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>
--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* [PATCH resend] arm: dts: ls1021a: Specify interrupt-affinity for pmu node
From: Rasmus Villemoes @ 2017-12-05 8:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1510672189-13306-1-git-send-email-rasmus.villemoes@prevas.dk>
From: Esben Haabendal <eha@deif.com>
From: Esben Haabendal <eha@deif.com>
This avoids the warning
hw perfevents: no interrupt-affinity property for /pmu, guessing.
Signed-off-by: Esben Haabendal <eha@deif.com>
[RV: adapt commit log to the warning emitted in current mainline]
Signed-off-by: Rasmus Villemoes <rasmus.villemoes@prevas.dk>
---
arch/arm/boot/dts/ls1021a.dtsi | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/boot/dts/ls1021a.dtsi b/arch/arm/boot/dts/ls1021a.dtsi
index 02a266faec9b..96dc1a29fc64 100644
--- a/arch/arm/boot/dts/ls1021a.dtsi
+++ b/arch/arm/boot/dts/ls1021a.dtsi
@@ -106,6 +106,7 @@
compatible = "arm,cortex-a7-pmu";
interrupts = <GIC_SPI 138 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 139 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-affinity = <&cpu0>, <&cpu1>;
};
reboot {
--
2.7.4
^ permalink raw reply related
* [PATCH v2] sunxi-rsb: Include OF based modalias in device uevent
From: Maxime Ripard @ 2017-12-05 8:19 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAGb2v67GF1JTDT9ip2OzycwN6euBu9QqLBLwkyUKD6D8Bs873Q@mail.gmail.com>
On Tue, Dec 05, 2017 at 03:28:25PM +0800, Chen-Yu Tsai wrote:
> Hi,
>
> On Tue, Nov 28, 2017 at 11:47 PM, Maxime Ripard
> <maxime.ripard@free-electrons.com> wrote:
> > On Mon, Nov 27, 2017 at 08:05:34PM +0100, Stefan Br?ns wrote:
> >> Include the OF-based modalias in the uevent sent when registering devices
> >> on the sunxi RSB bus, so that user space has a chance to autoload the
> >> kernel module for the device.
> >>
> >> Fixes a regression caused by commit 3f241bfa60bd ("arm64: allwinner: a64:
> >> pine64: Use dcdc1 regulator for mmc0"). When the axp20x-rsb module for
> >> the AXP803 PMIC is built as a module, it is not loaded and the system
> >> ends up with an disfunctional MMC controller.
> >>
>
> Tags should be:
>
> Fixes: d787dcdb9c8f ("bus: sunxi-rsb: Add driver for Allwinner Reduced
> Serial Bus")
> Cc: stable <stable@vger.kernel.org> # 4.4.x 7a3b7cd332db of: device:
> Export of_device_{get_modalias, uvent_modalias} to modules
>
> >> Cc: stable <stable@vger.kernel.org>
> >> Signed-off-by: Stefan Br?ns <stefan.bruens@rwth-aachen.de>
> >
> > Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
>
> Acked-by: Chen-Yu Tsai <wens@csie.org>
>
> Maxime, could you merge this as a fix to get it in fast?
Applied, thanks!
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20171205/cddf8e2f/attachment.sig>
^ permalink raw reply
* [PATCH resend] arm: dts: ls1021a: Add label to USB controllers
From: Rasmus Villemoes @ 2017-12-05 8:16 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1510663184-19537-1-git-send-email-rasmus.villemoes@prevas.dk>
From: Esben Haabendal <eha@deif.com>
Add usb2 and usb3 labels to USB2 and USB3 controller device tree nodes,
for easier modification in board dts files.
Signed-off-by: Esben Haabendal <eha@deif.com>
Signed-off-by: Rasmus Villemoes <rasmus.villemoes@prevas.dk>
---
arch/arm/boot/dts/ls1021a.dtsi | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm/boot/dts/ls1021a.dtsi b/arch/arm/boot/dts/ls1021a.dtsi
index 3ff2b8a9f01a..02a266faec9b 100644
--- a/arch/arm/boot/dts/ls1021a.dtsi
+++ b/arch/arm/boot/dts/ls1021a.dtsi
@@ -675,7 +675,7 @@
};
};
- usb at 8600000 {
+ usb2: usb at 8600000 {
compatible = "fsl-usb2-dr-v2.5", "fsl-usb2-dr";
reg = <0x0 0x8600000 0x0 0x1000>;
interrupts = <GIC_SPI 171 IRQ_TYPE_LEVEL_HIGH>;
@@ -683,7 +683,7 @@
phy_type = "ulpi";
};
- usb3 at 3100000 {
+ usb3: usb3 at 3100000 {
compatible = "snps,dwc3";
reg = <0x0 0x3100000 0x0 0x10000>;
interrupts = <GIC_SPI 93 IRQ_TYPE_LEVEL_HIGH>;
--
2.7.4
^ permalink raw reply related
* [PATCH resend] arm: dts: ls1021a: add reboot node to .dtsi
From: Rasmus Villemoes @ 2017-12-05 8:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1510566980-26602-1-git-send-email-rasmus.villemoes@prevas.dk>
The LS1021A can be reset via the dcfg regmap in the same way as the
arm64 layerscape SoCs, so add the corresponding DT node.
Signed-off-by: Rasmus Villemoes <rasmus.villemoes@prevas.dk>
---
arch/arm/boot/dts/ls1021a.dtsi | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/arm/boot/dts/ls1021a.dtsi b/arch/arm/boot/dts/ls1021a.dtsi
index 9319e1f0f1d8..3ff2b8a9f01a 100644
--- a/arch/arm/boot/dts/ls1021a.dtsi
+++ b/arch/arm/boot/dts/ls1021a.dtsi
@@ -108,6 +108,13 @@
<GIC_SPI 139 IRQ_TYPE_LEVEL_HIGH>;
};
+ reboot {
+ compatible = "syscon-reboot";
+ regmap = <&dcfg>;
+ offset = <0xb0>;
+ mask = <0x02>;
+ };
+
soc {
compatible = "simple-bus";
#address-cells = <2>;
--
2.7.4
^ permalink raw reply related
* [PATCH 3/8] mfd: axp20x: probe axp20x_adc driver for AXP813
From: Maxime Ripard @ 2017-12-05 8:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <302c93023d8c4a54cafe321d912df484e39727b7.1512396054.git-series.quentin.schulz@free-electrons.com>
On Mon, Dec 04, 2017 at 03:12:49PM +0100, Quentin Schulz wrote:
> This makes the axp20x_adc driver probe with platform device id
> "axp813-adc".
>
> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
> ---
> drivers/mfd/axp20x.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
> index 2468b43..42e54d1 100644
> --- a/drivers/mfd/axp20x.c
> +++ b/drivers/mfd/axp20x.c
> @@ -878,7 +878,9 @@ static struct mfd_cell axp813_cells[] = {
> .resources = axp803_pek_resources,
> }, {
> .name = "axp20x-regulator",
> - }
> + }, {
> + .name = "axp813-adc",
> + },
Any particular reason you're not adding it to the DT?
Thanks!
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20171205/3ed3b476/attachment-0001.sig>
^ permalink raw reply
* [PATCH 4/9] ASoC: sun8i-codec: Add support for A64 SoC
From: Maxime Ripard @ 2017-12-05 8:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171203204157.20829-5-anarsoul@gmail.com>
On Sun, Dec 03, 2017 at 12:41:52PM -0800, Vasily Khoruzhick wrote:
> Digital part of audio codec block in the A64 is very similar to what
> is used by the A33(sun8i) devices. However, it uses different LRCK
> divider.
>
> Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
What makes it impossible to derive from the sample rate and number of
channels?
Have you tried other sampling rates?
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20171205/f4ae3140/attachment.sig>
^ permalink raw reply
* [PATCH 2/9] ASoC: sun4i-i2s: Add compatibility with A64 codec I2S
From: Maxime Ripard @ 2017-12-05 8:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CA+E=qVd+uM0Aq_=sf+fomQ-m+3Oy0MdG7OPjF5tVagG1UgKYzg@mail.gmail.com>
Hi,
On Sun, Dec 03, 2017 at 11:34:06PM -0800, Vasily Khoruzhick wrote:
> I'd keep it sun50i_a64_acodec_i2s, since other 3 I2S modules aren't
> compatible with this one, but similar to H3.
Please don't top-post.
What are those differences?
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20171205/bf8638a9/attachment.sig>
^ permalink raw reply
* [PATCH v3] ARM: dts: Add basic devicetree for D-Link DNS-313
From: Linus Walleij @ 2017-12-05 7:56 UTC (permalink / raw)
To: linux-arm-kernel
This adds a basic device tree for the D-Link DNS-313
NAS enclosure. This device has a thermal sensor and a
fan so we add a thermal zone for the chassis in the
device tree based on information from the product.
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
ChangeLog v2->v3:
- Collect Andrew's ACK.
- Add thermal zone management for the chassis from system info.
ChangeLog v1->v2:
- Fixed the unit name on the MDIO PHY.
- Add the G751 temperature sensor
---
arch/arm/boot/dts/Makefile | 1 +
arch/arm/boot/dts/gemini-dlink-dns-313.dts | 241 +++++++++++++++++++++++++++++
2 files changed, 242 insertions(+)
create mode 100644 arch/arm/boot/dts/gemini-dlink-dns-313.dts
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index d0381e9caf21..4388905a4326 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -192,6 +192,7 @@ dtb-$(CONFIG_ARCH_EXYNOS5) += \
exynos5800-peach-pi.dtb
dtb-$(CONFIG_ARCH_GEMINI) += \
gemini-dlink-dir-685.dtb \
+ gemini-dlink-dns-313.dtb \
gemini-nas4220b.dtb \
gemini-rut1xx.dtb \
gemini-sq201.dtb \
diff --git a/arch/arm/boot/dts/gemini-dlink-dns-313.dts b/arch/arm/boot/dts/gemini-dlink-dns-313.dts
new file mode 100644
index 000000000000..076b8d89befb
--- /dev/null
+++ b/arch/arm/boot/dts/gemini-dlink-dns-313.dts
@@ -0,0 +1,241 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Device Tree file for D-Link DNS-313 1-Bay Network Storage Enclosure
+ */
+
+/dts-v1/;
+
+#include "gemini.dtsi"
+#include <dt-bindings/input/input.h>
+#include <dt-bindings/thermal/thermal.h>
+
+/ {
+ model = "D-Link DNS-313 1-Bay Network Storage Enclosure";
+ compatible = "dlink,dir-313", "cortina,gemini";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ memory {
+ /* 64 MB SDRAM in a Nanya NT5DS32M16BS-6K package */
+ device_type = "memory";
+ reg = <0x00000000 0x4000000>;
+ };
+
+ aliases {
+ mdio-gpio0 = &mdio0;
+ };
+
+ chosen {
+ stdout-path = "uart0:19200n8";
+ };
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ button-esc {
+ debounce_interval = <50>;
+ wakeup-source;
+ linux,code = <KEY_ESC>;
+ label = "reset";
+ gpios = <&gpio1 31 GPIO_ACTIVE_LOW>;
+ };
+ };
+
+ leds {
+ compatible = "gpio-leds";
+ led-power {
+ label = "dns313:blue:power";
+ gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>;
+ default-state = "on";
+ linux,default-trigger = "heartbeat";
+ };
+ led-disk-blue {
+ label = "dns313:blue:disk";
+ gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>;
+ default-state = "off";
+ };
+ led-disk-green {
+ label = "dns313:green:disk";
+ gpios = <&gpio0 3 GPIO_ACTIVE_HIGH>;
+ default-state = "off";
+ linux,default-trigger = "ide-disk";
+ /* Ideally should activate while reading */
+ };
+ led-disk-red {
+ label = "dns313:red:disk";
+ gpios = <&gpio0 4 GPIO_ACTIVE_HIGH>;
+ default-state = "off";
+ /* Ideally should activate while writing */
+ };
+ };
+
+ /*
+ * This is a ADDA AD0405GB-G73 fan @3000 and 6000 RPM.
+ */
+ fan0: gpio-fan {
+ compatible = "gpio-fan";
+ gpios = <&gpio0 11 GPIO_ACTIVE_HIGH>,
+ <&gpio0 12 GPIO_ACTIVE_HIGH>;
+ gpio-fan,speed-map = <0 0>, <3000 1>, <6000 2>;
+ cooling-min-level = <0>;
+ cooling-max-level = <2>;
+ #cooling-cells = <2>;
+ };
+
+
+ /* Global Mixed-Mode Technology G751 mounted on GPIO I2C */
+ gpio-i2c {
+ compatible = "i2c-gpio";
+ sda-gpios = <&gpio0 15 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>;
+ scl-gpios = <&gpio0 16 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ g751: temperature-sensor at 48 {
+ compatible = "gmt,g751";
+ reg = <0x48>;
+ #thermal-sensor-cells = <0>;
+ };
+ };
+
+ thermal-zones {
+ chassis-thermal {
+ /* Poll every 20 seconds */
+ polling-delay = <20000>;
+ /* Poll every 2nd second when cooling */
+ polling-delay-passive = <2000>;
+
+ thermal-sensors = <&g751>;
+
+ /* Tripping points from the fan.script in the rootfs */
+ trips {
+ chassis_alert0: chassis-alert0 {
+ /* At 43 degrees turn on low speed */
+ temperature = <43000>;
+ hysteresis = <3000>;
+ type = "active";
+ };
+ chassis_alert1: chassis-alert1 {
+ /* At 47 degrees turn on high speed */
+ temperature = <47000>;
+ hysteresis = <3000>;
+ type = "active";
+ };
+ chassis_crit: chassis-crit {
+ /* Just shut down at 60 degrees */
+ temperature = <60000>;
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+
+ cooling-maps {
+ map0 {
+ trip = <&chassis_alert0>;
+ cooling-device = <&fan0 1 1>;
+ };
+ map1 {
+ trip = <&chassis_alert1>;
+ cooling-device = <&fan0 2 2>;
+ };
+ };
+ };
+ };
+
+ mdio0: ethernet-phy {
+ compatible = "virtual,mdio-gpio";
+ /* Uses MDC and MDIO */
+ gpios = <&gpio0 22 GPIO_ACTIVE_HIGH>, /* MDC */
+ <&gpio0 21 GPIO_ACTIVE_HIGH>; /* MDIO */
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ /* This is a Realtek RTL8211B Gigabit ethernet transceiver */
+ phy0: ethernet-phy at 1 {
+ reg = <1>;
+ device_type = "ethernet-phy";
+ };
+ };
+
+ soc {
+ flash at 30000000 {
+ status = "okay";
+ /* 512KB of flash */
+ reg = <0x30000000 0x00080000>;
+
+ /*
+ * This "RedBoot" is the Storlink derivative.
+ */
+ partition at 0 {
+ label = "RedBoot";
+ reg = <0x00000000 0x00040000>;
+ read-only;
+ };
+ partition at 40000 {
+ label = "MTD1";
+ reg = <0x00040000 0x00020000>;
+ read-only;
+ };
+ partition at 60000 {
+ label = "MTD2";
+ reg = <0x00060000 0x00020000>;
+ read-only;
+ };
+ };
+
+ syscon: syscon at 40000000 {
+ pinctrl {
+ /*
+ */
+ gpio0_default_pins: pinctrl-gpio0 {
+ mux {
+ function = "gpio0";
+ groups =
+ /* Used by LEDs conflicts ICE */
+ "gpio0bgrp",
+ /* Used by ? conflicts ICE */
+ "gpio0cgrp",
+ /*
+ * Used by fan & G751, conflicts LPC,
+ * UART modem lines, SSP
+ */
+ "gpio0egrp",
+ /* Used by G751 */
+ "gpio0fgrp",
+ /* Used by MDIO */
+ "gpio0igrp";
+ };
+ };
+ gpio1_default_pins: pinctrl-gpio1 {
+ mux {
+ function = "gpio1";
+ /* Used by "reset" button */
+ groups = "gpio1dgrp";
+ };
+ };
+ };
+ };
+
+ sata: sata at 46000000 {
+ /* The ROM uses this muxmode */
+ cortina,gemini-ata-muxmode = <3>;
+ cortina,gemini-enable-sata-bridge;
+ status = "okay";
+ };
+
+ gpio0: gpio at 4d000000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&gpio0_default_pins>;
+ };
+
+ gpio1: gpio at 4e000000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&gpio1_default_pins>;
+ };
+
+ ata at 63000000 {
+ status = "okay";
+ };
+ };
+};
--
2.14.3
^ permalink raw reply related
* [PATCH v2] sunxi-rsb: Include OF based modalias in device uevent
From: Chen-Yu Tsai @ 2017-12-05 7:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171128154734.v2yog6lwiombv3io@flea.home>
Hi,
On Tue, Nov 28, 2017 at 11:47 PM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> On Mon, Nov 27, 2017 at 08:05:34PM +0100, Stefan Br?ns wrote:
>> Include the OF-based modalias in the uevent sent when registering devices
>> on the sunxi RSB bus, so that user space has a chance to autoload the
>> kernel module for the device.
>>
>> Fixes a regression caused by commit 3f241bfa60bd ("arm64: allwinner: a64:
>> pine64: Use dcdc1 regulator for mmc0"). When the axp20x-rsb module for
>> the AXP803 PMIC is built as a module, it is not loaded and the system
>> ends up with an disfunctional MMC controller.
>>
Tags should be:
Fixes: d787dcdb9c8f ("bus: sunxi-rsb: Add driver for Allwinner Reduced
Serial Bus")
Cc: stable <stable@vger.kernel.org> # 4.4.x 7a3b7cd332db of: device:
Export of_device_{get_modalias, uvent_modalias} to modules
>> Cc: stable <stable@vger.kernel.org>
>> Signed-off-by: Stefan Br?ns <stefan.bruens@rwth-aachen.de>
>
> Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Chen-Yu Tsai <wens@csie.org>
Maxime, could you merge this as a fix to get it in fast?
ChenYu
^ permalink raw reply
* [PATCH v9 3/5] perf utils: use pmu->is_uncore to detect PMU UNCORE devices
From: Jin, Yao @ 2017-12-05 7:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAKTKpr7oM4h_gxj1P=r_J_T5L_Mmtqqj_Uvji-fARKnQRnCP1w@mail.gmail.com>
Hi,
I applied the diff but it's failed.
jinyao at skl:~/skl-ws/perf-dev/lck-4594/src$ patch -p1 < 1.pat
patching file tools/perf/util/pmu.c
patch: **** malformed patch at line 41: *head, struct perf_pmu *pmu)
Could you send the patch as attachment to me in another mail thread?
to yao.jin at linux.intel.com
cc yao.jin@intel.com
Thanks
Jin Yao
On 12/5/2017 3:12 PM, Ganapatrao Kulkarni wrote:
> diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
> index 5ad8a18..57e38fd 100644
> --- a/tools/perf/util/pmu.c
> +++ b/tools/perf/util/pmu.c
> @@ -538,6 +538,34 @@ static bool pmu_is_uncore(const char *name)
> }
>
> /*
> + * PMU CORE devices have different name other than cpu in sysfs on some
> + * platforms. looking for possible sysfs files to identify as core device.
> + */
> +static int is_pmu_core(const char *name)
> +{
> + struct stat st;
> + char path[PATH_MAX];
> + const char *sysfs = sysfs__mountpoint();
> +
> + if (!sysfs)
> + return 0;
> +
> + /* Look for cpu sysfs (x86 and others) */
> + scnprintf(path, PATH_MAX, "%s/bus/event_source/devices/cpu", sysfs);
> + if ((stat(path, &st) == 0) &&
> + (strncmp(name, "cpu", strlen("cpu")) == 0))
> + return 1;
> +
> + /* Look for cpu sysfs (specific to arm) */
> + scnprintf(path, PATH_MAX, "%s/bus/event_source/devices/%s/cpus",
> + sysfs, name);
> + if (stat(path, &st) == 0)
> + return 1;
> +
> + return 0;
> +}
> +
> +/*
> * Return the CPU id as a raw string.
> *
> * Each architecture should provide a more precise id string that
> @@ -641,7 +669,7 @@ static void pmu_add_cpu_aliases(struct list_head
> *head, struct perf_pmu *pmu)
> break;
> }
>
> - if (pmu->is_uncore) {
> + if (!is_pmu_core(name)) {
> /* check for uncore devices */
> if (pe->pmu == NULL)
> continue;
^ permalink raw reply
* [PATCH v9 3/5] perf utils: use pmu->is_uncore to detect PMU UNCORE devices
From: Ganapatrao Kulkarni @ 2017-12-05 7:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <6fdad34d-1612-0447-e58e-5c748f92668d@linux.intel.com>
thanks Jin Yao for point this out.
looks like logic of leveraging uncore device type(which i have changed
in v9) does not go well
with some json events of x86.
can you please try below diff(logic used till v8), which keeps the
original logic of identifying core/cpu PMUs.
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 5ad8a18..57e38fd 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -538,6 +538,34 @@ static bool pmu_is_uncore(const char *name)
}
/*
+ * PMU CORE devices have different name other than cpu in sysfs on some
+ * platforms. looking for possible sysfs files to identify as core device.
+ */
+static int is_pmu_core(const char *name)
+{
+ struct stat st;
+ char path[PATH_MAX];
+ const char *sysfs = sysfs__mountpoint();
+
+ if (!sysfs)
+ return 0;
+
+ /* Look for cpu sysfs (x86 and others) */
+ scnprintf(path, PATH_MAX, "%s/bus/event_source/devices/cpu", sysfs);
+ if ((stat(path, &st) == 0) &&
+ (strncmp(name, "cpu", strlen("cpu")) == 0))
+ return 1;
+
+ /* Look for cpu sysfs (specific to arm) */
+ scnprintf(path, PATH_MAX, "%s/bus/event_source/devices/%s/cpus",
+ sysfs, name);
+ if (stat(path, &st) == 0)
+ return 1;
+
+ return 0;
+}
+
+/*
* Return the CPU id as a raw string.
*
* Each architecture should provide a more precise id string that
@@ -641,7 +669,7 @@ static void pmu_add_cpu_aliases(struct list_head
*head, struct perf_pmu *pmu)
break;
}
- if (pmu->is_uncore) {
+ if (!is_pmu_core(name)) {
/* check for uncore devices */
if (pe->pmu == NULL)
continue;
i have tried this diff on my x86 PC(haswell) and looks to be ok.
please confirm your testing on skylake.
gkulkarni at gkFc25>perf>> ./perf stat --per-thread -p 12663 -M CPI,IPC sleep 1
Performance counter stats for process id '12663':
bash-12663 278,886 inst_retired.any:u
bash-12663 482,284 cycles:u
bash-12663 278,886 inst_retired.any:u
bash-12663 483,597
cpu_clk_unhalted.thread:u
1.000923760 seconds time elapsed
On Tue, Dec 5, 2017 at 7:42 AM, Jin, Yao <yao.jin@linux.intel.com> wrote:
> Hi Kulkarni, Arnaldo,
>
> This patch has been merged in perf/core branch today.
>
> But I see a regression issue when I run the 'perf stat'.
>
> With bisect checking, I locate to this patch.
>
> commit ad8737a08973f5dca632bdd63cf2abc99670e540
> Author: Ganapatrao Kulkarni <ganapatrao.kulkarni@cavium.com>
> Date: Tue Oct 17 00:02:20 2017 +0530
>
> perf pmu: Use pmu->is_uncore to detect UNCORE devices
>
> For example (on Intel skylake desktop),
>
> 1. The correct output should be (without this patch):
>
> root at skl:/tmp# perf stat --per-thread -p 1754 -M CPI,IPC
> ^C
> Performance counter stats for process id '1754':
>
> vmstat-1754 1,882,798 inst_retired.any #
> 0.8 CPI
> vmstat-1754 1,589,720 cycles
> vmstat-1754 1,882,798 inst_retired.any #
> 1.2 IPC
> vmstat-1754 1,589,720 cpu_clk_unhalted.thread
>
> 2.647443167 seconds time elapsed
>
> 2. With this patch, the output will be:
>
> root at skl:/tmp# perf stat --per-thread -p 1754 -M CPI,IPC
> ^C
> Performance counter stats for process id '1754':
>
> vmstat-1754 1,945,589 inst_retired.any
> vmstat-1754 <not supported> inst_retired.any
> vmstat-1754 1,609,892 cycles
> vmstat-1754 1,945,589 inst_retired.any
> vmstat-1754 <not supported> inst_retired.any
> vmstat-1754 1,609,892 cpu_clk_unhalted.thread
> vmstat-1754 <not supported> cpu_clk_unhalted.thread
>
> 3.051274166 seconds time elapsed
>
> Could you please help to take a look?
>
> Thanks
> Jin Yao
>
>
> On 10/17/2017 2:32 AM, Ganapatrao Kulkarni wrote:
>>
>> PMU CORE devices are identified using sysfs filename cpu, however
>> on some platforms(like arm/arm64), PMU CORE sysfs name is not cpu.
>> Hence cpu cannot be used to differentiate PMU CORE/UNCORE devices.
>>
>> commit:
>> 66ec1191 ("perf pmu: Unbreak perf record for arm/arm64 with events with
>> explicit PMU")
>>
>> has introduced pmu->is_uncore, which is set to PMU UNCORE devices only.
>> Adding changes to use pmu->is_uncore to identify UNCORE devices.
>>
>> Acked-by: Will Deacon <will.deacon@arm.com>
>> Tested-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
>> Signed-off-by: Ganapatrao Kulkarni <ganapatrao.kulkarni@cavium.com>
>> ---
>> tools/perf/util/pmu.c | 11 +++++++----
>> 1 file changed, 7 insertions(+), 4 deletions(-)
>>
>> diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
>> index 8b17db5..9110718 100644
>> --- a/tools/perf/util/pmu.c
>> +++ b/tools/perf/util/pmu.c
>> @@ -603,7 +603,6 @@ static void pmu_add_cpu_aliases(struct list_head
>> *head, struct perf_pmu *pmu)
>> */
>> i = 0;
>> while (1) {
>> - const char *pname;
>> pe = &map->table[i++];
>> if (!pe->name) {
>> @@ -612,9 +611,13 @@ static void pmu_add_cpu_aliases(struct list_head
>> *head, struct perf_pmu *pmu)
>> break;
>> }
>> - pname = pe->pmu ? pe->pmu : "cpu";
>> - if (strncmp(pname, name, strlen(pname)))
>> - continue;
>> + if (pmu->is_uncore) {
>> + /* check for uncore devices */
>> + if (pe->pmu == NULL)
>> + continue;
>> + if (strncmp(pe->pmu, name, strlen(pe->pmu)))
>> + continue;
>> + }
>> /* need type casts to override 'const' */
>> __perf_pmu__new_alias(head, NULL, (char *)pe->name,
>>
>
thanks
Ganapat
^ permalink raw reply related
* [PATCH 2/2] ARM: dts: keystone-k2g-evm: Enable UART 2
From: Vignesh R @ 2017-12-05 6:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171205064812.15758-1-vigneshr@ti.com>
From: Franklin S Cooper Jr <fcooper@ti.com>
66AK2G GP EVM has a Baseboard Management Controller (BMC) on board.
This controller handles things like setting the SoCs boot mode along
with controlling the on board character LCD display module.
Enable UART2 which communicates with the BMC. This enables userspace
applications to display something on the onboard LCD controlled by the
BMC.
Signed-off-by: Franklin S Cooper Jr <fcooper@ti.com>
Signed-off-by: Vignesh R <vigneshr@ti.com>
---
arch/arm/boot/dts/keystone-k2g-evm.dts | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/arch/arm/boot/dts/keystone-k2g-evm.dts b/arch/arm/boot/dts/keystone-k2g-evm.dts
index 8d100217e38f..9737730ddc21 100644
--- a/arch/arm/boot/dts/keystone-k2g-evm.dts
+++ b/arch/arm/boot/dts/keystone-k2g-evm.dts
@@ -115,6 +115,13 @@
K2G_CORE_IOPAD(0x121c) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* qspi_csn0.qspi_csn0 */
>;
};
+
+ uart2_pins: pinmux_uart2_pins {
+ pinctrl-single,pins = <
+ K2G_CORE_IOPAD(0x11ec) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* uart2_rxd.uart2_rxd */
+ K2G_CORE_IOPAD(0x11f0) (BUFFER_CLASS_B | PIN_PULLDOWN | MUX_MODE0) /* uart2_txd.uart2_txd */
+ >;
+ };
};
&uart0 {
@@ -263,3 +270,9 @@
};
};
};
+
+&uart2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart2_pins>;
+ status = "okay";
+};
--
2.15.0
^ permalink raw reply related
* [PATCH 1/2] ARM: dts: keystone-k2g: Add UART 1 and 2 instances
From: Vignesh R @ 2017-12-05 6:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171205064812.15758-1-vigneshr@ti.com>
From: Franklin S Cooper Jr <fcooper@ti.com>
Add DT nodes for two other UART instances of 66AK2G SoC.
Also add power domain and clock domain nodes to UART 0
Signed-off-by: Franklin S Cooper Jr <fcooper@ti.com>
Signed-off-by: Vignesh R <vigneshr@ti.com>
---
arch/arm/boot/dts/keystone-k2g.dtsi | 29 ++++++++++++++++++++++++++++-
1 file changed, 28 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/keystone-k2g.dtsi b/arch/arm/boot/dts/keystone-k2g.dtsi
index ef82c0a6e607..3c48a9f5c882 100644
--- a/arch/arm/boot/dts/keystone-k2g.dtsi
+++ b/arch/arm/boot/dts/keystone-k2g.dtsi
@@ -28,6 +28,8 @@
aliases {
serial0 = &uart0;
+ serial1 = &uart1;
+ serial2 = &uart2;
i2c0 = &i2c0;
i2c1 = &i2c1;
i2c2 = &i2c2;
@@ -114,7 +116,32 @@
reg-io-width = <4>;
reg = <0x02530c00 0x100>;
interrupts = <GIC_SPI 164 IRQ_TYPE_EDGE_RISING>;
- clock-frequency = <200000000>;
+ clocks = <&k2g_clks 0x2c 0>;
+ power-domains = <&k2g_pds 0x2c>;
+ status = "disabled";
+ };
+
+ uart1: serial at 02531000 {
+ compatible = "ti,da830-uart", "ns16550a";
+ current-speed = <115200>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ reg = <0x02531000 0x100>;
+ interrupts = <GIC_SPI 165 IRQ_TYPE_EDGE_RISING>;
+ clocks = <&k2g_clks 0x2d 0>;
+ power-domains = <&k2g_pds 0x2d>;
+ status = "disabled";
+ };
+
+ uart2: serial at 02531400 {
+ compatible = "ti,da830-uart", "ns16550a";
+ current-speed = <115200>;
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ reg = <0x02531400 0x100>;
+ interrupts = <GIC_SPI 166 IRQ_TYPE_EDGE_RISING>;
+ clocks = <&k2g_clks 0x2e 0>;
+ power-domains = <&k2g_pds 0x2e>;
status = "disabled";
};
--
2.15.0
^ permalink raw reply related
* [PATCH 0/2] 66AK2G: Add DT entry for UART1 and UART2
From: Vignesh R @ 2017-12-05 6:48 UTC (permalink / raw)
To: linux-arm-kernel
This series adds DT entry for the remaining two UART instances on 66AK2G
SoC.
Based on linux-next.
Franklin S Cooper Jr (2):
ARM: dts: keystone-k2g: Add UART 1 and 2 instances
ARM: dts: keystone-k2g-evm: Enable UART 2
arch/arm/boot/dts/keystone-k2g-evm.dts | 13 +++++++++++++
arch/arm/boot/dts/keystone-k2g.dtsi | 29 ++++++++++++++++++++++++++++-
2 files changed, 41 insertions(+), 1 deletion(-)
--
2.15.0
^ permalink raw reply
* [PATCH] soc: qcom: Separate kryo l2 accessors from PMU driver
From: Ilia Lin @ 2017-12-05 6:33 UTC (permalink / raw)
To: linux-arm-kernel
The driver provides kernel level API for other drivers
to access the MSM8996 L2 cache registers.
Separating the L2 access code from the PMU driver and
making it public to allow other drivers use it.
The accesses must be separated with a single spinlock,
maintained in this driver.
Change-Id: I2865e888491e85d678e298279400c371427e30ea
Signed-off-by: Ilia Lin <ilialin@codeaurora.org>
---
arch/arm64/Kconfig.platforms | 7 ++++
drivers/perf/qcom_l2_pmu.c | 48 +--------------------------
drivers/soc/qcom/Makefile | 1 +
drivers/soc/qcom/kryo-l2-accessors.c | 64 ++++++++++++++++++++++++++++++++++++
include/soc/qcom/kryo-l2-accessors.h | 27 +++++++++++++++
5 files changed, 100 insertions(+), 47 deletions(-)
create mode 100644 drivers/soc/qcom/kryo-l2-accessors.c
create mode 100644 include/soc/qcom/kryo-l2-accessors.h
diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
index 2401373..1cfed45 100644
--- a/arch/arm64/Kconfig.platforms
+++ b/arch/arm64/Kconfig.platforms
@@ -138,6 +138,13 @@ config ARCH_QCOM
help
This enables support for the ARMv8 based Qualcomm chipsets.
+config ARCH_MSM8996
+ bool "Enable Support for Qualcomm Technologies, Inc. MSM8996"
+ depends on ARCH_QCOM
+ help
+ This enables support for the MSM8996 chipset. If you do not
+ wish to build a kernel that runs on this chipset, say 'N' here.
+
config ARCH_REALTEK
bool "Realtek Platforms"
help
diff --git a/drivers/perf/qcom_l2_pmu.c b/drivers/perf/qcom_l2_pmu.c
index 4fdc848..ba5f241 100644
--- a/drivers/perf/qcom_l2_pmu.c
+++ b/drivers/perf/qcom_l2_pmu.c
@@ -30,7 +30,7 @@
#include <asm/barrier.h>
#include <asm/local64.h>
-#include <asm/sysreg.h>
+#include <soc/qcom/kryo-l2-accessors.h>
#define MAX_L2_CTRS 9
@@ -87,9 +87,6 @@
#define L2_COUNTER_RELOAD BIT_ULL(31)
#define L2_CYCLE_COUNTER_RELOAD BIT_ULL(63)
-#define L2CPUSRSELR_EL1 sys_reg(3, 3, 15, 0, 6)
-#define L2CPUSRDR_EL1 sys_reg(3, 3, 15, 0, 7)
-
#define reg_idx(reg, i) (((i) * IA_L2_REG_OFFSET) + reg##_BASE)
/*
@@ -107,49 +104,6 @@
#define L2_EVENT_STREX 0x421
#define L2_EVENT_CLREX 0x422
-static DEFINE_RAW_SPINLOCK(l2_access_lock);
-
-/**
- * set_l2_indirect_reg: write value to an L2 register
- * @reg: Address of L2 register.
- * @value: Value to be written to register.
- *
- * Use architecturally required barriers for ordering between system register
- * accesses
- */
-static void set_l2_indirect_reg(u64 reg, u64 val)
-{
- unsigned long flags;
-
- raw_spin_lock_irqsave(&l2_access_lock, flags);
- write_sysreg_s(reg, L2CPUSRSELR_EL1);
- isb();
- write_sysreg_s(val, L2CPUSRDR_EL1);
- isb();
- raw_spin_unlock_irqrestore(&l2_access_lock, flags);
-}
-
-/**
- * get_l2_indirect_reg: read an L2 register value
- * @reg: Address of L2 register.
- *
- * Use architecturally required barriers for ordering between system register
- * accesses
- */
-static u64 get_l2_indirect_reg(u64 reg)
-{
- u64 val;
- unsigned long flags;
-
- raw_spin_lock_irqsave(&l2_access_lock, flags);
- write_sysreg_s(reg, L2CPUSRSELR_EL1);
- isb();
- val = read_sysreg_s(L2CPUSRDR_EL1);
- raw_spin_unlock_irqrestore(&l2_access_lock, flags);
-
- return val;
-}
-
struct cluster_pmu;
/*
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 40c56f6..2bf8d93 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o
obj-$(CONFIG_QCOM_SMP2P) += smp2p.o
obj-$(CONFIG_QCOM_SMSM) += smsm.o
obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o
+obj-$(CONFIG_ARCH_MSM8996) += kryo-l2-accessors.o
diff --git a/drivers/soc/qcom/kryo-l2-accessors.c b/drivers/soc/qcom/kryo-l2-accessors.c
new file mode 100644
index 0000000..6be57e6
--- /dev/null
+++ b/drivers/soc/qcom/kryo-l2-accessors.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#include <linux/spinlock.h>
+#include <asm/sysreg.h>
+#include <soc/qcom/kryo-l2-accessors.h>
+
+#define L2CPUSRSELR_EL1 sys_reg(3, 3, 15, 0, 6)
+#define L2CPUSRDR_EL1 sys_reg(3, 3, 15, 0, 7)
+
+static DEFINE_RAW_SPINLOCK(l2_access_lock);
+
+/**
+ * set_l2_indirect_reg: write value to an L2 register
+ * @reg: Address of L2 register.
+ * @value: Value to be written to register.
+ *
+ * Use architecturally required barriers for ordering between system register
+ * accesses, and system registers with respect to device memory
+ */
+void set_l2_indirect_reg(u64 reg, u64 val)
+{
+ unsigned long flags;
+ mb();
+ raw_spin_lock_irqsave(&l2_access_lock, flags);
+ write_sysreg_s(reg, L2CPUSRSELR_EL1);
+ isb();
+ write_sysreg_s(val, L2CPUSRDR_EL1);
+ isb();
+ raw_spin_unlock_irqrestore(&l2_access_lock, flags);
+}
+EXPORT_SYMBOL(set_l2_indirect_reg);
+
+/**
+ * get_l2_indirect_reg: read an L2 register value
+ * @reg: Address of L2 register.
+ *
+ * Use architecturally required barriers for ordering between system register
+ * accesses, and system registers with respect to device memory
+ */
+u64 get_l2_indirect_reg(u64 reg)
+{
+ u64 val;
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&l2_access_lock, flags);
+ write_sysreg_s(reg, L2CPUSRSELR_EL1);
+ isb();
+ val = read_sysreg_s(L2CPUSRDR_EL1);
+ raw_spin_unlock_irqrestore(&l2_access_lock, flags);
+
+ return val;
+}
+EXPORT_SYMBOL(get_l2_indirect_reg);
diff --git a/include/soc/qcom/kryo-l2-accessors.h b/include/soc/qcom/kryo-l2-accessors.h
new file mode 100644
index 0000000..2bebce1
--- /dev/null
+++ b/include/soc/qcom/kryo-l2-accessors.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#ifndef __ASM_ARCH_MSM_MSM_KRYO_L2_ACCESSORS_H
+#define __ASM_ARCH_MSM_MSM_KRYO_L2_ACCESSORS_H
+
+#ifdef CONFIG_ARCH_QCOM
+void set_l2_indirect_reg(u64 reg_addr, u64 val);
+u64 get_l2_indirect_reg(u64 reg_addr);
+#else
+static inline void set_l2_indirect_reg(u32 reg_addr, u32 val) {}
+static inline u32 get_l2_indirect_reg(u32 reg_addr)
+{
+ return 0;
+}
+#endif
+#endif
--
1.9.1
^ permalink raw reply related
* [PATCH] arm64: dts: allwinner: a64: bananapi-m64: Add LED device node
From: Chen-Yu Tsai @ 2017-12-05 6:19 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAMty3ZC2-sVbCgzKV3nZQfG4CtNXJdntuPhjyr_Wbn1xXJc=Dw@mail.gmail.com>
On Tue, Dec 5, 2017 at 2:15 PM, Jagan Teki <jagan@amarulasolutions.com> wrote:
> On Fri, Dec 1, 2017 at 9:57 PM, Chen-Yu Tsai <wens@csie.org> wrote:
>> The Bananapi-M64 has 3 LEDS in red, green, and blue. These are toggled
>> via GPIO lines, which drive transistors that control current across the
>> LEDS. The red LED is by default on, via an additional pull-up on the
>> control line. We consider this means that it is a power indicator.
>> So we set the "default-on" property for it.
>>
>> The pingroups the GPIO lines belong to require external regulators be
>> enabled to be able to drive the GPIO high. These regulators also have
>> other purposes. However the pin controller does not have bindings for
>> regulators. Here we just set them to always-on.
>>
>> Signed-off-by: Chen-Yu Tsai <wens@csie.org>
>> ---
>> .../boot/dts/allwinner/sun50i-a64-bananapi-m64.dts | 36 ++++++++++++++++++++++
>> 1 file changed, 36 insertions(+)
>>
>> diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts
>> index 4a8d3f83a36e..a6975670cd1c 100644
>> --- a/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts
>> +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts
>> @@ -60,6 +60,26 @@
>> stdout-path = "serial0:115200n8";
>> };
>>
>> + leds {
>> + compatible = "gpio-leds";
>> +
>> + pwr-led {
>> + label = "bananapi-m64:red:pwr";
>> + gpios = <&pio 3 24 GPIO_ACTIVE_HIGH>; /* PD24 */
>> + default-state = "on";
>> + };
>> +
>> + green {
>> + label = "bananapi-m64:green:user";
>> + gpios = <&pio 4 14 GPIO_ACTIVE_HIGH>; /* PE14 */
>> + };
>> +
>> + blue {
>> + label = "bananapi-m64:blue:user";
>> + gpios = <&pio 4 15 GPIO_ACTIVE_HIGH>; /* PE15 */
>> + };
>> + };
>> +
>> wifi_pwrseq: wifi_pwrseq {
>> compatible = "mmc-pwrseq-simple";
>> reset-gpios = <&r_pio 0 2 GPIO_ACTIVE_LOW>; /* PL2 */
>> @@ -153,6 +173,17 @@
>>
>> #include "axp803.dtsi"
>>
>> +®_aldo1 {
>> + /*
>> + * This regulator also drives the PE pingroup GPIOs,
>> + * which also controls two LEDs.
>> + */
>> + regulator-always-on;
>> + regulator-min-microvolt = <2800000>;
>> + regulator-max-microvolt = <2800000>;
>
> why 2.8v?
That is what is written on the schematics. Plus it seems camera
modules are meant to use 2.8v.
>
>> + regulator-name = "afvcc-csi";
>> +};
>> +
>> ®_aldo2 {
>> regulator-always-on;
>> regulator-min-microvolt = <1800000>;
>> @@ -168,6 +199,11 @@
>> };
>>
>> ®_dc1sw {
>> + /*
>> + * This regulator also indirectly drives the PD pingroup GPIOs,
>> + * which also controls the power LED.
>> + */
>> + regulator-always-on;
>
> This make rgmii phy always on, does this make emac power-drop?
emac power-drop? Please elaborate.
ChenYu
^ permalink raw reply
* [PATCH v2 1/3] staging: vc04_services: Join multiline dereferences
From: Greg Kroah-Hartman @ 2017-12-05 6:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <c48f337e4d1e58161432664a8731fb50cc6025cb.1512441073.git.sky@genki.is>
On Mon, Dec 04, 2017 at 09:39:05PM -0500, Genki Sky wrote:
> Signed-off-by: Genki Sky <sky@genki.is>
> ---
> .../vc04_services/bcm2835-camera/bcm2835-camera.c | 16 ++++++----------
> 1 file changed, 6 insertions(+), 10 deletions(-)
I can't take patches without any changelog text at all, sorry.
greg k-h
^ permalink raw reply
* [PATCH] arm64: dts: allwinner: a64: bananapi-m64: Add LED device node
From: Jagan Teki @ 2017-12-05 6:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171201162733.23156-1-wens@csie.org>
On Fri, Dec 1, 2017 at 9:57 PM, Chen-Yu Tsai <wens@csie.org> wrote:
> The Bananapi-M64 has 3 LEDS in red, green, and blue. These are toggled
> via GPIO lines, which drive transistors that control current across the
> LEDS. The red LED is by default on, via an additional pull-up on the
> control line. We consider this means that it is a power indicator.
> So we set the "default-on" property for it.
>
> The pingroups the GPIO lines belong to require external regulators be
> enabled to be able to drive the GPIO high. These regulators also have
> other purposes. However the pin controller does not have bindings for
> regulators. Here we just set them to always-on.
>
> Signed-off-by: Chen-Yu Tsai <wens@csie.org>
> ---
> .../boot/dts/allwinner/sun50i-a64-bananapi-m64.dts | 36 ++++++++++++++++++++++
> 1 file changed, 36 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts
> index 4a8d3f83a36e..a6975670cd1c 100644
> --- a/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts
> +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-bananapi-m64.dts
> @@ -60,6 +60,26 @@
> stdout-path = "serial0:115200n8";
> };
>
> + leds {
> + compatible = "gpio-leds";
> +
> + pwr-led {
> + label = "bananapi-m64:red:pwr";
> + gpios = <&pio 3 24 GPIO_ACTIVE_HIGH>; /* PD24 */
> + default-state = "on";
> + };
> +
> + green {
> + label = "bananapi-m64:green:user";
> + gpios = <&pio 4 14 GPIO_ACTIVE_HIGH>; /* PE14 */
> + };
> +
> + blue {
> + label = "bananapi-m64:blue:user";
> + gpios = <&pio 4 15 GPIO_ACTIVE_HIGH>; /* PE15 */
> + };
> + };
> +
> wifi_pwrseq: wifi_pwrseq {
> compatible = "mmc-pwrseq-simple";
> reset-gpios = <&r_pio 0 2 GPIO_ACTIVE_LOW>; /* PL2 */
> @@ -153,6 +173,17 @@
>
> #include "axp803.dtsi"
>
> +®_aldo1 {
> + /*
> + * This regulator also drives the PE pingroup GPIOs,
> + * which also controls two LEDs.
> + */
> + regulator-always-on;
> + regulator-min-microvolt = <2800000>;
> + regulator-max-microvolt = <2800000>;
why 2.8v?
> + regulator-name = "afvcc-csi";
> +};
> +
> ®_aldo2 {
> regulator-always-on;
> regulator-min-microvolt = <1800000>;
> @@ -168,6 +199,11 @@
> };
>
> ®_dc1sw {
> + /*
> + * This regulator also indirectly drives the PD pingroup GPIOs,
> + * which also controls the power LED.
> + */
> + regulator-always-on;
This make rgmii phy always on, does this make emac power-drop?
--
Jagan Teki
Senior Linux Kernel Engineer | Amarula Solutions
U-Boot, Linux | Upstream Maintainer
Hyderabad, India.
^ permalink raw reply
* [PATCH v2] ARM: dts: sun8i: h3: Add dts file for Libre Computer Board ALL-H3-CC H3 ver.
From: Chen-Yu Tsai @ 2017-12-05 6:06 UTC (permalink / raw)
To: linux-arm-kernel
The Libre Computer Board ALL-H3-CC from Libre Technology is a Raspberry
Pi B+ form factor single board computer based on the Allwinner H3 SoC.
The board has 1GB DDR3 SDRAM, provided by 4 2Gb chips. The mounting holes
and connectors are in the exact same position as on the Raspberry Pi B+.
Raspberry Pi B+ like peripherals supported on this board include:
- Power input through micro-USB connector (without USB OTG)
- Native 100 Mbps ethernet using the internal PHY, as opposed to
USB-based on the RPi
- 4x USB 2.0 host ports, directly connected to the SoC, as opposed to
being connected through a USB 2.0 hub on the RPi
- TV and audio output on a 3.5mm TRRS jack
- HDMI output
- Micro-SD card slot
- Standard RPi B+ GPIO header, with the standard peripherals routed to
the same pins.
* 5V, 3.3V power, and ground
* I2C0 on the H3 is routed to I2C1 pins on the RPi header
* I2C1 on the H3 is routed to I2C0 pins on the RPi header
* UART1 on the H3 is routed to UART0 pins on the RPi header
* SPI0 on the H3 is routed to SPI0 pins on the RPi header,
with GPIO pin PA17 replacing the missing Chip Select 1
* I2S1 on the H3 is routed to PCM pins on the RPi header
- Additional peripherals from the H3 are available on different pins.
These include I2S0, JTAG, PWM1, SPDIF, SPI1, and UART3
In addition, there are a number of new features:
- Console UART header
- Consumer IR receiver
- Camera interface (not compatible with RPi)
- Onboard microphone
- eMMC expansion module port
- Heatsink mounting holes
- Power button
The power button requires corresponding software for the embedded
coprocessor to properly function.
This patch adds a dts file for this board that enables all "onboard"
peripherals currently supported. This means no display or camera
support.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
Changes since v1:
- Changed board compatible string
- Added LED color to LED labels
- Changed button label and key event to power button
- Added description of power button to commit message
There are two other variants [1] of this board, with different SoCs and
DRAM configuration. But the board remains the same, thanks to the SoCs
being pin compatible.
The EMAC/EPHY LED polarity driver issue has been fixed in v4.15-rc2. But
there was discussion to move this property under the PHY node instead.
[1] https://www.kickstarter.com/projects/librecomputer/libre-computer-board-tritium-sbc-linux-android-7-n
---
arch/arm/boot/dts/Makefile | 1 +
arch/arm/boot/dts/sun8i-h3-libretech-all-h3-cc.dts | 196 +++++++++++++++++++++
2 files changed, 197 insertions(+)
create mode 100644 arch/arm/boot/dts/sun8i-h3-libretech-all-h3-cc.dts
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 3c139c63098f..aa68f719692f 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -953,6 +953,7 @@ dtb-$(CONFIG_MACH_SUN8I) += \
sun8i-h2-plus-orangepi-zero.dtb \
sun8i-h3-bananapi-m2-plus.dtb \
sun8i-h3-beelink-x2.dtb \
+ sun8i-h3-libretech-all-h3-cc.dtb \
sun8i-h3-nanopi-m1.dtb \
sun8i-h3-nanopi-m1-plus.dtb \
sun8i-h3-nanopi-neo.dtb \
diff --git a/arch/arm/boot/dts/sun8i-h3-libretech-all-h3-cc.dts b/arch/arm/boot/dts/sun8i-h3-libretech-all-h3-cc.dts
new file mode 100644
index 000000000000..d406571a0dd6
--- /dev/null
+++ b/arch/arm/boot/dts/sun8i-h3-libretech-all-h3-cc.dts
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2017 Chen-Yu Tsai <wens@csie.org>
+ *
+ * SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+ */
+
+/dts-v1/;
+#include "sun8i-h3.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+
+/ {
+ model = "Libre Computer Board ALL-H3-CC H3";
+ compatible = "libretech,all-h3-cc-h3", "allwinner,sun8i-h3";
+
+ aliases {
+ ethernet0 = &emac;
+ serial0 = &uart0;
+ };
+
+ chosen {
+ stdout-path = "serial0:115200n8";
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ pwr_led {
+ label = "librecomputer:green:pwr";
+ gpios = <&r_pio 0 10 GPIO_ACTIVE_HIGH>; /* PL10 */
+ default-state = "on";
+ };
+
+ status_led {
+ label = "librecomputer:blue:status";
+ gpios = <&pio 0 7 GPIO_ACTIVE_HIGH>; /* PA7 */
+ };
+ };
+
+ gpio_keys {
+ compatible = "gpio-keys";
+
+ power {
+ label = "power";
+ linux,code = <KEY_POWER>;
+ gpios = <&r_pio 0 2 GPIO_ACTIVE_LOW>; /* PL2 */
+ };
+ };
+
+ reg_vcc1v2: vcc1v2 {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc1v2";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ regulator-boot-on;
+ vin-supply = <®_vcc5v0>;
+ gpio = <&r_pio 0 8 GPIO_ACTIVE_HIGH>; /* PL8 */
+ enable-active-high;
+ };
+
+ reg_vcc3v3: vcc3v3 {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc3v3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ vin-supply = <®_vcc5v0>;
+ };
+
+ /* This represents the board's 5V input */
+ reg_vcc5v0: vcc5v0 {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc5v0";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ };
+
+ reg_vcc_dram: vcc-dram {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc-dram";
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <1500000>;
+ regulator-always-on;
+ regulator-boot-on;
+ vin-supply = <®_vcc5v0>;
+ gpio = <&r_pio 0 9 GPIO_ACTIVE_HIGH>; /* PL9 */
+ enable-active-high;
+ };
+
+ reg_vcc_io: vcc-io {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc-io";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ regulator-boot-on;
+ vin-supply = <®_vcc3v3>;
+ gpio = <&r_pio 0 5 GPIO_ACTIVE_LOW>; /* PL5 */
+ };
+
+ reg_vdd_cpux: vdd-cpux {
+ compatible = "regulator-fixed";
+ regulator-name = "vdd-cpux";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ regulator-boot-on;
+ vin-supply = <®_vcc5v0>;
+ gpio = <&r_pio 0 8 GPIO_ACTIVE_HIGH>; /* PL8 */
+ enable-active-high;
+ };
+};
+
+&codec {
+ allwinner,audio-routing =
+ "Line Out", "LINEOUT",
+ "MIC1", "Mic",
+ "Mic", "MBIAS";
+ status = "okay";
+};
+
+&ehci0 {
+ status = "okay";
+};
+
+&ehci1 {
+ status = "okay";
+};
+
+&ehci2 {
+ status = "okay";
+};
+
+&ehci3 {
+ status = "okay";
+};
+
+&emac {
+ phy-handle = <&int_mii_phy>;
+ phy-mode = "mii";
+ allwinner,leds-active-low;
+ status = "okay";
+};
+
+&ir {
+ pinctrl-names = "default";
+ pinctrl-0 = <&ir_pins_a>;
+ status = "okay";
+};
+
+&mmc0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc0_pins_a>;
+ vmmc-supply = <®_vcc_io>;
+ bus-width = <4>;
+ cd-gpios = <&pio 5 6 GPIO_ACTIVE_HIGH>; /* PF6 */
+ cd-inverted;
+ status = "okay";
+};
+
+&ohci0 {
+ status = "okay";
+};
+
+&ohci1 {
+ status = "okay";
+};
+
+&ohci2 {
+ status = "okay";
+};
+
+&ohci3 {
+ status = "okay";
+};
+
+&uart0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart0_pins_a>;
+ status = "okay";
+};
+
+&usb_otg {
+ dr_mode = "host";
+ status = "okay";
+};
+
+&usbphy {
+ /* VBUS on USB ports are always on */
+ usb0_vbus-supply = <®_vcc5v0>;
+ usb1_vbus-supply = <®_vcc5v0>;
+ usb2_vbus-supply = <®_vcc5v0>;
+ usb3_vbus-supply = <®_vcc5v0>;
+ status = "okay";
+};
--
2.15.0
^ permalink raw reply related
* [PATCH 5/6] cpufreq: Add DVFS support for Armada 37xx
From: Viresh Kumar @ 2017-12-05 5:54 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171201112508.14121-6-gregory.clement@free-electrons.com>
On 01-12-17, 12:25, Gregory CLEMENT wrote:
> This patch adds DVFS support for the Armada 37xx SoCs
>
> There are up to four CPU frequency loads for Armada 37xx controlled by
> the hardware.
>
> This driver associates the CPU load level to a frequency, then the
> hardware will switch while selecting a load level.
>
> The hardware also can associate a voltage for each level (AVS support)
> but it is not yet supported
>
> Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
> ---
> drivers/cpufreq/Kconfig.arm | 7 +
> drivers/cpufreq/Makefile | 1 +
> drivers/cpufreq/armada-37xx-cpufreq.c | 241 ++++++++++++++++++++++++++++++++++
> 3 files changed, 249 insertions(+)
> create mode 100644 drivers/cpufreq/armada-37xx-cpufreq.c
>
> diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
> index 17625115c67f..3018ff0d068f 100644
> --- a/drivers/cpufreq/Kconfig.arm
> +++ b/drivers/cpufreq/Kconfig.arm
> @@ -19,6 +19,13 @@ config ACPI_CPPC_CPUFREQ
>
> If in doubt, say N.
>
> +config ARM_ARMADA_37XX_CPUFREQ
> + tristate "Armada 37xx CPUFreq support"
> + depends on ARCH_MVEBU
> + help
> + This adds the CPUFreq driver support for Marvell Armada 37xx SoCs.
> + The Armada 37xx PMU supports 4 frequency and VDD levels.
> +
> # big LITTLE core layer and glue drivers
> config ARM_BIG_LITTLE_CPUFREQ
> tristate "Generic ARM big LITTLE CPUfreq driver"
> diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
> index d762e76887e7..e07715ce8844 100644
> --- a/drivers/cpufreq/Makefile
> +++ b/drivers/cpufreq/Makefile
> @@ -52,6 +52,7 @@ obj-$(CONFIG_ARM_BIG_LITTLE_CPUFREQ) += arm_big_little.o
> # LITTLE drivers, so that it is probed last.
> obj-$(CONFIG_ARM_DT_BL_CPUFREQ) += arm_big_little_dt.o
>
> +obj-$(CONFIG_ARM_ARMADA_37XX_CPUFREQ) += armada-37xx-cpufreq.o
> obj-$(CONFIG_ARM_BRCMSTB_AVS_CPUFREQ) += brcmstb-avs-cpufreq.o
> obj-$(CONFIG_ACPI_CPPC_CPUFREQ) += cppc_cpufreq.o
> obj-$(CONFIG_ARCH_DAVINCI) += davinci-cpufreq.o
> diff --git a/drivers/cpufreq/armada-37xx-cpufreq.c b/drivers/cpufreq/armada-37xx-cpufreq.c
> new file mode 100644
> index 000000000000..40c9a744cc6e
> --- /dev/null
> +++ b/drivers/cpufreq/armada-37xx-cpufreq.c
> @@ -0,0 +1,241 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * CPU frequency scaling support for Armada 37xx platform.
> + *
> + * Copyright (C) 2017 Marvell
> + *
> + * Gregory CLEMENT <gregory.clement@free-electrons.com>
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/cpu.h>
> +#include <linux/cpufreq.h>
> +#include <linux/err.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/of_irq.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_opp.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +/* Power management in North Bridge register set */
> +#define ARMADA_37XX_NB_L0L1 0x18
> +#define ARMADA_37XX_NB_L2L3 0x1C
> +#define ARMADA_37XX_NB_TBG_DIV_OFF 13
> +#define ARMADA_37XX_NB_TBG_DIV_MASK 0x7
> +#define ARMADA_37XX_NB_CLK_SEL_OFF 11
> +#define ARMADA_37XX_NB_CLK_SEL_MASK 0x1
> +#define ARMADA_37XX_NB_CLK_SEL_TBG 0x1
> +#define ARMADA_37XX_NB_TBG_SEL_OFF 9
> +#define ARMADA_37XX_NB_TBG_SEL_MASK 0x3
> +#define ARMADA_37XX_NB_VDD_SEL_OFF 6
> +#define ARMADA_37XX_NB_VDD_SEL_MASK 0x3
> +#define ARMADA_37XX_NB_CONFIG_SHIFT 16
Looks like you have added tabs after #define as well? Perhaps a space
is good enough there.
> +#define ARMADA_37XX_NB_DYN_MOD 0x24
> +#define ARMADA_37XX_NB_CLK_SEL_EN BIT(26)
> +#define ARMADA_37XX_NB_TBG_EN BIT(28)
> +#define ARMADA_37XX_NB_DIV_EN BIT(29)
> +#define ARMADA_37XX_NB_VDD_EN BIT(30)
> +#define ARMADA_37XX_NB_DFS_EN BIT(31)
> +#define ARMADA_37XX_NB_CPU_LOAD 0x30
> +#define ARMADA_37XX_NB_CPU_LOAD_MASK 0x3
> +#define ARMADA_37XX_DVFS_LOAD_0 0
> +#define ARMADA_37XX_DVFS_LOAD_1 1
> +#define ARMADA_37XX_DVFS_LOAD_2 2
> +#define ARMADA_37XX_DVFS_LOAD_3 3
> +
> +/*
> + * On Armada 37xx the Power management manages 4 level of CPU load,
> + * each level can be associated with a CPU clock source, a CPU
> + * divider, a VDD level, etc...
> + */
> +#define LOAD_LEVEL_NR 4
> +
> +struct armada_37xx_dvfs {
> + u32 cpu_freq_max;
> + u8 divider[LOAD_LEVEL_NR];
> +};
> +
> +static struct armada_37xx_dvfs armada_37xx_dvfs[] = {
> + {.cpu_freq_max = 1200*1000*1000, .divider = {1, 2, 4, 6} },
> + {.cpu_freq_max = 1000*1000*1000, .divider = {1, 2, 4, 5} },
> + {.cpu_freq_max = 800*1000*1000, .divider = {1, 2, 3, 4} },
> + {.cpu_freq_max = 600*1000*1000, .divider = {2, 4, 5, 6} },
> +};
> +
> +static struct armada_37xx_dvfs *armada_37xx_cpu_freq_info_get(u32 freq)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(armada_37xx_dvfs); i++) {
> + if (freq == armada_37xx_dvfs[i].cpu_freq_max)
> + return &armada_37xx_dvfs[i];
> + }
> +
> + pr_err("Unsupported CPU frequency %d MHz\n", freq/1000000);
> + return NULL;
> +}
> +
> +/*
> + * Setup the four level managed by the hardware. Once the four level
> + * will be configured then the DVFS will be enabled.
> + */
> +static void __init armada37xx_cpufreq_dvfs_setup(struct regmap *base,
> + struct clk *clk, u8 *divider)
> +{
> + int load_level;
> + struct clk *parent;
> +
> + for (load_level = 0; load_level < LOAD_LEVEL_NR; load_level++) {
> + unsigned int reg, mask, val, offset = 0;
> +
> + if (load_level <= ARMADA_37XX_DVFS_LOAD_1)
> + reg = ARMADA_37XX_NB_L0L1;
> + else
> + reg = ARMADA_37XX_NB_L2L3;
> +
> + if (load_level == ARMADA_37XX_DVFS_LOAD_0 ||
> + load_level == ARMADA_37XX_DVFS_LOAD_2)
> + offset += ARMADA_37XX_NB_CONFIG_SHIFT;
> +
> + /* Set cpu clock source, for all the level we use TBG */
> + val = ARMADA_37XX_NB_CLK_SEL_TBG << ARMADA_37XX_NB_CLK_SEL_OFF;
> + mask = (ARMADA_37XX_NB_CLK_SEL_MASK
> + << ARMADA_37XX_NB_CLK_SEL_OFF);
> +
> + /*
> + * Set cpu divider based on the pre-computed array in
> + * order to have balanced step.
> + */
> + val |= divider[load_level] << ARMADA_37XX_NB_TBG_DIV_OFF;
> + mask |= (ARMADA_37XX_NB_TBG_DIV_MASK
> + << ARMADA_37XX_NB_TBG_DIV_OFF);
> +
> + /* Set VDD divider which is actually the load level. */
> + val |= load_level << ARMADA_37XX_NB_VDD_SEL_OFF;
> + mask |= (ARMADA_37XX_NB_VDD_SEL_MASK
> + << ARMADA_37XX_NB_VDD_SEL_OFF);
> +
> + val <<= offset;
> + mask <<= offset;
> +
> + regmap_update_bits(base, reg, mask, val);
> + }
> +
> + /*
> + * Set cpu clock source, for all the level we keep the same
> + * clock source that the one already configured. For this one
> + * we need to use the clock framework
> + */
> + parent = clk_get_parent(clk);
> + clk_set_parent(clk, parent);
> +}
> +
> +static void __init armada37xx_cpufreq_disable_dvfs(struct regmap *base)
> +{
> + unsigned int reg = ARMADA_37XX_NB_DYN_MOD,
> + mask = ARMADA_37XX_NB_DFS_EN;
> +
> + regmap_update_bits(base, reg, mask, 0);
> +}
> +
> +static void __init armada37xx_cpufreq_enable_dvfs(struct regmap *base)
> +{
> + unsigned int val, reg = ARMADA_37XX_NB_CPU_LOAD,
> + mask = ARMADA_37XX_NB_CPU_LOAD_MASK;
> +
> + /* Start with the highest load (0) */
> + val = ARMADA_37XX_DVFS_LOAD_0;
> + regmap_update_bits(base, reg, mask, val);
> +
> + /* Now enable DVFS for the CPUs */
> + reg = ARMADA_37XX_NB_DYN_MOD;
> + mask = ARMADA_37XX_NB_CLK_SEL_EN | ARMADA_37XX_NB_TBG_EN |
> + ARMADA_37XX_NB_DIV_EN | ARMADA_37XX_NB_VDD_EN |
> + ARMADA_37XX_NB_DFS_EN;
> +
> + regmap_update_bits(base, reg, mask, mask);
> +}
> +
> +static int __init armada37xx_cpufreq_driver_init(void)
> +{
> + struct armada_37xx_dvfs *dvfs;
> + struct platform_device *pdev;
> + unsigned int cur_frequency;
> + struct regmap *nb_pm_base;
> + struct device *cpu_dev;
> + int load_level, ret;
> + struct clk *clk;
> +
> + nb_pm_base =
> + syscon_regmap_lookup_by_compatible("marvell,armada-3700-nb-pm");
> +
> + if (IS_ERR(nb_pm_base))
> + return -ENODEV;
> +
> + /* Before doing any configuration on the DVFS first, disable it */
> + armada37xx_cpufreq_disable_dvfs(nb_pm_base);
> +
> + /*
> + * On CPU 0 register the operating points supported (which are
> + * the nominal CPU frequency and full integer divisions of
> + * it).
> + */
> + cpu_dev = get_cpu_device(0);
> + if (!cpu_dev) {
> + dev_err(cpu_dev, "Cannot get CPU\n");
> + return -ENODEV;
> + }
> +
> + clk = clk_get(cpu_dev, 0);
> + if (IS_ERR(clk)) {
> + dev_err(cpu_dev, "Cannot get clock for CPU0\n");
> + return PTR_ERR(clk);
> + }
> +
> + /* Get nominal (current) CPU frequency */
> + cur_frequency = clk_get_rate(clk);
> + if (!cur_frequency) {
> + dev_err(cpu_dev, "Failed to get clock rate for CPU\n");
> + return -EINVAL;
> + }
> +
> + dvfs = armada_37xx_cpu_freq_info_get(cur_frequency);
> + if (!dvfs)
> + return -EINVAL;
> +
> + armada37xx_cpufreq_dvfs_setup(nb_pm_base, clk, dvfs->divider);
> +
> + /*
> + * In case of a failure of dev_pm_opp_add(), we don't bother
> + * with cleaning up the registered OPP (there's no function to
> + * do so),
What do you mean by the comment within ()? We do have
dev_pm_opp_remove() helper.
> and simply cancel the registration of the cpufreq
> + * device.
> + */
> + for (load_level = ARMADA_37XX_DVFS_LOAD_0; load_level < LOAD_LEVEL_NR;
> + load_level++) {
> + unsigned long freq = dvfs->divider[load_level];
> +
> + ret = dev_pm_opp_add(cpu_dev, freq, 0);
> + if (ret)
> + return ret;
> + }
> +
> + /* Now that everything is setup, enable the DVFS at hardware level */
> + armada37xx_cpufreq_enable_dvfs(nb_pm_base);
> +
> + pdev = platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
> +
> + return PTR_ERR_OR_ZERO(pdev);
> +}
> +/* late_initcall, to guarantee the driver is loaded after A37xx clock driver */
> +late_initcall(armada37xx_cpufreq_driver_init);
> +
> +MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>");
> +MODULE_DESCRIPTION("Armada 37xx cpufreq driver");
> +MODULE_LICENSE("GPL");
> --
> 2.15.0
--
viresh
^ permalink raw reply
* [PATCH 4/7 v2] net: ethernet: i825xx: Fix platform_get_irq's error checking
From: Arvind Yadav @ 2017-12-05 5:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20171204.132556.1601454745789376393.davem@davemloft.net>
Hi David,
On Monday 04 December 2017 11:55 PM, David Miller wrote:
> From: Arvind Yadav <arvind.yadav.cs@gmail.com>
> Date: Mon, 4 Dec 2017 23:18:20 +0530
>
>> @@ -120,9 +120,10 @@ static int sni_82596_probe(struct platform_device *dev)
>> netdevice->dev_addr[5] = readb(eth_addr + 0x06);
>> iounmap(eth_addr);
>>
>> - if (!netdevice->irq) {
>> + if (netdevice->irq <= 0) {
>> printk(KERN_ERR "%s: IRQ not found for i82596 at 0x%lx\n",
>> __FILE__, netdevice->base_addr);
>> + retval = netdevice->irq ? netdevice->irq : -ENODEV;
>> goto probe_failed;
>> }
> Ok, thinking about this some more...
>
> It is impossible to use platform_get_irq() without every single call
> site having this funny:
>
> ret = val ? val : -ENODEV;
>
> sequence.
>
> This is unnecessary duplication and it is also error prone, so I
> really think this logic belongs in platform_get_irq() itself. It can
> convert '0' to -ENODEV and that way we need no special logic in the
> callers at all.
platform_get_irq() will return 0 only for sparc, If sparc initialize
platform
data irq[PROMINTR_MAX] as zero. Otherwise platform_get_irq() will never
return
0. It will return either IRQ number or error (as negative number). But I
am getting
review comment by reviewer/maintainer in other subsystem to add check for
zero. So I have done same changes here. Please correct me if i am wrong.
~arvind
^ permalink raw reply
* [PATCH v3 2/3] mailbox: Add support for Hi3660 mailbox
From: Leo Yan @ 2017-12-05 5:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CABb+yY1+uMhZ8nXxsuv4LFGqOkdH6AX-U_AeNJ0+j1+iA6=yhQ@mail.gmail.com>
On Tue, Dec 05, 2017 at 08:58:05AM +0530, Jassi Brar wrote:
> On Fri, Nov 17, 2017 at 2:54 PM, Xu YiPing <xuyiping@hisilicon.com> wrote:
> > From: Kaihua Zhong <zhongkaihua@huawei.com>
> >
> > Hi3660 mailbox controller is used to send message within multiple
> > processors, MCU, HIFI, etc. It supports 32 mailbox channels and every
> > channel can only be used for single transferring direction. Once the
> > channel is enabled, it needs to specify the destination interrupt and
> > acknowledge interrupt, these two interrupt vectors are used to create
> > the connection between the mailbox and interrupt controllers.
> >
> > The data transferring supports two modes, one is named as "automatic
> > acknowledge" mode so after send message the kernel doesn't need to wait
> > for acknowledge from remote and directly return; there have another mode
> > is to rely on handling interrupt for acknowledge.
> >
> > This commit is for initial version driver, which only supports
> > "automatic acknowledge" mode to support CPU clock, which is the only
> > one consumer to use mailbox and has been verified. Later may enhance
> > this driver for interrupt mode (e.g. for supporting HIFI).
> >
> > Signed-off-by: Leo Yan <leo.yan@linaro.org>
> > Signed-off-by: Ruyi Wang <wangruyi@huawei.com>
> > Signed-off-by: Kaihua Zhong <zhongkaihua@huawei.com>
> > ---
> > drivers/mailbox/Kconfig | 8 +
> > drivers/mailbox/Makefile | 2 +
> > drivers/mailbox/hi3660-mailbox.c | 322 +++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 332 insertions(+)
> > create mode 100644 drivers/mailbox/hi3660-mailbox.c
> >
> > diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
> > index c5731e5..4b5d6e9 100644
> > --- a/drivers/mailbox/Kconfig
> > +++ b/drivers/mailbox/Kconfig
> > @@ -108,6 +108,14 @@ config TI_MESSAGE_MANAGER
> > multiple processors within the SoC. Select this driver if your
> > platform has support for the hardware block.
> >
> > +config HI3660_MBOX
> > + tristate "Hi3660 Mailbox"
> > + depends on ARCH_HISI && OF
> > + help
> > + An implementation of the hi3660 mailbox. It is used to send message
> > + between application processors and other processors/MCU/DSP. Select
> > + Y here if you want to use Hi3660 mailbox controller.
> > +
> > config HI6220_MBOX
> > tristate "Hi6220 Mailbox"
> > depends on ARCH_HISI
> > diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
> > index d54e412..7d1bd51 100644
> > --- a/drivers/mailbox/Makefile
> > +++ b/drivers/mailbox/Makefile
> > @@ -26,6 +26,8 @@ obj-$(CONFIG_TI_MESSAGE_MANAGER) += ti-msgmgr.o
> >
> > obj-$(CONFIG_XGENE_SLIMPRO_MBOX) += mailbox-xgene-slimpro.o
> >
> > +obj-$(CONFIG_HI3660_MBOX) += hi3660-mailbox.o
> > +
> > obj-$(CONFIG_HI6220_MBOX) += hi6220-mailbox.o
> >
> > obj-$(CONFIG_BCM_PDC_MBOX) += bcm-pdc-mailbox.o
> > diff --git a/drivers/mailbox/hi3660-mailbox.c b/drivers/mailbox/hi3660-mailbox.c
> > new file mode 100644
> > index 0000000..ba80834
> > --- /dev/null
> > +++ b/drivers/mailbox/hi3660-mailbox.c
> > @@ -0,0 +1,322 @@
> > +/*
> > + * Hisilicon's Hi3660 mailbox controller driver
> > + *
> > + * Copyright (c) 2017 Hisilicon Limited.
> > + * Copyright (c) 2017 Linaro Limited.
> > + *
> > + * Author: Leo Yan <leo.yan@linaro.org>
> > + *
> > + * 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, version 2 of the License.
> > + *
> > + * 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.
> > + *
> >
> We now use SPDX licence
Thanks for quick response, Jassi.
Will change to SPDX licence fashion.
> > + */
> > +
> > +#include <linux/bitops.h>
> > +#include <linux/delay.h>
> > +#include <linux/device.h>
> > +#include <linux/err.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/io.h>
> > +#include <linux/iopoll.h>
> > +#include <linux/mailbox_controller.h>
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/slab.h>
> > +
> > +#include "mailbox.h"
> > +
> > +#define MBOX_CHAN_MAX 32
> > +
> > +#define MBOX_RX (0x0)
> > +#define MBOX_TX (0x1)
> > +
> > +#define MBOX_BASE(mbox, ch) ((mbox)->base + ((ch) * 0x40))
> > +#define MBOX_SRC_REG (0x00)
> > +#define MBOX_DST_REG (0x04)
> > +#define MBOX_DCLR_REG (0x08)
> > +#define MBOX_DSTAT_REG (0x0c)
> > +#define MBOX_MODE_REG (0x10)
> > +#define MBOX_IMASK_REG (0x14)
> > +#define MBOX_ICLR_REG (0x18)
> > +#define MBOX_SEND_REG (0x1c)
> > +#define MBOX_DATA_REG (0x20)
> > +
> > +#define MBOX_IPC_LOCK_REG (0xa00)
> > +#define MBOX_IPC_UNLOCK (0x1acce551)
> > +
> > +#define MBOX_AUTOMATIC_ACK (1)
> > +
> Please, no brackets around numbers.
Sure, will remove brackets.
> > +#define MBOX_STATE_IDLE BIT(4)
> > +#define MBOX_STATE_ACK BIT(7)
> > +
> > +#define MBOX_MSG_LEN 8
> > +
> > +/**
> > + * Hi3660 mailbox channel device data
> > + *
> > + * A channel can be used for TX or RX, it can trigger remote
> > + * processor interrupt to notify remote processor and can receive
> > + * interrupt if has incoming message.
> > + *
> > + * @dst_irq: Interrupt vector for remote processor
> > + * @ack_irq: Interrupt vector for local processor
> > + */
> > +struct hi3660_mbox_dev {
> >
> Better than dev, maybe hi3660_chan_info ?
>
> > + unsigned int dst_irq;
> > + unsigned int ack_irq;
> > +};
> > +
> > +/**
> > + * Hi3660 mailbox controller data
> > + *
> > + * Mailbox controller includes 32 channels and can allocate
> > + * channel for message transferring.
> > + *
> > + * @dev: Device to which it is attached
> > + * @base: Base address of the register mapping region
> > + * @chan: Representation of channels in mailbox controller
> > + * @mdev: Representation of channel device data
> > + * @controller: Representation of a communication channel controller
> > + */
> > +struct hi3660_mbox {
> > + struct device *dev;
> > + void __iomem *base;
> > + struct mbox_chan chan[MBOX_CHAN_MAX];
> > + struct hi3660_mbox_dev mdev[MBOX_CHAN_MAX];
> >
> Maybe mchan, instead of mdev.
Will refactor structure name.
> > + struct mbox_controller controller;
> > +};
> > +
> > +static inline struct hi3660_mbox *to_hi3660_mbox(struct mbox_controller *mbox)
> > +{
> > + return container_of(mbox, struct hi3660_mbox, controller);
> > +}
> > +
> > +static int hi3660_mbox_check_state(struct mbox_chan *chan)
> > +{
> > + unsigned long ch = (unsigned long)chan->con_priv;
> > + struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox);
> > + struct hi3660_mbox_dev *mdev = &mbox->mdev[ch];
> > + void __iomem *base = MBOX_BASE(mbox, ch);
> > + unsigned long val;
> > + unsigned int state, ret;
> > +
> > + /* Mailbox is idle so directly bail out */
> > + state = readl_relaxed(base + MBOX_MODE_REG);
> > + if (state & MBOX_STATE_IDLE)
> > + return 0;
> > +
> > + /* Wait for acknowledge from remote */
> > + ret = readx_poll_timeout_atomic(readl_relaxed, base + MBOX_MODE_REG,
> > + val, (val & MBOX_STATE_ACK), 1000, 300000);
> > + if (ret) {
> > + dev_err(mbox->dev, "%s: timeout for receiving ack\n", __func__);
> > + return ret;
> > + }
> > +
> > + /* Ensure channel is released */
> > + writel_relaxed(0xffffffff, base + MBOX_IMASK_REG);
> > + writel_relaxed(BIT(mdev->ack_irq), base + MBOX_SRC_REG);
> > +
> > + return 0;
> > +}
> > +
> > +static int hi3660_mbox_unlock(struct mbox_chan *chan)
> > +{
> > + struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox);
> > + unsigned int val, retry = 3;
> > +
> > + do {
> > + writel_relaxed(MBOX_IPC_UNLOCK, mbox->base + MBOX_IPC_LOCK_REG);
> > +
> > + val = readl_relaxed(mbox->base + MBOX_IPC_LOCK_REG);
> > + if (!val)
> > + break;
> > +
> > + udelay(10);
> > + } while (retry--);
> > +
> > + return (!val) ? 0 : -ETIMEDOUT;
> > +}
> > +
> > +static int hi3660_mbox_acquire_channel(struct mbox_chan *chan)
> > +{
> > + unsigned long ch = (unsigned long)chan->con_priv;
> > + struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox);
> > + struct hi3660_mbox_dev *mdev = &mbox->mdev[ch];
> > + void __iomem *base = MBOX_BASE(mbox, ch);
> > + unsigned int val, retry;
> > +
> > + for (retry = 10; retry; retry--) {
> > + /* Check if channel has been acquired */
> > + if (readl_relaxed(base + MBOX_MODE_REG) & MBOX_STATE_IDLE) {
> > + writel_relaxed(BIT(mdev->ack_irq), base + MBOX_SRC_REG);
> > + val = readl_relaxed(base + MBOX_SRC_REG);
> > + if (val & BIT(mdev->ack_irq))
> > + break;
> > + }
> > + }
> > +
> > + return retry ? 0 : -ETIMEDOUT;
> > +}
> > +
> > +static int hi3660_mbox_send(struct mbox_chan *chan, u32 *msg)
> > +{
> > + unsigned long ch = (unsigned long)chan->con_priv;
> > + struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox);
> > + struct hi3660_mbox_dev *mdev = &mbox->mdev[ch];
> > + void __iomem *base = MBOX_BASE(mbox, ch);
> > + unsigned int i;
> > +
> > + /* Clear mask for destination interrupt */
> > + writel_relaxed(~BIT(mdev->dst_irq), base + MBOX_IMASK_REG);
> > +
> > + /* Config destination for interrupt vector */
> > + writel_relaxed(BIT(mdev->dst_irq), base + MBOX_DST_REG);
> > +
> > + /* Automatic acknowledge mode */
> > + writel_relaxed(MBOX_AUTOMATIC_ACK, base + MBOX_MODE_REG);
> > +
> > + /* Fill message data */
> > + for (i = 0; i < MBOX_MSG_LEN; i++)
> > + writel_relaxed(msg[i], base + MBOX_DATA_REG + i * 4);
> > +
> > + /* Trigger data transferring */
> > + writel_relaxed(BIT(mdev->ack_irq), base + MBOX_SEND_REG);
> > + return 0;
> > +}
> > +
> > +static int hi3660_mbox_send_data(struct mbox_chan *chan, void *msg)
> > +{
> > + struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox);
> > + int err;
> > +
> > + err = hi3660_mbox_check_state(chan);
> > + if (err) {
> > + dev_err(mbox->dev, "checking state failed\n");
> > + return err;
> > + }
> > +
> > + err = hi3660_mbox_unlock(chan);
> > + if (err) {
> > + dev_err(mbox->dev, "unlocking mailbox failed\n");
> > + return err;
> > + }
> > +
> > + err = hi3660_mbox_acquire_channel(chan);
> > + if (err) {
> > + dev_err(mbox->dev, "acquiring channel failed\n");
> > + return err;
> > + }
> > +
> Please remember .send_data() is called with irqs disabled spinlock
> All these polling functions hurt. Please move the above three checks
> in last_tx_done()
I have tried to move these three checks in last_tx_done(), since these
three functions tightly couple with mailbox state machine, so I failed
to move them to last_tx_done().
Anyway, I will try to move them to last_tx_done() for next version
patch.
Thanks,
Leo Yan
^ permalink raw reply
* [PATCH 2/2] perf: add arm64 smmuv3 pmu driver
From: Linu Cherian @ 2017-12-05 5:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1de250db-87d1-ed85-d0a7-43319139412b@arm.com>
Hi Robin,
On Mon Aug 07, 2017 at 03:31:24PM +0100, Robin Murphy wrote:
> On 04/08/17 20:59, Neil Leeder wrote:
> > Adds a new driver to support the SMMU v3 PMU and add it into the
> > perf events framework.
> >
> > Each SMMU node may have multiple PMUs associated with it, each of
> > which may support different events.
> >
> > PMUs are named smmu_0_<phys_addr_page> where <phys_addr_page>
> > is the physical page address of the SMMU PMCG.
> > For example, the SMMU PMCG at 0xff88840000 is named smmu_0_ff88840
>
> This seems a bit rough - is it at feasible to at least chase the node
> reference and namespace them by the associated component, e.g. something
> like "arm-smmu-v3.x:pmcg.y"? The user can always dig the associated
> physical address out of /proc/iomem if necessary.
>
Assuming x indicates the smmu v3 node id and y indicates pmcg group id.
If are making such a assumption,
# Would like to clarified if the architecture gurantees that a smmu pmcg group is always
associated with a specific SMMU node.
From, Section 10.2 in SMMU Architecture Specification
"A counter group is conceptually free-standing and has no interdependency on the behavior of other counter
groups or even on the SMMU configuration itself"
Does the above and also Fig 19 from the same section,
imply that a PMCG group need not be restricted to a SMMU node ?
> > Filtering by stream id is done by specifying filtering parameters
> > with the event. Options are:
> > filter_enable - 0 = no filtering, 1 = filtering enabled
> > filter_span - 0 = exact match, 1 = pattern match
> > filter_sec - applies to non-secure (0) or secure (1) namespace
>
> I'm a little dubious as to how useful it is to expose this, since we
> can't see the true value of SCR.SO so have no way of knowing what we'll
> actually end up counting.
>
> > filter_stream_id - pattern to filter against
> > Further filtering information is available in the SMMU documentation.
> >
> > Example: perf stat -e smmu_0_ff88840/transaction,filter_enable=1,
> > filter_span=1,filter_stream_id=0x42/ -a pwd
> > Applies filter pattern 0x42 to transaction events.
> >
> > SMMU events are not attributable to a CPU, so task mode and sampling
> > are not supported.
> >
> > Signed-off-by: Neil Leeder <nleeder@codeaurora.org>
> > ---
> > drivers/perf/Kconfig | 9 +
> > drivers/perf/Makefile | 1 +
> > drivers/perf/arm_smmuv3_pmu.c | 813 ++++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 823 insertions(+)
> > create mode 100644 drivers/perf/arm_smmuv3_pmu.c
> >
> > diff --git a/drivers/perf/Kconfig b/drivers/perf/Kconfig
> > index e5197ff..e7721d1 100644
> > --- a/drivers/perf/Kconfig
> > +++ b/drivers/perf/Kconfig
> > @@ -17,6 +17,15 @@ config ARM_PMU_ACPI
> > depends on ARM_PMU && ACPI
> > def_bool y
> >
> > +config ARM_SMMUV3_PMU
> > + bool "ARM SMMUv3 PMU"
> > + depends on PERF_EVENTS && ARM64 && ACPI
>
> PERF_EVENTS is already a top-level dependency now.
>
> > + help
> > + Provides support for the SMMU version 3 performance monitor unit (PMU)
> > + on ARM-based systems.
> > + Adds the SMMU PMU into the perf events subsystem for
> > + monitoring SMMU performance events.
> > +
> > config QCOM_L2_PMU
> > bool "Qualcomm Technologies L2-cache PMU"
> > depends on ARCH_QCOM && ARM64 && ACPI
> > diff --git a/drivers/perf/Makefile b/drivers/perf/Makefile
> > index 6420bd4..3012f5e 100644
> > --- a/drivers/perf/Makefile
> > +++ b/drivers/perf/Makefile
> > @@ -1,5 +1,6 @@
> > obj-$(CONFIG_ARM_PMU) += arm_pmu.o arm_pmu_platform.o
> > obj-$(CONFIG_ARM_PMU_ACPI) += arm_pmu_acpi.o
> > +obj-$(CONFIG_ARM_SMMUV3_PMU) += arm_smmuv3_pmu.o
> > obj-$(CONFIG_QCOM_L2_PMU) += qcom_l2_pmu.o
> > obj-$(CONFIG_QCOM_L3_PMU) += qcom_l3_pmu.o
> > obj-$(CONFIG_XGENE_PMU) += xgene_pmu.o
> > diff --git a/drivers/perf/arm_smmuv3_pmu.c b/drivers/perf/arm_smmuv3_pmu.c
> > new file mode 100644
> > index 0000000..1e70791
> > --- /dev/null
> > +++ b/drivers/perf/arm_smmuv3_pmu.c
> > @@ -0,0 +1,813 @@
> > +/* Copyright (c) 2017 The Linux Foundation. All rights reserved.
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 and
> > + * only version 2 as published by the Free Software Foundation.
> > + *
> > + * 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.
> > + */
> > +
> > +/*
> > + * This driver adds support for perf events to use the Performance
> > + * Monitor Counter Groups (PMCG) associated with an SMMUv3 node
> > + * to monitor that node.
> > + *
> > + * Devices are named smmu_0_<phys_addr_page> where <phys_addr_page>
> > + * is the physical page address of the SMMU PMCG.
> > + * For example, the SMMU PMCG at 0xff88840000 is named smmu_0_ff88840
> > + *
> > + * Filtering by stream id is done by specifying filtering parameters
> > + * with the event. options are:
> > + * filter_enable - 0 = no filtering, 1 = filtering enabled
> > + * filter_span - 0 = exact match, 1 = pattern match
> > + * filter_sec - filter applies to non-secure (0) or secure (1) namespace
> > + * filter_stream_id - pattern to filter against
> > + * Further filtering information is available in the SMMU documentation.
> > + *
> > + * Example: perf stat -e smmu_0_ff88840/transaction,filter_enable=1,
> > + * filter_span=1,filter_stream_id=0x42/ -a pwd
> > + * Applies filter pattern 0x42 to transaction events.
> > + *
> > + * SMMU events are not attributable to a CPU, so task mode and sampling
> > + * are not supported.
> > + */
> > +
> > +#include <linux/acpi.h>
> > +#include <linux/acpi_iort.h>
> > +#include <linux/bitops.h>
> > +#include <linux/cpuhotplug.h>
> > +#include <linux/cpumask.h>
> > +#include <linux/device.h>
> > +#include <linux/errno.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/irq.h>
> > +#include <linux/kernel.h>
> > +#include <linux/list.h>
> > +#include <linux/msi.h>
>
> Is MSI support planned?
>
> > +#include <linux/perf_event.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/smp.h>
> > +#include <linux/sysfs.h>
> > +#include <linux/types.h>
> > +
> > +#include <asm/local64.h>
> > +
> > +#define SMMU_PMCG_EVCNTR0 0x0
> > +#define SMMU_PMCG_EVCNTR(n, stride) (SMMU_PMCG_EVCNTR0 + (n) * (stride))
> > +#define SMMU_PMCG_EVTYPER0 0x400
> > +#define SMMU_PMCG_EVTYPER(n) (SMMU_PMCG_EVTYPER0 + (n) * 4)
> > +#define SMMU_PMCG_EVTYPER_SEC_SID_SHIFT 30
> > +#define SMMU_PMCG_EVTYPER_SID_SPAN_SHIFT 29
> > +#define SMMU_PMCG_EVTYPER_EVENT_MASK GENMASK(15, 0)
> > +#define SMMU_PMCG_SVR0 0x600
> > +#define SMMU_PMCG_SVR(n, stride) (SMMU_PMCG_SVR0 + (n) * (stride))
> > +#define SMMU_PMCG_SMR0 0xA00
> > +#define SMMU_PMCG_SMR(n) (SMMU_PMCG_SMR0 + (n) * 4)
> > +#define SMMU_PMCG_CNTENSET0 0xC00
> > +#define SMMU_PMCG_CNTENCLR0 0xC20
> > +#define SMMU_PMCG_INTENSET0 0xC40
> > +#define SMMU_PMCG_INTENCLR0 0xC60
> > +#define SMMU_PMCG_OVSCLR0 0xC80
> > +#define SMMU_PMCG_OVSSET0 0xCC0
> > +#define SMMU_PMCG_CAPR 0xD88
> > +#define SMMU_PMCG_SCR 0xDF8
> > +#define SMMU_PMCG_CFGR 0xE00
> > +#define SMMU_PMCG_CFGR_SID_FILTER_TYPE BIT(23)
> > +#define SMMU_PMCG_CFGR_CAPTURE BIT(22)
> > +#define SMMU_PMCG_CFGR_MSI BIT(21)
> > +#define SMMU_PMCG_CFGR_RELOC_CTRS BIT(20)
> > +#define SMMU_PMCG_CFGR_SIZE_MASK GENMASK(13, 8)
> > +#define SMMU_PMCG_CFGR_SIZE_SHIFT 8
> > +#define SMMU_PMCG_CFGR_COUNTER_SIZE_32 31
> > +#define SMMU_PMCG_CFGR_NCTR_MASK GENMASK(5, 0)
> > +#define SMMU_PMCG_CFGR_NCTR_SHIFT 0
> > +#define SMMU_PMCG_CR 0xE04
> > +#define SMMU_PMCG_CR_ENABLE BIT(0)
> > +#define SMMU_PMCG_CEID0 0xE20
> > +#define SMMU_PMCG_CEID1 0xE28
> > +#define SMMU_PMCG_IRQ_CTRL 0xE50
> > +#define SMMU_PMCG_IRQ_CTRL_IRQEN BIT(0)
> > +#define SMMU_PMCG_IRQ_CTRLACK 0xE54
> > +#define SMMU_PMCG_IRQ_CTRLACK_IRQEN BIT(0)
> > +#define SMMU_PMCG_IRQ_CFG0 0xE58
> > +#define SMMU_PMCG_IRQ_CFG0_ADDR_MASK GENMASK(51, 2)
> > +#define SMMU_PMCG_IRQ_CFG1 0xE60
> > +#define SMMU_PMCG_IRQ_CFG2 0xE64
> > +#define SMMU_PMCG_IRQ_STATUS 0xE68
> > +#define SMMU_PMCG_IRQ_STATUS_IRQ_ABT BIT(0)
> > +#define SMMU_PMCG_AIDR 0xE70
>
> Several of these are unused (although at least IRQ0_CFG1 probably should
> be, to zero it to a known state if we aren't supporting MSIs).
>
> > +#define SMMU_COUNTER_RELOAD BIT(31)
> > +#define SMMU_DEFAULT_FILTER_SEC 0
> > +#define SMMU_DEFAULT_FILTER_SPAN 1
> > +#define SMMU_DEFAULT_FILTER_STREAM_ID GENMASK(31, 0)
> > +
> > +#define SMMU_MAX_COUNTERS 64
> > +#define SMMU_MAX_EVENT_ID 128
> > +#define SMMU_NUM_EVENTS_U32 (SMMU_MAX_EVENT_ID / sizeof(u32))
> > +
> > +#define SMMU_PA_SHIFT 12
> > +
> > +/* Events */
> > +#define SMMU_PMU_CYCLES 0
> > +#define SMMU_PMU_TRANSACTION 1
> > +#define SMMU_PMU_TLB_MISS 2
> > +#define SMMU_PMU_CONFIG_CACHE_MISS 3
> > +#define SMMU_PMU_TRANS_TABLE_WALK 4
> > +#define SMMU_PMU_CONFIG_STRUCT_ACCESS 5
> > +#define SMMU_PMU_PCIE_ATS_TRANS_RQ 6
> > +#define SMMU_PMU_PCIE_ATS_TRANS_PASSED 7
> > +
> > +static int cpuhp_state_num;
> > +
> > +struct smmu_pmu {
> > + struct hlist_node node;
> > + struct perf_event *events[SMMU_MAX_COUNTERS];
> > + DECLARE_BITMAP(used_counters, SMMU_MAX_COUNTERS);
> > + DECLARE_BITMAP(supported_events, SMMU_MAX_EVENT_ID);
> > + unsigned int irq;
> > + unsigned int on_cpu;
> > + struct pmu pmu;
> > + unsigned int num_counters;
> > + struct platform_device *pdev;
> > + void __iomem *reg_base;
> > + void __iomem *reloc_base;
> > + u64 counter_present_mask;
> > + u64 counter_mask;
> > + bool reg_size_32;
>
> This guy is redundant...
>
> > +};
> > +
> > +#define to_smmu_pmu(p) (container_of(p, struct smmu_pmu, pmu))
> > +
> > +#define SMMU_PMU_EVENT_ATTR_EXTRACTOR(_name, _config, _size, _shift) \
> > + static inline u32 get_##_name(struct perf_event *event) \
> > + { \
> > + return (event->attr._config >> (_shift)) & \
> > + GENMASK_ULL((_size) - 1, 0); \
> > + }
> > +
> > +SMMU_PMU_EVENT_ATTR_EXTRACTOR(event, config, 16, 0);
> > +SMMU_PMU_EVENT_ATTR_EXTRACTOR(filter_stream_id, config1, 32, 0);
> > +SMMU_PMU_EVENT_ATTR_EXTRACTOR(filter_span, config1, 1, 32);
> > +SMMU_PMU_EVENT_ATTR_EXTRACTOR(filter_sec, config1, 1, 33);
> > +SMMU_PMU_EVENT_ATTR_EXTRACTOR(filter_enable, config1, 1, 34);
> > +
> > +static inline void smmu_pmu_enable(struct pmu *pmu)
> > +{
> > + struct smmu_pmu *smmu_pmu = to_smmu_pmu(pmu);
> > +
> > + writel(SMMU_PMCG_CR_ENABLE, smmu_pmu->reg_base + SMMU_PMCG_CR);
> > + writel(SMMU_PMCG_IRQ_CTRL_IRQEN,
> > + smmu_pmu->reg_base + SMMU_PMCG_IRQ_CTRL);
> > +}
> > +
> > +static inline void smmu_pmu_disable(struct pmu *pmu)
> > +{
> > + struct smmu_pmu *smmu_pmu = to_smmu_pmu(pmu);
> > +
> > + writel(0, smmu_pmu->reg_base + SMMU_PMCG_CR);
> > + writel(0, smmu_pmu->reg_base + SMMU_PMCG_IRQ_CTRL);
> > +}
> > +
> > +static inline void smmu_pmu_counter_set_value(struct smmu_pmu *smmu_pmu,
> > + u32 idx, u64 value)
> > +{
> > + if (smmu_pmu->reg_size_32)
>
> ...since it would be just as efficient to directly test
> smmu_pmu->counter_mask & BIT(32) here and below.
>
> > + writel(value, smmu_pmu->reloc_base + SMMU_PMCG_EVCNTR(idx, 4));
> > + else
> > + writeq(value, smmu_pmu->reloc_base + SMMU_PMCG_EVCNTR(idx, 8));
> > +}
> > +
> > +static inline u64 smmu_pmu_counter_get_value(struct smmu_pmu *smmu_pmu, u32 idx)
> > +{
> > + u64 value;
> > +
> > + if (smmu_pmu->reg_size_32)
> > + value = readl(smmu_pmu->reloc_base + SMMU_PMCG_EVCNTR(idx, 4));
> > + else
> > + value = readq(smmu_pmu->reloc_base + SMMU_PMCG_EVCNTR(idx, 8));
> > +
> > + return value;
> > +}
> > +
> > +static inline void smmu_pmu_counter_enable(struct smmu_pmu *smmu_pmu, u32 idx)
> > +{
> > + writeq(BIT(idx), smmu_pmu->reg_base + SMMU_PMCG_CNTENSET0);
> > +}
> > +
> > +static inline void smmu_pmu_counter_disable(struct smmu_pmu *smmu_pmu, u32 idx)
> > +{
> > + writeq(BIT(idx), smmu_pmu->reg_base + SMMU_PMCG_CNTENCLR0);
> > +}
> > +
> > +static inline void smmu_pmu_interrupt_enable(struct smmu_pmu *smmu_pmu, u32 idx)
> > +{
> > + writeq(BIT(idx), smmu_pmu->reg_base + SMMU_PMCG_INTENSET0);
> > +}
> > +
> > +static inline void smmu_pmu_interrupt_disable(struct smmu_pmu *smmu_pmu,
> > + u32 idx)
> > +{
> > + writeq(BIT(idx), smmu_pmu->reg_base + SMMU_PMCG_INTENCLR0);
> > +}
> > +
> > +static void smmu_pmu_reset(struct smmu_pmu *smmu_pmu)
> > +{
> > + unsigned int i;
> > +
> > + for (i = 0; i < smmu_pmu->num_counters; i++) {
> > + smmu_pmu_counter_disable(smmu_pmu, i);
> > + smmu_pmu_interrupt_disable(smmu_pmu, i);
> > + }
>
> Surely it would be far quicker and simpler to do this?
>
> writeq(smmu_pmu->counter_present_mask,
> smmu_pmu->reg_base + SMMU_PMCG_CNTENCLR0);
> writeq(smmu_pmu->counter_present_mask,
> smmu_pmu->reg_base + SMMU_PMCG_INTENCLR0);
>
> > + smmu_pmu_disable(&smmu_pmu->pmu);
> > +}
> > +
> > +static inline void smmu_pmu_set_evtyper(struct smmu_pmu *smmu_pmu, u32 idx,
> > + u32 val)
> > +{
> > + writel(val, smmu_pmu->reg_base + SMMU_PMCG_EVTYPER(idx));
> > +}
> > +
> > +static inline void smmu_pmu_set_smr(struct smmu_pmu *smmu_pmu, u32 idx, u32 val)
> > +{
> > + writel(val, smmu_pmu->reg_base + SMMU_PMCG_SMR(idx));
> > +}
> > +
> > +static inline u64 smmu_pmu_getreset_ovsr(struct smmu_pmu *smmu_pmu)
> > +{
> > + u64 result = readq_relaxed(smmu_pmu->reloc_base + SMMU_PMCG_OVSSET0);
> > +
> > + writeq(result, smmu_pmu->reloc_base + SMMU_PMCG_OVSCLR0);
> > + return result;
> > +}
> > +
> > +static inline bool smmu_pmu_has_overflowed(struct smmu_pmu *smmu_pmu, u64 ovsr)
> > +{
> > + return !!(ovsr & smmu_pmu->counter_present_mask);
> > +}
>
> Personally, I find these helpers abstracting simple reads/writes
> actually make the code harder to follow, especially when they're each
> used a grand total of once. That may well just be me, though.
>
> > +static void smmu_pmu_event_update(struct perf_event *event)
> > +{
> > + struct hw_perf_event *hwc = &event->hw;
> > + struct smmu_pmu *smmu_pmu = to_smmu_pmu(event->pmu);
> > + u64 delta, prev, now;
> > + u32 idx = hwc->idx;
> > +
> > + do {
> > + prev = local64_read(&hwc->prev_count);
> > + now = smmu_pmu_counter_get_value(smmu_pmu, idx);
> > + } while (local64_cmpxchg(&hwc->prev_count, prev, now) != prev);
> > +
> > + /* handle overflow. */
> > + delta = now - prev;
> > + delta &= smmu_pmu->counter_mask;
> > +
> > + local64_add(delta, &event->count);
> > +}
> > +
> > +static void smmu_pmu_set_period(struct smmu_pmu *smmu_pmu,
> > + struct hw_perf_event *hwc)
> > +{
> > + u32 idx = hwc->idx;
> > + u64 new;
> > +
> > + /*
> > + * We limit the max period to half the max counter value of the smallest
> > + * counter size, so that even in the case of extreme interrupt latency
> > + * the counter will (hopefully) not wrap past its initial value.
> > + */
>
> Having once fought to properly understand the underlying logic I despise
> this unhelpfully-vague comment, but that's not your fault ;)
>
> > + new = SMMU_COUNTER_RELOAD;
>
> Given that we *are* following the "use the top counter bit as an
> implicit overflow bit" pattern of arm_pmu, it feels a bit weird to not
> use the actual half-maximum value here (especially since it's easily
> computable from counter_mask). I'm about 85% sure it probably still
> works, but as ever inconsistency breeds uncertainty.
> > + local64_set(&hwc->prev_count, new);
> > + smmu_pmu_counter_set_value(smmu_pmu, idx, new);
> > +}
> > +
> > +static irqreturn_t smmu_pmu_handle_irq(int irq_num, void *data)
> > +{
> > + struct smmu_pmu *smmu_pmu = data;
> > + u64 ovsr;
> > + unsigned int idx;
> > +
> > + ovsr = smmu_pmu_getreset_ovsr(smmu_pmu);
> > + if (!smmu_pmu_has_overflowed(smmu_pmu, ovsr))
>
> You have an architectural guarantee that unimplemented bits of OVSSET0
> are RES0, so checking !ovsr is sufficient.
>
> > + return IRQ_NONE;
> > +
> > + for_each_set_bit(idx, (unsigned long *)&ovsr, smmu_pmu->num_counters) {
> > + struct perf_event *event = smmu_pmu->events[idx];
> > + struct hw_perf_event *hwc;
> > +
> > + if (WARN_ON_ONCE(!event))
> > + continue;
> > +
> > + smmu_pmu_event_update(event);
> > + hwc = &event->hw;
> > +
> > + smmu_pmu_set_period(smmu_pmu, hwc);
> > + }
> > +
> > + return IRQ_HANDLED;
> > +}
> > +
> > +static unsigned int smmu_pmu_get_event_idx(struct smmu_pmu *smmu_pmu)
> > +{
> > + unsigned int idx;
> > + unsigned int num_ctrs = smmu_pmu->num_counters;
> > +
> > + idx = find_first_zero_bit(smmu_pmu->used_counters, num_ctrs);
> > + if (idx == num_ctrs)
> > + /* The counters are all in use. */
> > + return -EAGAIN;
> > +
> > + set_bit(idx, smmu_pmu->used_counters);
> > +
> > + return idx;
> > +}
> > +
> > +/*
> > + * Implementation of abstract pmu functionality required by
> > + * the core perf events code.
> > + */
> > +
> > +static int smmu_pmu_event_init(struct perf_event *event)
> > +{
> > + struct hw_perf_event *hwc = &event->hw;
> > + struct perf_event *sibling;
> > + struct smmu_pmu *smmu_pmu;
> > + u32 event_id;
> > +
> > + if (event->attr.type != event->pmu->type)
> > + return -ENOENT;
> > +
> > + smmu_pmu = to_smmu_pmu(event->pmu);
> > +
> > + if (hwc->sample_period) {
> > + dev_dbg_ratelimited(&smmu_pmu->pdev->dev,
> > + "Sampling not supported\n");
> > + return -EOPNOTSUPP;
> > + }
> > +
> > + if (event->cpu < 0) {
> > + dev_dbg_ratelimited(&smmu_pmu->pdev->dev,
> > + "Per-task mode not supported\n");
> > + return -EOPNOTSUPP;
> > + }
> > +
> > + /* We cannot filter accurately so we just don't allow it. */
> > + if (event->attr.exclude_user || event->attr.exclude_kernel ||
> > + event->attr.exclude_hv || event->attr.exclude_idle) {
> > + dev_dbg_ratelimited(&smmu_pmu->pdev->dev,
> > + "Can't exclude execution levels\n");
> > + return -EOPNOTSUPP;
> > + }
> > +
> > + /* Verify specified event is supported on this PMU */
> > + event_id = get_event(event);
> > + if ((event_id >= SMMU_MAX_EVENT_ID) ||
>
> What about raw imp-def events?
>
> > + (!test_bit(event_id, smmu_pmu->supported_events))) {
> > + dev_dbg_ratelimited(&smmu_pmu->pdev->dev,
> > + "Invalid event %d for this PMU\n",
> > + event_id);
> > + return -EINVAL;
> > + }
> > +
> > + /* Don't allow groups with mixed PMUs, except for s/w events */
> > + if (event->group_leader->pmu != event->pmu &&
> > + !is_software_event(event->group_leader)) {
> > + dev_dbg_ratelimited(&smmu_pmu->pdev->dev,
> > + "Can't create mixed PMU group\n");
> > + return -EINVAL;
> > + }
> > +
> > + list_for_each_entry(sibling, &event->group_leader->sibling_list,
> > + group_entry)
> > + if (sibling->pmu != event->pmu &&
> > + !is_software_event(sibling)) {
> > + dev_dbg_ratelimited(&smmu_pmu->pdev->dev,
> > + "Can't create mixed PMU group\n");
> > + return -EINVAL;
> > + }
> > +
> > + /* Ensure all events in a group are on the same cpu */
> > + if ((event->group_leader != event) &&
> > + (event->cpu != event->group_leader->cpu)) {
> > + dev_dbg_ratelimited(&smmu_pmu->pdev->dev,
> > + "Can't create group on CPUs %d and %d",
> > + event->cpu, event->group_leader->cpu);
> > + return -EINVAL;
> > + }
> > +
> > + hwc->idx = -1;
> > +
> > + /*
> > + * Ensure all events are on the same cpu so all events are in the
> > + * same cpu context, to avoid races on pmu_enable etc.
> > + */
> > + event->cpu = smmu_pmu->on_cpu;
> > +
> > + return 0;
> > +}
> > +
> > +static void smmu_pmu_event_start(struct perf_event *event, int flags)
> > +{
> > + struct smmu_pmu *smmu_pmu = to_smmu_pmu(event->pmu);
> > + struct hw_perf_event *hwc = &event->hw;
> > + int idx = hwc->idx;
> > + u32 evtyper;
> > + u32 filter_sec;
> > + u32 filter_span;
> > + u32 filter_stream_id;
> > +
> > + hwc->state = 0;
> > +
> > + smmu_pmu_set_period(smmu_pmu, hwc);
> > +
> > + if (get_filter_enable(event)) {
> > + filter_sec = get_filter_sec(event);
> > + filter_span = get_filter_span(event);
> > + filter_stream_id = get_filter_stream_id(event);
> > + } else {
> > + filter_sec = SMMU_DEFAULT_FILTER_SEC;
> > + filter_span = SMMU_DEFAULT_FILTER_SPAN;
> > + filter_stream_id = SMMU_DEFAULT_FILTER_STREAM_ID;
> > + }
> > +
> > + evtyper = get_event(event) |
> > + filter_span << SMMU_PMCG_EVTYPER_SID_SPAN_SHIFT |
> > + filter_sec << SMMU_PMCG_EVTYPER_SEC_SID_SHIFT;
> > +
> > + smmu_pmu_set_evtyper(smmu_pmu, idx, evtyper);
> > + smmu_pmu_set_smr(smmu_pmu, idx, filter_stream_id);
> > + smmu_pmu_interrupt_enable(smmu_pmu, idx);
> > + smmu_pmu_counter_enable(smmu_pmu, idx);
> > +}
> > +
> > +static void smmu_pmu_event_stop(struct perf_event *event, int flags)
> > +{
> > + struct smmu_pmu *smmu_pmu = to_smmu_pmu(event->pmu);
> > + struct hw_perf_event *hwc = &event->hw;
> > + int idx = hwc->idx;
> > +
> > + if (hwc->state & PERF_HES_STOPPED)
> > + return;
> > +
> > + smmu_pmu_interrupt_disable(smmu_pmu, idx);
> > + smmu_pmu_counter_disable(smmu_pmu, idx);
> > +
> > + if (flags & PERF_EF_UPDATE)
> > + smmu_pmu_event_update(event);
> > + hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
> > +}
> > +
> > +static int smmu_pmu_event_add(struct perf_event *event, int flags)
> > +{
> > + struct hw_perf_event *hwc = &event->hw;
> > + int idx;
> > + struct smmu_pmu *smmu_pmu = to_smmu_pmu(event->pmu);
> > +
> > + idx = smmu_pmu_get_event_idx(smmu_pmu);
> > + if (idx < 0)
> > + return idx;
> > +
> > + hwc->idx = idx;
> > + hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
> > + smmu_pmu->events[idx] = event;
> > + local64_set(&hwc->prev_count, 0);
> > +
> > + if (flags & PERF_EF_START)
> > + smmu_pmu_event_start(event, flags);
> > +
> > + /* Propagate changes to the userspace mapping. */
> > + perf_event_update_userpage(event);
> > +
> > + return 0;
> > +}
> > +
> > +static void smmu_pmu_event_del(struct perf_event *event, int flags)
> > +{
> > + struct hw_perf_event *hwc = &event->hw;
> > + struct smmu_pmu *smmu_pmu = to_smmu_pmu(event->pmu);
> > + int idx = hwc->idx;
> > +
> > + smmu_pmu_event_stop(event, flags | PERF_EF_UPDATE);
> > + smmu_pmu->events[idx] = NULL;
> > + clear_bit(idx, smmu_pmu->used_counters);
> > +
> > + perf_event_update_userpage(event);
> > +}
> > +
> > +static void smmu_pmu_event_read(struct perf_event *event)
> > +{
> > + smmu_pmu_event_update(event);
> > +}
> > +
> > +/* cpumask */
> > +
> > +static ssize_t smmu_pmu_cpumask_show(struct device *dev,
> > + struct device_attribute *attr,
> > + char *buf)
> > +{
> > + struct smmu_pmu *smmu_pmu = to_smmu_pmu(dev_get_drvdata(dev));
> > +
> > + return cpumap_print_to_pagebuf(true, buf, cpumask_of(smmu_pmu->on_cpu));
> > +}
> > +
> > +static struct device_attribute smmu_pmu_cpumask_attr =
> > + __ATTR(cpumask, 0444, smmu_pmu_cpumask_show, NULL);
> > +
> > +static struct attribute *smmu_pmu_cpumask_attrs[] = {
> > + &smmu_pmu_cpumask_attr.attr,
> > + NULL,
> > +};
> > +
> > +static struct attribute_group smmu_pmu_cpumask_group = {
> > + .attrs = smmu_pmu_cpumask_attrs,
> > +};
> > +
> > +/* Events */
> > +
> > +ssize_t smmu_pmu_event_show(struct device *dev,
> > + struct device_attribute *attr, char *page)
> > +{
> > + struct perf_pmu_events_attr *pmu_attr;
> > +
> > + pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr);
> > +
> > + return sprintf(page, "event=0x%02llx\n", pmu_attr->id);
> > +}
> > +
> > +#define SMMU_EVENT_ATTR(_name, _id) \
> > + (&((struct perf_pmu_events_attr[]) { \
> > + { .attr = __ATTR(_name, 0444, smmu_pmu_event_show, NULL), \
> > + .id = _id, } \
> > + })[0].attr.attr)
> > +
> > +static struct attribute *smmu_pmu_events[] = {
> > + SMMU_EVENT_ATTR(cycles, SMMU_PMU_CYCLES),
> > + SMMU_EVENT_ATTR(transaction, SMMU_PMU_TRANSACTION),
> > + SMMU_EVENT_ATTR(tlb_miss, SMMU_PMU_TLB_MISS),
> > + SMMU_EVENT_ATTR(config_cache_miss, SMMU_PMU_CONFIG_CACHE_MISS),
> > + SMMU_EVENT_ATTR(trans_table_walk, SMMU_PMU_TRANS_TABLE_WALK),
> > + SMMU_EVENT_ATTR(config_struct_access, SMMU_PMU_CONFIG_STRUCT_ACCESS),
> > + SMMU_EVENT_ATTR(pcie_ats_trans_rq, SMMU_PMU_PCIE_ATS_TRANS_RQ),
> > + SMMU_EVENT_ATTR(pcie_ats_trans_passed, SMMU_PMU_PCIE_ATS_TRANS_PASSED),
> > + NULL
> > +};
> > +
> > +static umode_t smmu_pmu_event_is_visible(struct kobject *kobj,
> > + struct attribute *attr, int unused)
> > +{
> > + struct device *dev = kobj_to_dev(kobj);
> > + struct smmu_pmu *smmu_pmu = to_smmu_pmu(dev_get_drvdata(dev));
> > + struct perf_pmu_events_attr *pmu_attr;
> > +
> > + pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr.attr);
> > +
> > + if (test_bit(pmu_attr->id, smmu_pmu->supported_events))
> > + return attr->mode;
> > +
> > + return 0;
> > +}
> > +static struct attribute_group smmu_pmu_events_group = {
> > + .name = "events",
> > + .attrs = smmu_pmu_events,
> > + .is_visible = smmu_pmu_event_is_visible,
> > +};
> > +
> > +/* Formats */
> > +PMU_FORMAT_ATTR(event, "config:0-15");
> > +PMU_FORMAT_ATTR(filter_stream_id, "config1:0-31");
> > +PMU_FORMAT_ATTR(filter_span, "config1:32");
> > +PMU_FORMAT_ATTR(filter_sec, "config1:33");
> > +PMU_FORMAT_ATTR(filter_enable, "config1:34");
> > +
> > +static struct attribute *smmu_pmu_formats[] = {
> > + &format_attr_event.attr,
> > + &format_attr_filter_stream_id.attr,
> > + &format_attr_filter_span.attr,
> > + &format_attr_filter_sec.attr,
> > + &format_attr_filter_enable.attr,
> > + NULL
> > +};
> > +
> > +static struct attribute_group smmu_pmu_format_group = {
> > + .name = "format",
> > + .attrs = smmu_pmu_formats,
> > +};
> > +
> > +static const struct attribute_group *smmu_pmu_attr_grps[] = {
> > + &smmu_pmu_cpumask_group,
> > + &smmu_pmu_events_group,
> > + &smmu_pmu_format_group,
> > + NULL,
> > +};
> > +
> > +/*
> > + * Generic device handlers
> > + */
> > +
> > +static unsigned int get_num_counters(struct smmu_pmu *smmu_pmu)
> > +{
> > + u32 cfgr = readl(smmu_pmu->reg_base + SMMU_PMCG_CFGR);
> > +
> > + return ((cfgr & SMMU_PMCG_CFGR_NCTR_MASK) >> SMMU_PMCG_CFGR_NCTR_SHIFT)
> > + + 1;
> > +}
> > +
> > +static int smmu_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node)
> > +{
> > + struct smmu_pmu *smmu_pmu;
> > + unsigned int target;
> > +
> > + smmu_pmu = hlist_entry_safe(node, struct smmu_pmu, node);
>
> Is it ever valid for node to be NULL? If we can't trust it to be one of
> the PMUs we registered I think all bets are off anyway.
>
> > + if (cpu != smmu_pmu->on_cpu)
> > + return 0;
> > +
> > + target = cpumask_any_but(cpu_online_mask, cpu);
> > + if (target >= nr_cpu_ids)
> > + return 0;
> > +
> > + perf_pmu_migrate_context(&smmu_pmu->pmu, cpu, target);
> > + smmu_pmu->on_cpu = target;
> > + WARN_ON(irq_set_affinity(smmu_pmu->irq, cpumask_of(target)));
> > +
> > + return 0;
> > +}
> > +
> > +static int smmu_pmu_probe(struct platform_device *pdev)
> > +{
> > + struct smmu_pmu *smmu_pmu;
> > + struct resource *mem_resource_0, *mem_resource_1;
> > + void __iomem *mem_map_0, *mem_map_1;
> > + unsigned int reg_size;
> > + int err;
> > + int irq;
> > + u32 ceid[SMMU_NUM_EVENTS_U32];
> > + u64 ceid_64;
> > +
> > + smmu_pmu = devm_kzalloc(&pdev->dev, sizeof(*smmu_pmu), GFP_KERNEL);
> > + if (!smmu_pmu)
> > + return -ENOMEM;
> > +
> > + platform_set_drvdata(pdev, smmu_pmu);
> > + smmu_pmu->pmu = (struct pmu) {
> > + .task_ctx_nr = perf_invalid_context,
> > + .pmu_enable = smmu_pmu_enable,
> > + .pmu_disable = smmu_pmu_disable,
> > + .event_init = smmu_pmu_event_init,
> > + .add = smmu_pmu_event_add,
> > + .del = smmu_pmu_event_del,
> > + .start = smmu_pmu_event_start,
> > + .stop = smmu_pmu_event_stop,
> > + .read = smmu_pmu_event_read,
> > + .attr_groups = smmu_pmu_attr_grps,
> > + };
> > +
> > + mem_resource_0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > + mem_map_0 = devm_ioremap_resource(&pdev->dev, mem_resource_0);
> > +
> > + if (IS_ERR(mem_map_0)) {
> > + dev_err(&pdev->dev, "Can't map SMMU PMU @%pa\n",
> > + &mem_resource_0->start);
> > + return PTR_ERR(mem_map_0);
> > + }
> > +
> > + smmu_pmu->reg_base = mem_map_0;
> > + smmu_pmu->pmu.name =
> > + devm_kasprintf(&pdev->dev, GFP_KERNEL, "smmu_0_%llx",
> > + (mem_resource_0->start) >> SMMU_PA_SHIFT);
> > +
> > + if (!smmu_pmu->pmu.name) {
> > + dev_err(&pdev->dev, "Failed to create PMU name");
> > + return -EINVAL;
> > + }
> > +
> > + ceid_64 = readq(smmu_pmu->reg_base + SMMU_PMCG_CEID0);
> > + ceid[0] = ceid_64 & GENMASK(31, 0);
>
> It took a second look to determine that that masking does nothing...
>
> > + ceid[1] = ceid_64 >> 32;
> > + ceid_64 = readq(smmu_pmu->reg_base + SMMU_PMCG_CEID1);
> > + ceid[2] = ceid_64 & GENMASK(31, 0);
> > + ceid[3] = ceid_64 >> 32;
> > + bitmap_from_u32array(smmu_pmu->supported_events, SMMU_MAX_EVENT_ID,
> > + ceid, SMMU_NUM_EVENTS_U32);
>
> ...but then the whole lot might be cleaner and simpler with a u64[2]
> cast to u32* (or unioned to u32[4]) as necessary.
>
> > +
> > + /* Determine if page 1 is present */
> > + if (readl(smmu_pmu->reg_base + SMMU_PMCG_CFGR) &
> > + SMMU_PMCG_CFGR_RELOC_CTRS) {
> > + mem_resource_1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> > + mem_map_1 = devm_ioremap_resource(&pdev->dev, mem_resource_1);
> > +
> > + if (IS_ERR(mem_map_1)) {
> > + dev_err(&pdev->dev, "Can't map SMMU PMU @%pa\n",
> > + &mem_resource_1->start);
> > + return PTR_ERR(mem_map_1);
> > + }
> > + smmu_pmu->reloc_base = mem_map_1;
> > + } else {
> > + smmu_pmu->reloc_base = smmu_pmu->reg_base;
> > + }
> > +
> > + irq = platform_get_irq(pdev, 0);
> > + if (irq < 0) {
> > + dev_err(&pdev->dev,
> > + "Failed to get valid irq for smmu @%pa\n",
> > + &mem_resource_0->start);
> > + return irq;
> > + }
> > +
> > + err = devm_request_irq(&pdev->dev, irq, smmu_pmu_handle_irq,
> > + IRQF_NOBALANCING | IRQF_SHARED | IRQF_NO_THREAD,
> > + "smmu-pmu", smmu_pmu);
> > + if (err) {
> > + dev_err(&pdev->dev,
> > + "Unable to request IRQ%d for SMMU PMU counters\n", irq);
> > + return err;
> > + }
> > +
> > + smmu_pmu->irq = irq;
> > +
> > + /* Pick one CPU to be the preferred one to use */
> > + smmu_pmu->on_cpu = smp_processor_id();
> > + WARN_ON(irq_set_affinity(smmu_pmu->irq, cpumask_of(smmu_pmu->on_cpu)));
> > +
> > + smmu_pmu->num_counters = get_num_counters(smmu_pmu);
> > + smmu_pmu->pdev = pdev;
> > + smmu_pmu->counter_present_mask = GENMASK(smmu_pmu->num_counters - 1, 0);
> > + reg_size = (readl(smmu_pmu->reg_base + SMMU_PMCG_CFGR) &
> > + SMMU_PMCG_CFGR_SIZE_MASK) >> SMMU_PMCG_CFGR_SIZE_SHIFT;
> > + smmu_pmu->reg_size_32 = (reg_size == SMMU_PMCG_CFGR_COUNTER_SIZE_32);
> > + smmu_pmu->counter_mask = GENMASK_ULL(reg_size, 0);
> > +
> > + smmu_pmu_reset(smmu_pmu);
> > +
> > + err = cpuhp_state_add_instance_nocalls(cpuhp_state_num,
> > + &smmu_pmu->node);
> > + if (err) {
> > + dev_err(&pdev->dev, "Error %d registering hotplug", err);
> > + return err;
> > + }
> > +
> > + err = perf_pmu_register(&smmu_pmu->pmu, smmu_pmu->pmu.name, -1);
> > + if (err) {
> > + dev_err(&pdev->dev, "Error %d registering SMMU PMU\n", err);
> > + goto out_unregister;
> > + }
> > +
> > + dev_info(&pdev->dev, "Registered SMMU PMU @ %pa using %d counters\n",
> > + &mem_resource_0->start, smmu_pmu->num_counters);
> > +
> > + return err;
> > +
> > +out_unregister:
> > + cpuhp_state_remove_instance_nocalls(cpuhp_state_num, &smmu_pmu->node);
> > + return err;
> > +}
> > +
> > +static int smmu_pmu_remove(struct platform_device *pdev)
> > +{
> > + struct smmu_pmu *smmu_pmu = platform_get_drvdata(pdev);
> > +
> > + perf_pmu_unregister(&smmu_pmu->pmu);
> > + cpuhp_state_remove_instance_nocalls(cpuhp_state_num, &smmu_pmu->node);
> > +
> > + return 0;
> > +}
> > +
> > +static void smmu_pmu_shutdown(struct platform_device *pdev)
> > +{
> > + struct smmu_pmu *smmu_pmu = platform_get_drvdata(pdev);
> > +
> > + smmu_pmu_disable(&smmu_pmu->pmu);
> > +}
> > +
> > +static struct platform_driver smmu_pmu_driver = {
> > + .driver = {
> > + .name = "arm-smmu-pmu",
>
> Nit: "arm-smmu-v3-pmu" please, for consistency with the IOMMU driver
> naming. There is a SMMUv2 PMU driver in the works, too ;)
>
> Robin.
>
> > + },
> > + .probe = smmu_pmu_probe,
> > + .remove = smmu_pmu_remove,
> > + .shutdown = smmu_pmu_shutdown,
> > +};
> > +
> > +static int __init arm_smmu_pmu_init(void)
> > +{
> > + cpuhp_state_num = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN,
> > + "perf/arm/smmupmu:online",
> > + NULL,
> > + smmu_pmu_offline_cpu);
> > + if (cpuhp_state_num < 0)
> > + return cpuhp_state_num;
> > +
> > + return platform_driver_register(&smmu_pmu_driver);
> > +}
> > +module_init(arm_smmu_pmu_init);
> > +
> > +static void __exit arm_smmu_pmu_exit(void)
> > +{
> > + platform_driver_unregister(&smmu_pmu_driver);
> > +}
> > +
> > +module_exit(arm_smmu_pmu_exit);
> > +MODULE_LICENSE("GPL v2");
> >
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
--
Linu cherian
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox