* [PATCH 2/4] ARM: dts: Add am335x-boneblack-wireless
From: Tony Lindgren @ 2017-01-03 20:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CB5C8999-6CAE-41F6-85A6-D2A856BD8011@gmail.com>
* Jason Kridner <jkridner@gmail.com> [161228 13:27]:
>
>
> > On Dec 27, 2016, at 11:58 AM, Robert Nelson <robertcnelson@gmail.com> wrote:
> >
> > BeagleBone Black Wireless is clone of the BeagleBone Black (BBB) with the Ethernet
> > replaced by a TI wl1835 wireless module.
> >
> > This board can be indentified by the BWAx value after A335BNLT (BBB) in the at24 eeprom:
> > BWAx [aa 55 33 ee 41 33 33 35 42 4e 4c 54 42 57 41 35 |.U3.A335BNLTBWA5|]
>
> I believe the correct statement is BWxx, but BWBx reserves the option to be software incompatible in some way. My preference is to have it boot anyway, but I believe that is only dependent in the bootloader.
So does this patch need updating for that?
Regards,
Tony
^ permalink raw reply
* [PATCH v3] ARM: dts: turris-omnia: add support for ethernet switch
From: Andrew Lunn @ 2017-01-03 20:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170103193501.4827-1-uwe@kleine-koenig.org>
On Tue, Jan 03, 2017 at 08:35:01PM +0100, Uwe Kleine-K?nig wrote:
> The Turris Omnia features a Marvell MV88E6176 ethernet switch. Add it to
> the dts.
>
> Signed-off-by: Uwe Kleine-K?nig <uwe@kleine-koenig.org>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Andrew
^ permalink raw reply
* [PATCH 0/6] crypto: ARM/arm64 - AES and ChaCha20 updates for v4.11
From: Ard Biesheuvel @ 2017-01-03 20:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483381268-12987-1-git-send-email-ard.biesheuvel@linaro.org>
On 2 January 2017 at 18:21, Ard Biesheuvel <ard.biesheuvel@linaro.org> wrote:
> This series adds SIMD implementations for arm64 and ARM of ChaCha20 (*),
> and a port of the ARM bit-sliced AES algorithm to arm64, and
>
> Patch #1 is a prerequisite for the AES-XTS implementation in #6, which needs
> a secondary AES transform to generate the initial tweak.
>
Herbert,
I actually have a scalar AES implementation for arm64 which I could
use instead, making this patch unnecessary.
I could respin the entire series, or you could simply disregard #1 and
#6 for now, whichever you prefer.
Thanks,
Ard.
> Patch #2 optimizes the bit-sliced AES glue code for ARM to iterate over the
> input in the most efficient manner possible.
>
> Patch #3 adds a NEON implementation of ChaCha20 for ARM.
>
> Patch #4 adds a NEON implementation of ChaCha20 for arm64.
>
> Patch #5 modifies the existing NEON and ARMv8 Crypto Extensions implementations
> of AES-CTR to be available as a synchronous skcipher as well. This is intended
> for the mac80211 code, which uses synchronous encapsulations of ctr(aes)
> [ccm, gcm] in softirq context, which supports SIMD algorithms on arm64.
>
> Patch #6 adds a port of the ARM bit-sliced AES code to arm64, in ECB, CTR
> and XTS modes.
>
> Ard Biesheuvel (6):
> crypto: generic/aes - export encrypt and decrypt entry points
> crypto: arm/aes-neonbs - process 8 blocks in parallel if we can
> crypto: arm/chacha20 - implement NEON version based on SSE3 code
> crypto: arm64/chacha20 - implement NEON version based on SSE3 code
> crypto: arm64/aes-blk - expose AES-CTR as synchronous cipher as well
> crypto: arm64/aes - reimplement bit-sliced ARM/NEON implementation for
> arm64
>
> arch/arm/crypto/Kconfig | 6 +
> arch/arm/crypto/Makefile | 2 +
> arch/arm/crypto/aesbs-glue.c | 67 +-
> arch/arm/crypto/chacha20-neon-core.S | 524 ++++++++++++
> arch/arm/crypto/chacha20-neon-glue.c | 128 +++
> arch/arm64/crypto/Kconfig | 13 +
> arch/arm64/crypto/Makefile | 6 +
> arch/arm64/crypto/aes-glue.c | 25 +-
> arch/arm64/crypto/aes-neonbs-core.S | 879 ++++++++++++++++++++
> arch/arm64/crypto/aes-neonbs-glue.c | 344 ++++++++
> arch/arm64/crypto/chacha20-neon-core.S | 450 ++++++++++
> arch/arm64/crypto/chacha20-neon-glue.c | 127 +++
> crypto/aes_generic.c | 10 +-
> include/crypto/aes.h | 3 +
> 14 files changed, 2549 insertions(+), 35 deletions(-)
> create mode 100644 arch/arm/crypto/chacha20-neon-core.S
> create mode 100644 arch/arm/crypto/chacha20-neon-glue.c
> create mode 100644 arch/arm64/crypto/aes-neonbs-core.S
> create mode 100644 arch/arm64/crypto/aes-neonbs-glue.c
> create mode 100644 arch/arm64/crypto/chacha20-neon-core.S
> create mode 100644 arch/arm64/crypto/chacha20-neon-glue.c
>
> --
> 2.7.4
>
^ permalink raw reply
* [PATCH v3] ARM: dts: turris-omnia: add support for ethernet switch
From: Uwe Kleine-König @ 2017-01-03 19:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170103152107.GA32450@lunn.ch>
The Turris Omnia features a Marvell MV88E6176 ethernet switch. Add it to
the dts.
Signed-off-by: Uwe Kleine-K?nig <uwe@kleine-koenig.org>
---
Changes since (implicit) v1:
- drop mdio bus and per port phy-handle as they match the default
setup.
Changes since v2:
- Fix switch type in comment and commit log
- drop 2nd cpu port
arch/arm/boot/dts/armada-385-turris-omnia.dts | 58 +++++++++++++++++++++++++--
1 file changed, 55 insertions(+), 3 deletions(-)
diff --git a/arch/arm/boot/dts/armada-385-turris-omnia.dts b/arch/arm/boot/dts/armada-385-turris-omnia.dts
index ab49acb2d452..28eede180e4f 100644
--- a/arch/arm/boot/dts/armada-385-turris-omnia.dts
+++ b/arch/arm/boot/dts/armada-385-turris-omnia.dts
@@ -122,7 +122,7 @@
pinctrl-names = "default";
pinctrl-0 = <&ge0_rgmii_pins>;
status = "okay";
- phy-mode = "rgmii-id";
+ phy-mode = "rgmii";
fixed-link {
speed = <1000>;
@@ -135,7 +135,7 @@
pinctrl-names = "default";
pinctrl-0 = <&ge1_rgmii_pins>;
status = "okay";
- phy-mode = "rgmii-id";
+ phy-mode = "rgmii";
fixed-link {
speed = <1000>;
@@ -273,7 +273,59 @@
/* irq is connected to &pcawan pin 7 */
};
- /* Switch MV88E7176 at address 0x10 */
+ /* Switch MV88E6176 at address 0x10 */
+ switch at 10 {
+ compatible = "marvell,mv88e6085";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ dsa,member = <0 0>;
+
+ reg = <0x10>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ports at 0 {
+ reg = <0>;
+ label = "lan0";
+ };
+
+ ports at 1 {
+ reg = <1>;
+ label = "lan1";
+ };
+
+ ports at 2 {
+ reg = <2>;
+ label = "lan2";
+ };
+
+ ports at 3 {
+ reg = <3>;
+ label = "lan3";
+ };
+
+ ports at 4 {
+ reg = <4>;
+ label = "lan4";
+ };
+
+ ports at 5 {
+ reg = <5>;
+ label = "cpu";
+ ethernet = <ð1>;
+ phy-mode = "rgmii-id";
+
+ fixed-link {
+ speed = <1000>;
+ full-duplex;
+ };
+ };
+
+ /* port 6 is connected to eth0 */
+ };
+ };
};
&pinctrl {
--
2.11.0
^ permalink raw reply related
* [PATCH] cpufreq: s3c64xx: remove incorrect __init annotation
From: Krzysztof Kozlowski @ 2017-01-03 19:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAJZ5v0jaKnn+LTTdpgxaFLNaYSuqV8d9PKphOnHrPw0Zp_Uj5w@mail.gmail.com>
On Mon, Jan 02, 2017 at 10:19:29PM +0100, Rafael J. Wysocki wrote:
> On Mon, Jan 2, 2017 at 6:36 PM, Krzysztof Kozlowski <krzk@kernel.org> wrote:
> > On Mon, Jan 02, 2017 at 12:39:03PM +0530, Viresh Kumar wrote:
> >> On 16-12-16, 10:06, Arnd Bergmann wrote:
> >> > s3c64xx_cpufreq_config_regulator is incorrectly annotated
> >> > as __init, since the caller is also not init:
> >> >
> >> > WARNING: vmlinux.o(.text+0x92fe1c): Section mismatch in reference from the function s3c64xx_cpufreq_driver_init() to the function .init.text:s3c64xx_cpufreq_config_regulator()
> >> >
> >> > With modern gcc versions, the function gets inline, so we don't
> >> > see the warning, this only happens with gcc-4.6 and older.
> >> >
> >> > Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> >> > ---
> >> > drivers/cpufreq/s3c64xx-cpufreq.c | 2 +-
> >> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >> >
> >> > diff --git a/drivers/cpufreq/s3c64xx-cpufreq.c b/drivers/cpufreq/s3c64xx-cpufreq.c
> >> > index 176e84cc3991..0cb9040eca49 100644
> >> > --- a/drivers/cpufreq/s3c64xx-cpufreq.c
> >> > +++ b/drivers/cpufreq/s3c64xx-cpufreq.c
> >> > @@ -107,7 +107,7 @@ static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy,
> >> > }
> >> >
> >> > #ifdef CONFIG_REGULATOR
> >> > -static void __init s3c64xx_cpufreq_config_regulator(void)
> >> > +static void s3c64xx_cpufreq_config_regulator(void)
> >> > {
> >> > int count, v, i, found;
> >> > struct cpufreq_frequency_table *freq;
> >>
> >> Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
> >
> > Rafael,
> > Are you going to pick it up?
>
> I thought I did, didn't I?
Right, you did. I missed that. Thanks!
Best regards,
Krzysztof
^ permalink raw reply
* [PATCH] ARM: dts: imx53-qsb: Provide the TVE DAC regulators
From: Fabio Estevam @ 2017-01-03 19:12 UTC (permalink / raw)
To: linux-arm-kernel
From: Fabio Estevam <fabio.estevam@nxp.com>
On imx53-qsb the TVE DAC regulator comes from:
- LDO7 on the board with the Dialog DA9052 PMIC
- VDAC on the board with the MC34708 PMIC
Pass them in the 'dac-supply' node.
While at it, remove the 'regulator-always-on/regulator-boot-on'
properties as the TVE driver will properly handle it.
Tested on a imx53-qsb board with a Dialog DA9052 PMIC.
Signed-off-by: Fabio Estevam <fabio.estevam@nxp.com>
---
arch/arm/boot/dts/imx53-qsb.dts | 5 ++++-
arch/arm/boot/dts/imx53-qsrb.dts | 6 ++++--
2 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/arch/arm/boot/dts/imx53-qsb.dts b/arch/arm/boot/dts/imx53-qsb.dts
index 3799396..f4c158c 100644
--- a/arch/arm/boot/dts/imx53-qsb.dts
+++ b/arch/arm/boot/dts/imx53-qsb.dts
@@ -90,7 +90,6 @@
ldo7_reg: ldo7 {
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <3600000>;
- regulator-always-on;
};
ldo8_reg: ldo8 {
@@ -113,3 +112,7 @@
};
};
};
+
+&tve {
+ dac-supply = <&ldo7_reg>;
+};
diff --git a/arch/arm/boot/dts/imx53-qsrb.dts b/arch/arm/boot/dts/imx53-qsrb.dts
index 96d7eed..479ca4c 100644
--- a/arch/arm/boot/dts/imx53-qsrb.dts
+++ b/arch/arm/boot/dts/imx53-qsrb.dts
@@ -130,8 +130,6 @@
regulator-name = "VDAC";
regulator-min-microvolt = <2500000>;
regulator-max-microvolt = <2775000>;
- regulator-boot-on;
- regulator-always-on;
};
vgen1_reg: vgen1 {
@@ -152,3 +150,7 @@
};
};
};
+
+&tve {
+ dac-supply = <&vdac_reg>;
+};
--
2.7.4
^ permalink raw reply related
* [PATCH 1/2] arm64: dma_mapping: allow PCI host driver to limit DMA mask
From: Nikita Yushchenko @ 2017-01-03 19:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170103184444.GP6986@arm.com>
>> It is possible that PCI device supports 64-bit DMA addressing, and thus
>> it's driver sets device's dma_mask to DMA_BIT_MASK(64), however PCI host
>> bridge has limitations on inbound transactions addressing. Example of
>> such setup is NVME SSD device connected to RCAR PCIe controller.
>>
>> Previously there was attempt to handle this via bus notifier: after
>> driver is attached to PCI device, bridge driver gets notifier callback,
>> and resets dma_mask from there. However, this is racy: PCI device driver
>> could already allocate buffers and/or start i/o in probe routine.
>> In NVME case, i/o is started in workqueue context, and this race gives
>> "sometimes works, sometimes not" effect.
>>
>> Proper solution should make driver's dma_set_mask() call to fail if host
>> bridge can't support mask being set.
>>
>> This patch makes __swiotlb_dma_supported() to check mask being set for
>> PCI device against dma_mask of struct device corresponding to PCI host
>> bridge (one with name "pciXXXX:YY"), if that dma_mask is set.
>>
>> This is the least destructive approach: currently dma_mask of that device
>> object is not used anyhow, thus all existing setups will work as before,
>> and modification is required only in actually affected components -
>> driver of particular PCI host bridge, and dma_map_ops of particular
>> platform.
>>
>> Signed-off-by: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
>> ---
>> arch/arm64/mm/dma-mapping.c | 11 +++++++++++
>> 1 file changed, 11 insertions(+)
>>
>> diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
>> index 290a84f..49645277 100644
>> --- a/arch/arm64/mm/dma-mapping.c
>> +++ b/arch/arm64/mm/dma-mapping.c
>> @@ -28,6 +28,7 @@
>> #include <linux/dma-contiguous.h>
>> #include <linux/vmalloc.h>
>> #include <linux/swiotlb.h>
>> +#include <linux/pci.h>
>>
>> #include <asm/cacheflush.h>
>>
>> @@ -347,6 +348,16 @@ static int __swiotlb_get_sgtable(struct device *dev, struct sg_table *sgt,
>>
>> static int __swiotlb_dma_supported(struct device *hwdev, u64 mask)
>> {
>> +#ifdef CONFIG_PCI
>> + if (dev_is_pci(hwdev)) {
>> + struct pci_dev *pdev = to_pci_dev(hwdev);
>> + struct pci_host_bridge *br = pci_find_host_bridge(pdev->bus);
>> +
>> + if (br->dev.dma_mask && (*br->dev.dma_mask) &&
>> + (mask & (*br->dev.dma_mask)) != mask)
>> + return 0;
>> + }
>> +#endif
>
> Hmm, but this makes it look like the problem is both arm64 and swiotlb
> specific, when in reality it's not. Perhaps another hack you could try
> would be to register a PCI bus notifier in the host bridge looking for
> BUS_NOTIFY_BIND_DRIVER, then you could proxy the DMA ops for each child
> device before the driver has probed, but adding a dma_set_mask callback
> to limit the mask to what you need?
This is what Renesas BSP tries to do and it does not work.
BUS_NOTIFY_BIND_DRIVER arrives after driver's probe routine exits, but
i/o can be started before that.
^ permalink raw reply
* [PATCH 1/2] arm64: dma_mapping: allow PCI host driver to limit DMA mask
From: Nikita Yushchenko @ 2017-01-03 19:00 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170103184444.GP6986@arm.com>
03.01.2017 21:44, Will Deacon ?????:
> On Thu, Dec 29, 2016 at 11:45:03PM +0300, Nikita Yushchenko wrote:
>> It is possible that PCI device supports 64-bit DMA addressing, and thus
>> it's driver sets device's dma_mask to DMA_BIT_MASK(64), however PCI host
>> bridge has limitations on inbound transactions addressing. Example of
>> such setup is NVME SSD device connected to RCAR PCIe controller.
>>
>> Previously there was attempt to handle this via bus notifier: after
>> driver is attached to PCI device, bridge driver gets notifier callback,
>> and resets dma_mask from there. However, this is racy: PCI device driver
>> could already allocate buffers and/or start i/o in probe routine.
>> In NVME case, i/o is started in workqueue context, and this race gives
>> "sometimes works, sometimes not" effect.
>>
>> Proper solution should make driver's dma_set_mask() call to fail if host
>> bridge can't support mask being set.
>>
>> This patch makes __swiotlb_dma_supported() to check mask being set for
>> PCI device against dma_mask of struct device corresponding to PCI host
>> bridge (one with name "pciXXXX:YY"), if that dma_mask is set.
>>
>> This is the least destructive approach: currently dma_mask of that device
>> object is not used anyhow, thus all existing setups will work as before,
>> and modification is required only in actually affected components -
>> driver of particular PCI host bridge, and dma_map_ops of particular
>> platform.
>>
>> Signed-off-by: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
>> ---
>> arch/arm64/mm/dma-mapping.c | 11 +++++++++++
>> 1 file changed, 11 insertions(+)
>>
>> diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
>> index 290a84f..49645277 100644
>> --- a/arch/arm64/mm/dma-mapping.c
>> +++ b/arch/arm64/mm/dma-mapping.c
>> @@ -28,6 +28,7 @@
>> #include <linux/dma-contiguous.h>
>> #include <linux/vmalloc.h>
>> #include <linux/swiotlb.h>
>> +#include <linux/pci.h>
>>
>> #include <asm/cacheflush.h>
>>
>> @@ -347,6 +348,16 @@ static int __swiotlb_get_sgtable(struct device *dev, struct sg_table *sgt,
>>
>> static int __swiotlb_dma_supported(struct device *hwdev, u64 mask)
>> {
>> +#ifdef CONFIG_PCI
>> + if (dev_is_pci(hwdev)) {
>> + struct pci_dev *pdev = to_pci_dev(hwdev);
>> + struct pci_host_bridge *br = pci_find_host_bridge(pdev->bus);
>> +
>> + if (br->dev.dma_mask && (*br->dev.dma_mask) &&
>> + (mask & (*br->dev.dma_mask)) != mask)
>> + return 0;
>> + }
>> +#endif
>
> Hmm, but this makes it look like the problem is both arm64 and swiotlb
> specific, when in reality it's not. Perhaps another hack you could try
> would be to register a PCI bus notifier in the host bridge looking for
> BUS_NOTIFY_BIND_DRIVER, then you could proxy the DMA ops for each child
> device before the driver has probed, but adding a dma_set_mask callback
> to limit the mask to what you need?
This is what Renesas BSP tries to do and it does not work.
BUS_NOTIFY_BIND_DRIVER arrives after driver's probe routine exits, but
i/o can be started before that.
^ permalink raw reply
* [PATCH 2/4] input: tm2-touchkey: Add touchkey driver support for TM2
From: Dmitry Torokhov @ 2017-01-03 18:56 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <7794c9b2-48e6-1451-9a3e-fe24410a145d@osg.samsung.com>
On Tue, Jan 03, 2017 at 10:11:39AM -0300, Javier Martinez Canillas wrote:
> Hello Jaechul,
>
> On 01/03/2017 04:57 AM, Jaechul Lee wrote:
> > This patch adds support for the TM2 touch key and led
> > functionlity.
> >
>
> s/functionlity/functionality
>
> > The driver interfaces with userspace through an input device and
> > reports KEY_PHONE and KEY_BACK event types. LED brightness can be
> > controlled by "/sys/class/leds/tm2-touchkey/brightness".
> >
> > Signed-off-by: Jaechul Lee <jcsing.lee@samsung.com>
> > Signed-off-by: Beomho Seo <beomho.seo@samsung.com>
> > ---
> > drivers/input/keyboard/Kconfig | 11 ++
> > drivers/input/keyboard/Makefile | 1 +
> > drivers/input/keyboard/tm2-touchkey.c | 326 ++++++++++++++++++++++++++++++++++
> > 3 files changed, 338 insertions(+)
> > create mode 100644 drivers/input/keyboard/tm2-touchkey.c
> >
> > diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
> > index cbd75cf..72c0ba1 100644
> > --- a/drivers/input/keyboard/Kconfig
> > +++ b/drivers/input/keyboard/Kconfig
> > @@ -666,6 +666,17 @@ config KEYBOARD_TC3589X
> > To compile this driver as a module, choose M here: the
> > module will be called tc3589x-keypad.
> >
> > +config KEYBOARD_TM2_TOUCHKEY
> > + tristate "tm2-touchkey support"
> > + depends on I2C
There should be LED dependency as well.
> > + help
> > + Say Y here to enable the tm2-touchkey.
> > + touchkey driver for tm2. This driver can enable
> > + the interrupt and make input events and control led brightness.
> > +
> > + To compile this driver as a module, choose M here.
> > + module will be called tm2-touchkey
> > +
> > config KEYBOARD_TWL4030
> > tristate "TI TWL4030/TWL5030/TPS659x0 keypad support"
> > depends on TWL4030_CORE
> > diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
> > index d9f4cfc..7d9acff 100644
> > --- a/drivers/input/keyboard/Makefile
> > +++ b/drivers/input/keyboard/Makefile
> > @@ -61,6 +61,7 @@ obj-$(CONFIG_KEYBOARD_SUN4I_LRADC) += sun4i-lradc-keys.o
> > obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
> > obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o
> > obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o
> > +obj-$(CONFIG_KEYBOARD_TM2_TOUCHKEY) += tm2-touchkey.o
> > obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o
> > obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
> > obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o
> > diff --git a/drivers/input/keyboard/tm2-touchkey.c b/drivers/input/keyboard/tm2-touchkey.c
> > new file mode 100644
> > index 0000000..d9575d8
> > --- /dev/null
> > +++ b/drivers/input/keyboard/tm2-touchkey.c
> > @@ -0,0 +1,326 @@
> > +/*
> > + * Driver for keys on GPIO lines capable of generating interrupts.
This does not match what the driver does.
> > + *
> > + * Copyright 2005 Phil Blundell
> > + * Copyright 2016 Samsung Electronics Co., Ltd.
> > + *
> > + * Author: Beomho Seo <beomho.seo@samsung.com>
> > + * Author: Jaechul Lee <jcsing.lee@samsung.com>
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + */
> > +
> > +#include <linux/bitops.h>
> > +#include <linux/delay.h>
> > +#include <linux/device.h>
> > +#include <linux/i2c.h>
> > +#include <linux/input.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/irq.h>
> > +#include <linux/leds.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/pm.h>
> > +#include <linux/regulator/consumer.h>
> > +#include <linux/workqueue.h>
> > +
> > +#define TM2_TOUCHKEY_DEV_NAME "tm2-touchkey"
> > +#define TM2_TOUCHKEY_KEYCODE_REG 0x03
> > +#define TM2_TOUCHKEY_BASE_REG 0x00
> > +#define TM2_TOUCHKEY_CMD_LED_ON 0x10
> > +#define TM2_TOUCHKEY_CMD_LED_OFF 0x20
> > +#define TM2_TOUCHKEY_BIT_PRESS_EV BIT(3)
> > +#define TM2_TOUCHKEY_BIT_KEYCODE GENMASK(2, 0)
> > +#define TM2_TOUCHKEY_LED_VOLTAGE_MIN 2500000
> > +#define TM2_TOUCHKEY_LED_VOLTAGE_MAX 3300000
> > +
> > +enum {
> > + TM2_TOUCHKEY_KEY_MENU = 0x1,
> > + TM2_TOUCHKEY_KEY_BACK,
> > +};
> > +
> > +#define tm2_touchkey_power_enable(x) __tm2_touchkey_power_onoff(x, 1)
> > +#define tm2_touchkey_power_disable(x) __tm2_touchkey_power_onoff(x, 0)
> > +
> > +struct tm2_touchkey_data {
> > + struct i2c_client *client;
> > + struct input_dev *input_dev;
> > + struct led_classdev led_dev;
> > +
> > + u8 keycode_type;
> > + u8 pressed;
> > + struct work_struct irq_work;
> > +
> > + bool power_onoff;
> > + struct regulator *regulator_vcc; /* 1.8V */
> > + struct regulator *regulator_vdd; /* 3.3V */
> > +};
> > +
> > +static void tm2_touchkey_led_brightness_set(struct led_classdev *led_dev,
> > + enum led_brightness brightness)
> > +{
> > + struct tm2_touchkey_data *samsung_touchkey =
Just call it "touchkey", "samsung_touchkey" is too long for a local
variable.
> > + container_of(led_dev, struct tm2_touchkey_data, led_dev);
> > + u32 volt;
> > + u8 data;
> > +
> > + if (brightness == LED_OFF) {
> > + volt = TM2_TOUCHKEY_LED_VOLTAGE_MIN;
> > + data = TM2_TOUCHKEY_CMD_LED_OFF;
> > + } else {
> > + volt = TM2_TOUCHKEY_LED_VOLTAGE_MAX;
> > + data = TM2_TOUCHKEY_CMD_LED_ON;
> > + }
> > +
> > + regulator_set_voltage(samsung_touchkey->regulator_vdd, volt, volt);
> > + i2c_smbus_write_byte_data(samsung_touchkey->client,
> > + TM2_TOUCHKEY_BASE_REG, data);
> > +}
> > +
> > +static int __tm2_touchkey_power_onoff(struct tm2_touchkey_data
> > + *samsung_touchkey, bool onoff)
> > +{
> > + int ret = 0;
> > +
> > + if (samsung_touchkey->power_onoff == onoff)
> > + return ret;
> > +
> > + if (onoff) {
> > + ret = regulator_enable(samsung_touchkey->regulator_vcc);
> > + if (ret)
> > + return ret;
> > +
> > + ret = regulator_enable(samsung_touchkey->regulator_vdd);
> > + if (ret) {
> > + regulator_disable(samsung_touchkey->regulator_vcc);
> > + return ret;
> > + }
>
> I would add a comment about the sleep here.
Also "bulk" regulator API can be useful here.
>
> > + msleep(150);
> > + } else {
> > + int err;
> > +
> > + err = regulator_disable(samsung_touchkey->regulator_vcc);
> > + if (err)
> > + ret = err;
> > +
> > + err = regulator_disable(samsung_touchkey->regulator_vdd);
> > + if (err && !ret)
> > + ret = err;
> > + }
> > + samsung_touchkey->power_onoff = onoff;
> > +
> > + return ret;
> > +}
> > +
> > +static void tm2_touchkey_irq_work(struct work_struct *irq_work)
> > +{
> > + struct tm2_touchkey_data *samsung_touchkey =
> > + container_of(irq_work, struct tm2_touchkey_data, irq_work);
> > +
> > + if (!samsung_touchkey->pressed) {
> > + input_report_key(samsung_touchkey->input_dev, KEY_PHONE, 0);
> > + input_report_key(samsung_touchkey->input_dev, KEY_BACK, 0);
> > + } else {
> > + if (samsung_touchkey->keycode_type == TM2_TOUCHKEY_KEY_MENU)
> > + input_report_key(samsung_touchkey->input_dev,
> > + KEY_PHONE, 1);
> > + else
> > + input_report_key(samsung_touchkey->input_dev,
> > + KEY_BACK, 1);
> > + }
> > + input_sync(samsung_touchkey->input_dev);
> > +}
There is absolutely no reason for doing this in a work. Just call
input_report_key/input_sync from your threaded IRQ routine.
> > +
> > +static irqreturn_t tm2_touchkey_irq_handler(int irq, void *devid)
> > +{
> > + struct tm2_touchkey_data *samsung_touchkey = devid;
> > + u32 data;
> > +
> > + data = i2c_smbus_read_byte_data(samsung_touchkey->client,
> > + TM2_TOUCHKEY_KEYCODE_REG);
> > +
> > + if (data < 0) {
> > + dev_err(&samsung_touchkey->client->dev, "Failed to read i2c data\n");
> > + return IRQ_HANDLED;
> > + }
> > +
> > + samsung_touchkey->keycode_type = data & TM2_TOUCHKEY_BIT_KEYCODE;
> > + samsung_touchkey->pressed = !(data & TM2_TOUCHKEY_BIT_PRESS_EV);
> > +
> > + if (samsung_touchkey->keycode_type != TM2_TOUCHKEY_KEY_MENU &&
> > + samsung_touchkey->keycode_type != TM2_TOUCHKEY_KEY_BACK)
>
> Shouldn't at least a debug message be printed here so the user can
> know that an error occurred and a correct keycode was not received?
>
> > + return IRQ_HANDLED;
> > +
> > + schedule_work(&samsung_touchkey->irq_work);
> > +
> > + return IRQ_HANDLED;
> > +}
> > +
> > +static int tm2_touchkey_probe(struct i2c_client *client,
> > + const struct i2c_device_id *id)
> > +{
> > + struct tm2_touchkey_data *samsung_touchkey;
> > + int ret;
> > +
> > + ret = i2c_check_functionality(client->adapter,
> > + I2C_FUNC_SMBUS_BYTE |
> > + I2C_FUNC_SMBUS_BYTE_DATA);
> > + if (!ret) {
> > + dev_err(&client->dev, "No I2C functionality found\n");
> > + return -ENODEV;
> > + }
> > +
> > + samsung_touchkey = devm_kzalloc(&client->dev,
> > + sizeof(struct tm2_touchkey_data), GFP_KERNEL);
sizeof(*touchkey)
> > +
> > + if (!samsung_touchkey) {
> > + dev_err(&client->dev, "Failed to allocate memory.\n");
> > + return -ENOMEM;
> > + }
> > +
> > + samsung_touchkey->client = client;
> > + i2c_set_clientdata(client, samsung_touchkey);
> > + INIT_WORK(&samsung_touchkey->irq_work, tm2_touchkey_irq_work);
Work is not needed.
> > +
> > + /* regulator */
> > + samsung_touchkey->regulator_vcc =
> > + devm_regulator_get(&client->dev, "vcc");
> > + if (IS_ERR(samsung_touchkey->regulator_vcc)) {
> > + dev_err(&client->dev, "Failed to get vcc regulator\n");
> > + return PTR_ERR(samsung_touchkey->regulator_vcc);
> > + }
> > +
> > + samsung_touchkey->regulator_vdd =
> > + devm_regulator_get(&client->dev, "vdd");
> > + if (IS_ERR(samsung_touchkey->regulator_vdd)) {
> > + dev_err(&client->dev, "Failed to get vdd regulator\n");
> > + return PTR_ERR(samsung_touchkey->regulator_vcc);
> > + }
devm_regulator_bulk_get
> > +
> > + /* power */
> > + ret = tm2_touchkey_power_enable(samsung_touchkey);
> > + if (ret) {
> > + dev_err(&client->dev, "Failed to enable power\n");
> > + return ret;
> > + }
You need to install devm action (devm_add_action_or_reset) to disable
power when unloading driver (or if initialization fails).
> > +
> > + /* irq */
> > + ret = devm_request_threaded_irq(&client->dev,
> > + client->irq, NULL,
> > + tm2_touchkey_irq_handler,
> > + IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
I'd rather we rely on IRQ trigger from DT, so just use IRQF_PNESHOT
here.
I'd rather you allocated input device before requesting IRQ. The
registering input device is fine to do after getting IRQ.
> > + TM2_TOUCHKEY_DEV_NAME,
> > + samsung_touchkey);
> > + if (ret) {
> > + dev_err(&client->dev, "Failed to request threaded irq\n");
> > + return ret;
> > + }
> > +
> > + /* input device */
> > + samsung_touchkey->input_dev = devm_input_allocate_device(&client->dev);
> > + if (!samsung_touchkey->input_dev) {
> > + dev_err(&client->dev, "Failed to alloc input device.\n");
> > + return -ENOMEM;
> > + }
> > + samsung_touchkey->input_dev->name = TM2_TOUCHKEY_DEV_NAME;
> > + samsung_touchkey->input_dev->id.bustype = BUS_I2C;
> > + samsung_touchkey->input_dev->dev.parent = &client->dev;
No need to set parent when using devm_input_allocate_device(), it is
done automatically.
> > +
> > + set_bit(EV_KEY, samsung_touchkey->input_dev->evbit);
> > + set_bit(KEY_PHONE, samsung_touchkey->input_dev->keybit);
> > + set_bit(KEY_BACK, samsung_touchkey->input_dev->keybit);
Just do
input_set_capability(touchkey->input_dev, EV_KEY, KEY_PHONE);
input_set_capability(touchkey->input_dev, EV_KEY, KEY_BACK);
> > + input_set_drvdata(samsung_touchkey->input_dev, samsung_touchkey);
> > +
> > + ret = input_register_device(samsung_touchkey->input_dev);
> > + if (ret) {
> > + dev_err(&client->dev, "Failed to register input device.\n");
> > + return ret;
> > + }
> > +
> > + /* led device */
> > + samsung_touchkey->led_dev.name = TM2_TOUCHKEY_DEV_NAME;
> > + samsung_touchkey->led_dev.brightness = LED_FULL;
> > + samsung_touchkey->led_dev.max_brightness = LED_FULL;
> > + samsung_touchkey->led_dev.brightness_set =
> > + tm2_touchkey_led_brightness_set;
> > +
> > + ret = devm_led_classdev_register(&client->dev,
> > + &samsung_touchkey->led_dev);
> > + if (ret < 0) {
> > + dev_err(&client->dev, "Failed to register touchkey led\n");
> > + return ret;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static void tm2_touchkey_shutdown(struct i2c_client *client)
> > +{
This is something not normally done. Does your platform really keep
rails on when AP is off?
> > + struct tm2_touchkey_data *samsung_touchkey =
> > + i2c_get_clientdata(client);
> > + int ret;
> > +
> > + disable_irq(client->irq);
> > + ret = tm2_touchkey_power_disable(samsung_touchkey);
> > + if (ret)
> > + dev_err(&client->dev, "Failed to disable power\n");
> > +}
> > +
> > +static int tm2_touchkey_suspend(struct device *dev)
__maybe_unused
> > +{
> > + struct tm2_touchkey_data *samsung_touchkey = dev_get_drvdata(dev);
> > + int ret;
> > +
> > + disable_irq(samsung_touchkey->client->irq);
> > + ret = tm2_touchkey_power_disable(samsung_touchkey);
> > + if (ret)
> > + dev_err(dev, "Failed to disable power\n");
> > +
> > + return ret;
> > +}
>
> These two functions are basically the same, can you factor it out?
>
> > +
> > +static int tm2_touchkey_resume(struct device *dev)
__maybe_unused
> > +{
> > + struct tm2_touchkey_data *samsung_touchkey = dev_get_drvdata(dev);
> > + int ret;
> > +
> > + enable_irq(samsung_touchkey->client->irq);
> > + ret = tm2_touchkey_power_enable(samsung_touchkey);
> > + if (ret)
> > + dev_err(dev, "Failed to enable power\n");
> > +
> > + return ret;
> > +}
> > +
> > +static SIMPLE_DEV_PM_OPS(tm2_touchkey_pm_ops, tm2_touchkey_suspend,
> > + tm2_touchkey_resume);
> > +
> > +static const struct i2c_device_id tm2_touchkey_id_table[] = {
> > + {TM2_TOUCHKEY_DEV_NAME, 0},
> > + {},
> > +};
> > +
>
> You need a MODULE_DEVICE_TABLE(i2c, tm2_touchkey_id_table) here so the
> module can be autoloaded when the device is registered.
>
> > +static const struct of_device_id tm2_touchkey_of_match[] = {
> > + {.compatible = "samsung,tm2-touchkey",},
> > + {},
> > +};
> > +
>
> Here a MODULE_DEVICE_TABLE(of, tm2_touchkey_of_match) is not strictly
> needed since the I2C core always reports MODALIAS of the form i2c:<dev>
> but still is good to have so the I2C core can be fixed at some point.
Yes, please add MODULE_DEVICE_TABLE(of, ...).
>
> > +static struct i2c_driver tm2_touchkey_driver = {
> > + .driver = {
> > + .name = TM2_TOUCHKEY_DEV_NAME,
> > + .pm = &tm2_touchkey_pm_ops,
> > + .of_match_table = of_match_ptr(tm2_touchkey_of_match),
> > + },
> > + .probe = tm2_touchkey_probe,
> > + .shutdown = tm2_touchkey_shutdown,
> > + .id_table = tm2_touchkey_id_table,
> > +};
> > +
> > +module_i2c_driver(tm2_touchkey_driver);
> > +
> > +MODULE_AUTHOR("Beomho Seo <beomho.seo@samsung.com>");
> > +MODULE_AUTHOR("Jaechul Lee <jcsing.lee@samsung.com>");
> > +MODULE_DESCRIPTION("Samsung touchkey driver");
> > +MODULE_LICENSE("GPL v2");
> >
Thanks.
--
Dmitry
^ permalink raw reply
* [PATCH] ARM: multi_v7_defconfig: enable Qualcomm RPMCC
From: Bjorn Andersson @ 2017-01-03 18:52 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483389305-8283-1-git-send-email-andy.gross@linaro.org>
On Mon 02 Jan 12:35 PST 2017, Andy Gross wrote:
> This patch enables the Qualcomm RPM based Clock Controller present on
> A-family boards.
>
> Signed-off-by: Andy Gross <andy.gross@linaro.org>
Acked-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Regards,
Bjorn
> ---
> arch/arm/configs/multi_v7_defconfig | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
> index b01a438..4ff6779 100644
> --- a/arch/arm/configs/multi_v7_defconfig
> +++ b/arch/arm/configs/multi_v7_defconfig
> @@ -824,6 +824,7 @@ CONFIG_QCOM_SMSM=y
> CONFIG_QCOM_WCNSS_CTRL=m
> CONFIG_ROCKCHIP_PM_DOMAINS=y
> CONFIG_COMMON_CLK_QCOM=y
> +CONFIG_QCOM_CLK_RPM=y
> CONFIG_CHROME_PLATFORMS=y
> CONFIG_STAGING_BOARD=y
> CONFIG_CROS_EC_CHARDEV=m
> --
> 1.9.1
>
^ permalink raw reply
* [GIT PULL] drivers: firmware: PSCI fixes for v4.10
From: Lorenzo Pieralisi @ 2017-01-03 18:50 UTC (permalink / raw)
To: linux-arm-kernel
Hi ARM SoC,
please consider pulling these minor PSCI fixes for v4.10.
Thank you very much.
Regards,
Lorenzo
The following changes since commit 0c744ea4f77d72b3dcebb7a8f2684633ec79be88:
Linux 4.10-rc2 (2017-01-01 14:31:53 -0800)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/lpieralisi/linux.git tags/psci-fixes-4.10
for you to fetch changes up to 32d53d1baf874caabe66ba565699ed5853fa2b6f:
MAINTAINERS: extend PSCI entry to cover the newly add PSCI checker code (2017-01-03 17:53:00 +0000)
----------------------------------------------------------------
PSCI fixes for v4.10
Two minor fixes following the merge of the PSCI checker:
- Annotate the PSCI checker timer on the stack used to wake-up from
suspend to prevent warnings when the DEBUG_OBJECTS config option
is enabled
- Extend the PSCI entry in the maintainers list to also include the
PSCI checker code
----------------------------------------------------------------
Sudeep Holla (2):
drivers: psci: annotate timer on stack to silence odebug messages
MAINTAINERS: extend PSCI entry to cover the newly add PSCI checker code
MAINTAINERS | 2 +-
drivers/firmware/psci_checker.c | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
^ permalink raw reply
* [PATCH 1/2] arm64: dma_mapping: allow PCI host driver to limit DMA mask
From: Will Deacon @ 2017-01-03 18:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483044304-2085-1-git-send-email-nikita.yoush@cogentembedded.com>
On Thu, Dec 29, 2016 at 11:45:03PM +0300, Nikita Yushchenko wrote:
> It is possible that PCI device supports 64-bit DMA addressing, and thus
> it's driver sets device's dma_mask to DMA_BIT_MASK(64), however PCI host
> bridge has limitations on inbound transactions addressing. Example of
> such setup is NVME SSD device connected to RCAR PCIe controller.
>
> Previously there was attempt to handle this via bus notifier: after
> driver is attached to PCI device, bridge driver gets notifier callback,
> and resets dma_mask from there. However, this is racy: PCI device driver
> could already allocate buffers and/or start i/o in probe routine.
> In NVME case, i/o is started in workqueue context, and this race gives
> "sometimes works, sometimes not" effect.
>
> Proper solution should make driver's dma_set_mask() call to fail if host
> bridge can't support mask being set.
>
> This patch makes __swiotlb_dma_supported() to check mask being set for
> PCI device against dma_mask of struct device corresponding to PCI host
> bridge (one with name "pciXXXX:YY"), if that dma_mask is set.
>
> This is the least destructive approach: currently dma_mask of that device
> object is not used anyhow, thus all existing setups will work as before,
> and modification is required only in actually affected components -
> driver of particular PCI host bridge, and dma_map_ops of particular
> platform.
>
> Signed-off-by: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
> ---
> arch/arm64/mm/dma-mapping.c | 11 +++++++++++
> 1 file changed, 11 insertions(+)
>
> diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
> index 290a84f..49645277 100644
> --- a/arch/arm64/mm/dma-mapping.c
> +++ b/arch/arm64/mm/dma-mapping.c
> @@ -28,6 +28,7 @@
> #include <linux/dma-contiguous.h>
> #include <linux/vmalloc.h>
> #include <linux/swiotlb.h>
> +#include <linux/pci.h>
>
> #include <asm/cacheflush.h>
>
> @@ -347,6 +348,16 @@ static int __swiotlb_get_sgtable(struct device *dev, struct sg_table *sgt,
>
> static int __swiotlb_dma_supported(struct device *hwdev, u64 mask)
> {
> +#ifdef CONFIG_PCI
> + if (dev_is_pci(hwdev)) {
> + struct pci_dev *pdev = to_pci_dev(hwdev);
> + struct pci_host_bridge *br = pci_find_host_bridge(pdev->bus);
> +
> + if (br->dev.dma_mask && (*br->dev.dma_mask) &&
> + (mask & (*br->dev.dma_mask)) != mask)
> + return 0;
> + }
> +#endif
Hmm, but this makes it look like the problem is both arm64 and swiotlb
specific, when in reality it's not. Perhaps another hack you could try
would be to register a PCI bus notifier in the host bridge looking for
BUS_NOTIFY_BIND_DRIVER, then you could proxy the DMA ops for each child
device before the driver has probed, but adding a dma_set_mask callback
to limit the mask to what you need?
I agree that it would be better if dma_set_mask handled all of this
transparently, but it's all based on the underlying ops rather than the
bus type.
Will
^ permalink raw reply
* [PATCH v3 1/3] power: reset: add linkstation-reset driver
From: Florian Fainelli @ 2017-01-03 18:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAEQ9gE=MoQcr3eX0DAxZtvx0FW9pzgkUGjdxKHcsKwH7_+UsUw@mail.gmail.com>
On 01/03/2017 06:08 AM, Roger Shimizu wrote:
> Dear Florian, Andrew,
>
> Thanks for your email!
>
> On Tue, Jan 3, 2017 at 2:19 PM, Florian Fainelli <f.fainelli@gmail.com> wrote:
>>
>> Interestingly, I submitted a patch doing nearly the same thing recently
>> after hacking on a Buffalo Terastation Pro II two days after yours
>> without seeing yours:
>>
>> https://lkml.org/lkml/2016/12/28/273
>
> Glad to know there's other developer working on linkstation/kurobox platform!
>
>> Some comments below.
>>
>>> +
>>> +static void __iomem *base;
>>> +static unsigned long tclk;
>>> +static const struct reset_cfg *cfg;
>>
>> How about avoiding the singletons here and pass this down from the
>> platform driver's private data after we (see below) also make use of a
>> reboot notifier?
>
> I see your patches. Indeed, it's a good idea to avoid the singletons
> and use private data instead.
>
>>> +static int linkstation_reset_probe(struct platform_device *pdev)
>>> +{
>>> + struct device_node *np = pdev->dev.of_node;
>>> + struct resource *res;
>>> + struct clk *clk;
>>> + char symname[KSYM_NAME_LEN];
>>> +
>>> + const struct of_device_id *match =
>>> + of_match_node(linkstation_reset_of_match_table, np);
>>> + cfg = match->data;
>>> +
>>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> + if (!res) {
>>> + dev_err(&pdev->dev, "Missing resource");
>>> + return -EINVAL;
>>> + }
>>> +
>>> + base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
>>> + if (!base) {
>>> + dev_err(&pdev->dev, "Unable to map resource");
>>> + return -EINVAL;
>>> + }
>>> +
>>> + /* We need to know tclk in order to calculate the UART divisor */
>>> + clk = devm_clk_get(&pdev->dev, NULL);
>>> + if (IS_ERR(clk)) {
>>> + dev_err(&pdev->dev, "Clk missing");
>>> + return PTR_ERR(clk);
>>> + }
>>> +
>>> + tclk = clk_get_rate(clk);
>>
>> Does this work with the Terastation II which has not been converted to
>> DT yet? Is tclk available through the CLK API there?
>
> I have no idea whether device-based legacy code and make use of power
> reset driver.
> Maybe Sebastian Reichel can offer a comment?
>
> However, I think Terastation II should convert to DT first.
> If you're willing to test, I can help to provide a dts/dtb.
> (If you use Debian, I can even provide DEB of kernel image and
> flash-kernel patch, which is easy for you to test)
>
> On Tue, Jan 3, 2017 at 10:09 PM, Andrew Lunn <andrew@lunn.ch> wrote:
>>>> +
>>>> + /* Check that nothing else has already setup a handler */
>>>> + if (pm_power_off) {
>>>> + lookup_symbol_name((ulong)pm_power_off, symname);
>>>> + dev_err(&pdev->dev,
>>>> + "pm_power_off already claimed %p %s",
>>>> + pm_power_off, symname);
>>>> + return -EBUSY;
>>>> + }
>>>> + pm_power_off = linkstation_reset;
>>>
>>> That seems a bit complicated, why not just assume that there will be
>>> either this driver used, or not at all?
>>
>> That is probably my fault. This is a copy from code i wrote many years
>> ago for the QNAP. I guess at the time i was battling with two
>> different pm_power_off handlers, so put in this code.
>>
>>> Also, you are supposed to register a reboot notifier to which you can
>>> pass private context:
>>
>> At the time i wrote the QNAP code, this did not exist. So maybe my
>> code is no longer a good example to copy.
>
> Really appreciated, Andrew!
> I'll modify this part in next series.
>
> BTW. the private data passing to reboot notifier can be shared to
> power-off function as well?
> Do you have example?
>
>> https://lkml.org/lkml/2016/12/28/275
>>
>> As indicated in my patch series, the UART1-attached micro controller
>> does a lot more things that just providing reboot, which is why I chose
>> to move this code to a MFD driver, as the starting point before adding
>> support for LEDs, FAN, PWM, beeper which would be other types of devices.
>>
>> Is adding support for other peripherals on your TODO as well?
>
> Except reset feature (power-off and reboot), other feature, such as
> LEDs / FAN speed / buttons, is managed by user-land program micro-evtd
> [0][1].
> Since the upstream [1] is not active anymore, Ryan Tandy (in CC) and I
> are maintaining it in Debian [0].
>
> [0] https://tracker.debian.org/pkg/micro-evtd
> [1] https://sourceforge.net/projects/ppc-evtd
>
> micro-evtd worked well for device-based legacy code, but after DT
> conversion, Ryan found the device cannot shutdown properly (reboot is
> OK).
> That why I created this patch series.
> I think for such old hardware and mature user-land program, it doesn't
> deserve the effort to implement those again in kernel side.
> What do you think?
An argument could be made that these are hardware peripherals, and so
the kernel is the best place to abstract support for that, and present
you with the standard device drive model for such kind of peripherals.
We don't have to do everything at the same time, so first things first,
get the reboot working, have me convert the Terastation over to Device
Tree and then we can decide what to do with these additional peripherals.
Thanks!
--
Florian
^ permalink raw reply
* coresight: Problem caused by resetting enable_sink
From: Mathieu Poirier @ 2017-01-03 18:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <6598d4c2-bbfb-1792-d216-a15ab0e841e0@huawei.com>
On Mon, Dec 26, 2016 at 05:17:08PM +0800, Wangnan (F) wrote:
> Hi Mathieu,
Hello Wang,
>
> I meet problems caused by your commit d52c9750f150 ('coresight:
> reset "enable_sink" flag when need be'). Not only the one I
> posted in the previous patch.
>
> My use case is a simple 'perf record -e cs_etm// ls'. It works
> properly before this commit, and failed when allocating aux buffer
> after your commit. I can't fully understand your code, but the
> problem I meet seems caused by inappropriately reseting sink.
>
> My device is connected like this (use two etfs):
>
> Core0--+
> Core1--+-- funnel0 --> etf0
> Core2--|
> Core3--+
>
> Core0--+
> Core1--+-- funnel1 --> etf1
> Core2--|
> Core3--+
Yes, supporting this topology is a problem I long predicted.
>
> Before running perf, two etfs are activated using sysfs
> enable_sink interface.
>
> During etm_setup_aux, coresight_get_enabled_sink() finds
> etf0 for core0, and automatically deactivates it.
>
> For core1, coresight_get_enabled_sink() returns etf1.
> However, etf1 is not on the link of core1, so following
> coresight_build_path() fails.
>
> I guess your commit is based on the assumption that all
> sinks are in the patch for each cores. Like this:
>
>
> Core0--+
> Core1--+-- funnel0 --> etf0 ++
> Core2--| | +--- etr
> Core3--+ | |
> + replicator +
> Core0--+ | |
> Core1--+-- funnel1 --> etf1 ++ +--- etb
> Core2--|
> Core3--+
>
Correct - the entire solution is based on the assumption that all cores use the
same sink. When I wrote the driver not a single system enacted a topology where
cores wouldn't use the same sink for a trace session.
> But it is not true, at least for some hisilicon board.
>
> I have to revert your patch to make CoreSight on my board
> work. Please reconsider this patch, or please give some
> suggestion if you think I misunderstood your patch.
You understood the patch corretly but simply reverting it isn't the solution.
Supporting a system where different sinks are used won't be easy - a lot of
things need to be adjusted. The first and critical part that comes to mind is
the mechanic that fetches the sink definition from the cmd line when using the
perf interface (if I remember correctly the bison/flex parser can handle
multiple sink definition). From there everything needs to be tailored to handle
more than one sink.
You will likely have more questions... Get back to me and I'll be happy to help.
Regards,
Mathieu
>
> Thank you.
>
>
^ permalink raw reply
* [PATCH 0/6] staging: vchiq_arm: Fine-tuning for some function implementations
From: Greg Kroah-Hartman @ 2017-01-03 18:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <87r34kniic.fsf@eliezer.anholt.net>
On Tue, Jan 03, 2017 at 09:46:51AM -0800, Eric Anholt wrote:
> SF Markus Elfring <elfring@users.sourceforge.net> writes:
>
> > From: Markus Elfring <elfring@users.sourceforge.net>
> > Date: Sat, 31 Dec 2016 22:42:34 +0100
> >
> > Some update suggestions were taken into account
> > from static source code analysis.
>
> This series is:
>
> Reviewed-by: Eric Anholt <eric@anholt.net>
Nice, but I have a blacklist from this patch author, and have told them
numerous times that I no longer accept patches from them. If you think
they are worthy, please resend them with your reviewed-by on them, but I
will warn you, I've blacklisted them for a reason...
good luck!
greg k-h
^ permalink raw reply
* [RFC, PATCHv2 29/29] mm, x86: introduce RLIMIT_VADDR
From: Andy Lutomirski @ 2017-01-03 18:29 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <3492795.xaneWtGxgW@wuerfel>
On Tue, Jan 3, 2017 at 5:18 AM, Arnd Bergmann <arnd@arndb.de> wrote:
> On Monday, January 2, 2017 10:08:28 PM CET Andy Lutomirski wrote:
>>
>> > This seems to nicely address the same problem on arm64, which has
>> > run into the same issue due to the various page table formats
>> > that can currently be chosen at compile time.
>>
>> On further reflection, I think this has very little to do with paging
>> formats except insofar as paging formats make us notice the problem.
>> The issue is that user code wants to be able to assume an upper limit
>> on an address, and it gets an upper limit right now that depends on
>> architecture due to paging formats. But someone really might want to
>> write a *portable* 64-bit program that allocates memory with the high
>> 16 bits clear. So let's add such a mechanism directly.
>>
>> As a thought experiment, what if x86_64 simply never allocated "high"
>> (above 2^47-1) addresses unless a new mmap-with-explicit-limit syscall
>> were used? Old glibc would continue working. Old VMs would work.
>> New programs that want to use ginormous mappings would have to use the
>> new syscall. This would be totally stateless and would have no issues
>> with CRIU.
>
> I can see this working well for the 47-bit addressing default, but
> what about applications that actually rely on 39-bit addressing
> (I'd have to double-check, but I think this was the limit that
> people were most interested in for arm64)?
>
> 39 bits seems a little small to make that the default for everyone
> who doesn't pass the extra flag. Having to pass another flag to
> limit the addresses introduces other problems (e.g. mmap from
> library call that doesn't pass that flag).
That's a fair point. Maybe my straw man isn't so good.
>
>> If necessary, we could also have a prctl that changes a
>> "personality-like" limit that is in effect when the old mmap was used.
>> I say "personality-like" because it would reset under exactly the same
>> conditions that personality resets itself.
>
> For "personality-like", it would still have to interact
> with the existing PER_LINUX32 and PER_LINUX32_3GB flags that
> do the exact same thing, so actually using personality might
> be better.
>
> We still have a few bits in the personality arguments, and
> we could combine them with the existing ADDR_LIMIT_3GB
> and ADDR_LIMIT_32BIT flags that are mutually exclusive by
> definition, such as
>
> ADDR_LIMIT_32BIT = 0x0800000, /* existing */
> ADDR_LIMIT_3GB = 0x8000000, /* existing */
> ADDR_LIMIT_39BIT = 0x0010000, /* next free bit */
> ADDR_LIMIT_42BIT = 0x8010000,
> ADDR_LIMIT_47BIT = 0x0810000,
> ADDR_LIMIT_48BIT = 0x8810000,
>
> This would probably take only one or two personality bits for the
> limits that are interesting in practice.
Hmm. What if we approached this a bit differently? We could add a
single new personality bit ADDR_LIMIT_EXPLICIT. Setting this bit
cause PER_LINUX32_3GB etc to be automatically cleared. When
ADDR_LIMIT_EXPLICIT is in effect, prctl can set a 64-bit numeric
limit. If ADDR_LIMIT_EXPLICIT is cleared, the prctl value stops being
settable and reading it via prctl returns whatever is implied by the
other personality bits.
--Andy
^ permalink raw reply
* [RFC, PATCHv2 29/29] mm, x86: introduce RLIMIT_VADDR
From: Andy Lutomirski @ 2017-01-03 18:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170103160457.GB17319@node.shutemov.name>
On Tue, Jan 3, 2017 at 8:04 AM, Kirill A. Shutemov <kirill@shutemov.name> wrote:
> On Mon, Jan 02, 2017 at 10:08:28PM -0800, Andy Lutomirski wrote:
>> On Mon, Jan 2, 2017 at 12:44 AM, Arnd Bergmann <arnd@arndb.de> wrote:
>> > On Tuesday, December 27, 2016 4:54:13 AM CET Kirill A. Shutemov wrote:
>> >> As with other resources you can set the limit lower than current usage.
>> >> It would affect only future virtual address space allocations.
>>
>> I still don't buy all these use cases:
>>
>> >>
>> >> Use-cases for new rlimit:
>> >>
>> >> - Bumping the soft limit to RLIM_INFINITY, allows current process all
>> >> its children to use addresses above 47-bits.
>>
>> OK, I get this, but only as a workaround for programs that make
>> assumptions about the address space and don't use some mechanism (to
>> be designed?) to work correctly in spite of a larger address space.
>
> I guess you've misread the case. It's opt-in for large adrress space, not
> other way around.
>
> I believe 47-bit VA by default is right way to go to make the transition
> without breaking userspace.
What I meant was: setting the rlimit to anything other than -1ULL is a
workaround, but otherwise I agree. This still makes little sense if
set by PAM or other conventional rlimit tools.
>> >>
>> >> - Lowering the hard limit to 47-bits would prevent current process all
>> >> its children to use addresses above 47-bits, unless a process has
>> >> CAP_SYS_RESOURCES.
>>
>> I've tried and I can't imagine any reason to do this.
>
> That's just if something went wrong and we want to stop an application
> from use addresses above 47-bit.
But CAP_SYS_RESOURCES still makes no sense in this context.
>
>> >> - It?s also can be handy to lower hard or soft limit to arbitrary
>> >> address. User-mode emulation in QEMU may lower the limit to 32-bit
>> >> to emulate 32-bit machine on 64-bit host.
>>
>> I don't understand. QEMU user-mode emulation intercepts all syscalls.
>> What QEMU would *actually* want is a way to say "allocate me some
>> memory with the high N bits clear". mmap-via-int80 on x86 should be
>> fixed to do this, but a new syscall with an explicit parameter would
>> work, as would a prctl changing the current limit.
>
> Look at mess in mmap_find_vma(). QEmu has to guess where is free virtual
> memory. That's unnessesary complex.
>
> prctl would work for this too. new-mmap would *not*: there are more ways
> to allocate vitual address space: shmat(), mremap(). Changing all of them
> just for this is stupid.
Fair enough.
Except that mmap-via-int80, shmat-via-int80, etc should still work (if
I understand what qemu needs correctly), as would the prctl.
>
>> >>
>> >> TODO:
>> >> - port to non-x86;
>> >>
>> >> Not-yet-signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
>> >> Cc: linux-api at vger.kernel.org
>> >
>> > This seems to nicely address the same problem on arm64, which has
>> > run into the same issue due to the various page table formats
>> > that can currently be chosen at compile time.
>>
>> On further reflection, I think this has very little to do with paging
>> formats except insofar as paging formats make us notice the problem.
>> The issue is that user code wants to be able to assume an upper limit
>> on an address, and it gets an upper limit right now that depends on
>> architecture due to paging formats. But someone really might want to
>> write a *portable* 64-bit program that allocates memory with the high
>> 16 bits clear. So let's add such a mechanism directly.
>>
>> As a thought experiment, what if x86_64 simply never allocated "high"
>> (above 2^47-1) addresses unless a new mmap-with-explicit-limit syscall
>> were used? Old glibc would continue working. Old VMs would work.
>> New programs that want to use ginormous mappings would have to use the
>> new syscall. This would be totally stateless and would have no issues
>> with CRIU.
>
> Except, we need more than mmap as I mentioned.
>
> And what about stack? I'm not sure that everybody would be happy with
> stack in the middle of address space.
I would, personally. I think that, for very large address spaces, we
should allocate a large block of stack and get rid of the "stack grows
down forever" legacy idea. Then we would never need to worry about
the stack eventually hitting some other allocation. And 2^57 bytes is
hilariously large for a default stack.
^ permalink raw reply
* [PATCH 1/2] mmc: sdhci-iproc: Apply caps from bcm2835-mmc driver
From: Eric Anholt @ 2017-01-03 18:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483111474-29907-2-git-send-email-stefan.wahren@i2se.com>
Stefan Wahren <stefan.wahren@i2se.com> writes:
> Since the mmc module on bcm2835 neither provide a capabilities register nor
> free documentation we must rely on the downstream implementation [1].
Yeah, caps reg is wired to 0, but it can do high speed.
Since we're VDD_330 only, I'm not sure the driver type flags do
anything, but I'm still good with having consistency.
Reviewed-by: Eric Anholt <eric@anholt.net>
Interested in porting over the DMA support? :)
I also noticed that the emmc wrapper module claims it can run with a
clock from 50-100mhz, and the arasan says it can run at up to 50, 52, or
208mhz depending on SD/MMC mode. Could we get a boost from running the
module at a higher clock rate?
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 832 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170103/0285a674/attachment.sig>
^ permalink raw reply
* [PATCH] arm64: restore get_current() optimisation
From: Mark Rutland @ 2017-01-03 18:27 UTC (permalink / raw)
To: linux-arm-kernel
Hi Catalin,
My THREAD_INFO_IN_TASK series had an unintended performance regression in
get_current() / current_thread_info(). Could you please take the below as a
fix for the next rc?
Thanks,
Mark.
---->8----
Commit c02433dd6de32f04 ("arm64: split thread_info from task stack")
inverted the relationship between get_current() and
current_thread_info(), with sp_el0 now holding the current task_struct
rather than the current thead_info. The new implementation of
get_current() prevents the compiler from being able to optimize repeated
calls to either, resulting in a noticeable penalty in some
microbenchmarks.
This patch restores the previous optimisation by implementing
get_current() in the same way as our old current_thread_info(), using a
non-volatile asm statement.
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Reported-by: Davidlohr Bueso <dbueso@suse.de>
---
arch/arm64/include/asm/current.h | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/include/asm/current.h b/arch/arm64/include/asm/current.h
index f2bcbe2..86c4041 100644
--- a/arch/arm64/include/asm/current.h
+++ b/arch/arm64/include/asm/current.h
@@ -9,9 +9,17 @@
struct task_struct;
+/*
+ * We don't use read_sysreg() as we want the compiler to cache the value where
+ * possible.
+ */
static __always_inline struct task_struct *get_current(void)
{
- return (struct task_struct *)read_sysreg(sp_el0);
+ unsigned long sp_el0;
+
+ asm ("mrs %0, sp_el0" : "=r" (sp_el0));
+
+ return (struct task_struct *)sp_el0;
}
#define current get_current()
--
1.9.1
^ permalink raw reply related
* [RFC PATCH] sched: Remove set_task_state()
From: Mark Rutland @ 2017-01-03 18:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170103180658.GA7003@linux-80c1.suse>
On Tue, Jan 03, 2017 at 10:06:58AM -0800, Davidlohr Bueso wrote:
> On Tue, 03 Jan 2017, Mark Rutland wrote:
>
> >Does the below help?
>
> It does, yes. Performance is pretty much the same with either function
> without sysreg.
Great!
> With arm no longer in the picture, I'll send up another patchset with
> this change as well as Peter's cleanup remarks.
I intend to send a cleaned up version of the arm64 patch to Catalin in a
moment; the read_sysreg() issue is a regression introduced in v4.10-rc1,
so we should be able to get it in as a fix.
Thanks,
Mark.
^ permalink raw reply
* [RFC PATCH 10/10] dt-bindings: Document devicetree binding for ARM SPE
From: Will Deacon @ 2017-01-03 18:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483467027-14547-1-git-send-email-will.deacon@arm.com>
This patch documents the devicetree binding in use for ARM SPE.
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Rob Herring <robh@kernel.org>
Signed-off-by: Will Deacon <will.deacon@arm.com>
---
Documentation/devicetree/bindings/arm/spe-pmu.txt | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
create mode 100644 Documentation/devicetree/bindings/arm/spe-pmu.txt
diff --git a/Documentation/devicetree/bindings/arm/spe-pmu.txt b/Documentation/devicetree/bindings/arm/spe-pmu.txt
new file mode 100644
index 000000000000..d6540b491af4
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/spe-pmu.txt
@@ -0,0 +1,20 @@
+* ARMv8.2 Statistical Profiling Extension (SPE) Performance Monitor Units (PMU)
+
+ARMv8.2 introduces the optional Statistical Profiling Extension for collecting
+performance sample data using an in-memory trace buffer.
+
+** SPE Required properties:
+
+- compatible : should be one of:
+ "arm,arm-spe-pmu-v1"
+
+- interrupts : Exactly 1 PPI must be listed. For heterogeneous systems where
+ SPE is only supported on a subset of the CPUs, please consult
+ the arm,gic-v3 binding for details on describing a PPI partition.
+
+** Example:
+
+spe-pmu {
+ compatible = "arm,arm-spe-pmu-v1";
+ interrupts = <GIC_PPI 05 IRQ_TYPE_EDGE_RISING &part1>;
+};
--
2.1.4
^ permalink raw reply related
* [RFC PATCH 09/10] drivers/perf: Add support for ARMv8.2 Statistical Profiling Extension
From: Will Deacon @ 2017-01-03 18:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483467027-14547-1-git-send-email-will.deacon@arm.com>
The ARMv8.2 architecture introduces the Statistical Profiling Extension
(SPE). SPE provides a way to configure and collect profiling samples
from the CPU in the form of a trace buffer, which can be mapped directly
into userspace using the perf AUX buffer infrastructure.
This patch adds support for SPE in the form of a new perf driver.
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
---
drivers/perf/Kconfig | 8 +
drivers/perf/Makefile | 1 +
drivers/perf/arm_spe_pmu.c | 1247 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1256 insertions(+)
create mode 100644 drivers/perf/arm_spe_pmu.c
diff --git a/drivers/perf/Kconfig b/drivers/perf/Kconfig
index 4d5c5f9f0dbd..32b9d2756b0e 100644
--- a/drivers/perf/Kconfig
+++ b/drivers/perf/Kconfig
@@ -19,4 +19,12 @@ config XGENE_PMU
help
Say y if you want to use APM X-Gene SoC performance monitors.
+config ARM_SPE_PMU
+ tristate "Enable support for the ARMv8.2 Statistical Profiling Extension"
+ depends on PERF_EVENTS && ARM64
+ help
+ Enable perf support for the ARMv8.2 Statistical Profiling
+ Extension, which provides periodic sampling of operations in
+ the CPU pipeline and reports this via the perf AUX interface.
+
endmenu
diff --git a/drivers/perf/Makefile b/drivers/perf/Makefile
index b116e982810b..3a324da7d360 100644
--- a/drivers/perf/Makefile
+++ b/drivers/perf/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_ARM_PMU) += arm_pmu.o
obj-$(CONFIG_XGENE_PMU) += xgene_pmu.o
+obj-$(CONFIG_ARM_SPE_PMU) += arm_spe_pmu.o
diff --git a/drivers/perf/arm_spe_pmu.c b/drivers/perf/arm_spe_pmu.c
new file mode 100644
index 000000000000..88ac10aa24ac
--- /dev/null
+++ b/drivers/perf/arm_spe_pmu.c
@@ -0,0 +1,1247 @@
+/*
+ * Perf support for the Statistical Profiling Extension, introduced as
+ * part of ARMv8.2.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2016 ARM Limited
+ *
+ * Author: Will Deacon <will.deacon@arm.com>
+ */
+
+#define DRVNAME "arm_spe_pmu"
+#define pr_fmt(fmt) DRVNAME ": " fmt
+
+#include <linux/cpuhotplug.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/perf_event.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <asm/sysreg.h>
+
+/* ID registers */
+#define PMSIDR_EL1 sys_reg(3, 0, 9, 9, 7)
+#define PMSIDR_EL1_FE_SHIFT 0
+#define PMSIDR_EL1_FT_SHIFT 1
+#define PMSIDR_EL1_FL_SHIFT 2
+#define PMSIDR_EL1_ARCHINST_SHIFT 3
+#define PMSIDR_EL1_LDS_SHIFT 4
+#define PMSIDR_EL1_ERND_SHIFT 5
+#define PMSIDR_EL1_INTERVAL_SHIFT 8
+#define PMSIDR_EL1_INTERVAL_MASK 0xfUL
+#define PMSIDR_EL1_MAXSIZE_SHIFT 12
+#define PMSIDR_EL1_MAXSIZE_MASK 0xfUL
+#define PMSIDR_EL1_COUNTSIZE_SHIFT 16
+#define PMSIDR_EL1_COUNTSIZE_MASK 0xfUL
+
+#define PMBIDR_EL1 sys_reg(3, 0, 9, 10, 7)
+#define PMBIDR_EL1_ALIGN_SHIFT 0
+#define PMBIDR_EL1_ALIGN_MASK 0xfU
+#define PMBIDR_EL1_P_SHIFT 4
+#define PMBIDR_EL1_F_SHIFT 5
+
+/* Sampling controls */
+#define PMSCR_EL1 sys_reg(3, 0, 9, 9, 0)
+#define PMSCR_EL1_E0SPE_SHIFT 0
+#define PMSCR_EL1_E1SPE_SHIFT 1
+#define PMSCR_EL1_CX_SHIFT 3
+#define PMSCR_EL1_PA_SHIFT 4
+#define PMSCR_EL1_TS_SHIFT 5
+#define PMSCR_EL1_PCT_SHIFT 6
+
+#define PMSICR_EL1 sys_reg(3, 0, 9, 9, 2)
+
+#define PMSIRR_EL1 sys_reg(3, 0, 9, 9, 3)
+#define PMSIRR_EL1_RND_SHIFT 0
+#define PMSIRR_EL1_IVAL_MASK 0xffUL
+
+/* Filtering controls */
+#define PMSFCR_EL1 sys_reg(3, 0, 9, 9, 4)
+#define PMSFCR_EL1_FE_SHIFT 0
+#define PMSFCR_EL1_FT_SHIFT 1
+#define PMSFCR_EL1_FL_SHIFT 2
+#define PMSFCR_EL1_B_SHIFT 16
+#define PMSFCR_EL1_LD_SHIFT 17
+#define PMSFCR_EL1_ST_SHIFT 18
+
+#define PMSEVFR_EL1 sys_reg(3, 0, 9, 9, 5)
+#define PMSEVFR_EL1_RES0 0x0000ffff00ff0f55UL
+
+#define PMSLATFR_EL1 sys_reg(3, 0, 9, 9, 6)
+#define PMSLATFR_EL1_MINLAT_SHIFT 0
+
+/* Buffer controls */
+#define PMBLIMITR_EL1 sys_reg(3, 0, 9, 10, 0)
+#define PMBLIMITR_EL1_E_SHIFT 0
+#define PMBLIMITR_EL1_FM_SHIFT 1
+#define PMBLIMITR_EL1_FM_MASK 0x3UL
+#define PMBLIMITR_EL1_FM_STOP_IRQ (0 << PMBLIMITR_EL1_FM_SHIFT)
+
+#define PMBPTR_EL1 sys_reg(3, 0, 9, 10, 1)
+
+/* Buffer error reporting */
+#define PMBSR_EL1 sys_reg(3, 0, 9, 10, 3)
+#define PMBSR_EL1_COLL_SHIFT 16
+#define PMBSR_EL1_S_SHIFT 17
+#define PMBSR_EL1_EA_SHIFT 18
+#define PMBSR_EL1_DL_SHIFT 19
+#define PMBSR_EL1_EC_SHIFT 26
+#define PMBSR_EL1_EC_MASK 0x3fUL
+
+#define PMBSR_EL1_EC_BUF (0x0UL << PMBSR_EL1_EC_SHIFT)
+#define PMBSR_EL1_EC_FAULT_S1 (0x24UL << PMBSR_EL1_EC_SHIFT)
+#define PMBSR_EL1_EC_FAULT_S2 (0x25UL << PMBSR_EL1_EC_SHIFT)
+
+#define PMBSR_EL1_FAULT_FSC_SHIFT 0
+#define PMBSR_EL1_FAULT_FSC_MASK 0x3fUL
+
+#define PMBSR_EL1_BUF_BSC_SHIFT 0
+#define PMBSR_EL1_BUF_BSC_MASK 0x3fUL
+
+#define PMBSR_EL1_BUF_BSC_FULL (0x1UL << PMBSR_EL1_BUF_BSC_SHIFT)
+
+#define psb_csync() asm volatile("hint #17")
+
+struct arm_spe_pmu_buf {
+ int nr_pages;
+ bool snapshot;
+ void *base;
+};
+
+struct arm_spe_pmu {
+ struct pmu pmu;
+ struct platform_device *pdev;
+ cpumask_t supported_cpus;
+ struct hlist_node hotplug_node;
+
+ int irq; /* PPI */
+
+ u16 min_period;
+ u16 cnt_width;
+
+#define SPE_PMU_FEAT_FILT_EVT (1UL << 0)
+#define SPE_PMU_FEAT_FILT_TYP (1UL << 1)
+#define SPE_PMU_FEAT_FILT_LAT (1UL << 2)
+#define SPE_PMU_FEAT_ARCH_INST (1UL << 3)
+#define SPE_PMU_FEAT_LDS (1UL << 4)
+#define SPE_PMU_FEAT_ERND (1UL << 5)
+#define SPE_PMU_FEAT_DEV_PROBED (1UL << 63)
+ u64 features;
+
+ u16 max_record_sz;
+ u16 align;
+ struct perf_output_handle __percpu *handle;
+};
+
+#define to_spe_pmu(p) (container_of(p, struct arm_spe_pmu, pmu))
+
+/* Convert a free-running index from perf into an SPE buffer offset */
+#define PERF_IDX2OFF(idx, buf) ((idx) & (((buf)->nr_pages << PAGE_SHIFT) - 1))
+
+/* Convert a limit register into an SPE buffer offset */
+#define PMBLIMITR2OFF(lim, buf) (((lim) & PAGE_MASK) - (u64)((buf)->base))
+
+/* Keep track of our dynamic hotplug state */
+static enum cpuhp_state arm_spe_pmu_online;
+
+/* This sysfs gunk was really good fun to write. */
+enum arm_spe_pmu_capabilities {
+ SPE_PMU_CAP_ARCH_INST = 0,
+ SPE_PMU_CAP_ERND,
+ SPE_PMU_CAP_FEAT_MAX,
+ SPE_PMU_CAP_CNT_SZ = SPE_PMU_CAP_FEAT_MAX,
+ SPE_PMU_CAP_MIN_IVAL,
+};
+
+static int arm_spe_pmu_feat_caps[SPE_PMU_CAP_FEAT_MAX] = {
+ [SPE_PMU_CAP_ARCH_INST] = SPE_PMU_FEAT_ARCH_INST,
+ [SPE_PMU_CAP_ERND] = SPE_PMU_FEAT_ERND,
+};
+
+static u32 arm_spe_pmu_cap_get(struct arm_spe_pmu *spe_pmu, int cap)
+{
+ if (cap < SPE_PMU_CAP_FEAT_MAX)
+ return !!(spe_pmu->features & arm_spe_pmu_feat_caps[cap]);
+
+ switch (cap) {
+ case SPE_PMU_CAP_CNT_SZ:
+ return spe_pmu->cnt_width;
+ case SPE_PMU_CAP_MIN_IVAL:
+ return spe_pmu->min_period;
+ default:
+ WARN(1, "unknown cap %d\n", cap);
+ }
+
+ return 0;
+}
+
+static ssize_t arm_spe_pmu_cap_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct arm_spe_pmu *spe_pmu = platform_get_drvdata(pdev);
+ struct dev_ext_attribute *ea =
+ container_of(attr, struct dev_ext_attribute, attr);
+ int cap = (long)ea->var;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ arm_spe_pmu_cap_get(spe_pmu, cap));
+}
+
+#define SPE_EXT_ATTR_ENTRY(_name, _func, _var) \
+ &((struct dev_ext_attribute[]) { \
+ { __ATTR(_name, S_IRUGO, _func, NULL), (void *)_var } \
+ })[0].attr.attr
+
+#define SPE_CAP_EXT_ATTR_ENTRY(_name, _var) \
+ SPE_EXT_ATTR_ENTRY(_name, arm_spe_pmu_cap_show, _var)
+
+static struct attribute *arm_spe_pmu_cap_attr[] = {
+ SPE_CAP_EXT_ATTR_ENTRY(arch_inst, SPE_PMU_CAP_ARCH_INST),
+ SPE_CAP_EXT_ATTR_ENTRY(ernd, SPE_PMU_CAP_ERND),
+ SPE_CAP_EXT_ATTR_ENTRY(count_size, SPE_PMU_CAP_CNT_SZ),
+ SPE_CAP_EXT_ATTR_ENTRY(min_interval, SPE_PMU_CAP_MIN_IVAL),
+ NULL,
+};
+
+static struct attribute_group arm_spe_pmu_cap_group = {
+ .name = "caps",
+ .attrs = arm_spe_pmu_cap_attr,
+};
+
+/* User ABI */
+#define ATTR_CFG_FLD_ts_enable_CFG config /* PMSCR_EL1.TS */
+#define ATTR_CFG_FLD_ts_enable_LO 0
+#define ATTR_CFG_FLD_ts_enable_HI 0
+#define ATTR_CFG_FLD_pa_enable_CFG config /* PMSCR_EL1.PA */
+#define ATTR_CFG_FLD_pa_enable_LO 1
+#define ATTR_CFG_FLD_pa_enable_HI 1
+#define ATTR_CFG_FLD_jitter_CFG config /* PMSIRR_EL1.RND */
+#define ATTR_CFG_FLD_jitter_LO 16
+#define ATTR_CFG_FLD_jitter_HI 16
+#define ATTR_CFG_FLD_branch_filter_CFG config /* PMSFCR_EL1.B */
+#define ATTR_CFG_FLD_branch_filter_LO 32
+#define ATTR_CFG_FLD_branch_filter_HI 32
+#define ATTR_CFG_FLD_load_filter_CFG config /* PMSFCR_EL1.LD */
+#define ATTR_CFG_FLD_load_filter_LO 33
+#define ATTR_CFG_FLD_load_filter_HI 33
+#define ATTR_CFG_FLD_store_filter_CFG config /* PMSFCR_EL1.ST */
+#define ATTR_CFG_FLD_store_filter_LO 34
+#define ATTR_CFG_FLD_store_filter_HI 34
+
+#define ATTR_CFG_FLD_event_filter_CFG config1 /* PMSEVFR_EL1 */
+#define ATTR_CFG_FLD_event_filter_LO 0
+#define ATTR_CFG_FLD_event_filter_HI 63
+
+#define ATTR_CFG_FLD_min_latency_CFG config2 /* PMSLATFR_EL1.MINLAT */
+#define ATTR_CFG_FLD_min_latency_LO 0
+#define ATTR_CFG_FLD_min_latency_HI 11
+
+/* Why does everything I do descend into this? */
+#define __GEN_PMU_FORMAT_ATTR(cfg, lo, hi) \
+ (lo) == (hi) ? #cfg ":" #lo "\n" : #cfg ":" #lo "-" #hi
+
+#define _GEN_PMU_FORMAT_ATTR(cfg, lo, hi) \
+ __GEN_PMU_FORMAT_ATTR(cfg, lo, hi)
+
+#define GEN_PMU_FORMAT_ATTR(name) \
+ PMU_FORMAT_ATTR(name, \
+ _GEN_PMU_FORMAT_ATTR(ATTR_CFG_FLD_##name##_CFG, \
+ ATTR_CFG_FLD_##name##_LO, \
+ ATTR_CFG_FLD_##name##_HI))
+
+#define _ATTR_CFG_GET_FLD(attr, cfg, lo, hi) \
+ ((((attr)->cfg) >> lo) & GENMASK(hi - lo, 0))
+
+#define ATTR_CFG_GET_FLD(attr, name) \
+ _ATTR_CFG_GET_FLD(attr, \
+ ATTR_CFG_FLD_##name##_CFG, \
+ ATTR_CFG_FLD_##name##_LO, \
+ ATTR_CFG_FLD_##name##_HI)
+
+GEN_PMU_FORMAT_ATTR(ts_enable);
+GEN_PMU_FORMAT_ATTR(pa_enable);
+GEN_PMU_FORMAT_ATTR(jitter);
+GEN_PMU_FORMAT_ATTR(load_filter);
+GEN_PMU_FORMAT_ATTR(store_filter);
+GEN_PMU_FORMAT_ATTR(branch_filter);
+GEN_PMU_FORMAT_ATTR(event_filter);
+GEN_PMU_FORMAT_ATTR(min_latency);
+
+static struct attribute *arm_spe_pmu_formats_attr[] = {
+ &format_attr_ts_enable.attr,
+ &format_attr_pa_enable.attr,
+ &format_attr_jitter.attr,
+ &format_attr_load_filter.attr,
+ &format_attr_store_filter.attr,
+ &format_attr_branch_filter.attr,
+ &format_attr_event_filter.attr,
+ &format_attr_min_latency.attr,
+ NULL,
+};
+
+static struct attribute_group arm_spe_pmu_format_group = {
+ .name = "format",
+ .attrs = arm_spe_pmu_formats_attr,
+};
+
+static ssize_t arm_spe_pmu_get_attr_cpumask(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct arm_spe_pmu *spe_pmu = platform_get_drvdata(pdev);
+
+ return cpumap_print_to_pagebuf(true, buf, &spe_pmu->supported_cpus);
+}
+static DEVICE_ATTR(cpumask, S_IRUGO, arm_spe_pmu_get_attr_cpumask, NULL);
+
+static struct attribute *arm_spe_pmu_attrs[] = {
+ &dev_attr_cpumask.attr,
+ NULL,
+};
+
+static struct attribute_group arm_spe_pmu_group = {
+ .attrs = arm_spe_pmu_attrs,
+};
+
+static const struct attribute_group *arm_spe_pmu_attr_groups[] = {
+ &arm_spe_pmu_group,
+ &arm_spe_pmu_cap_group,
+ &arm_spe_pmu_format_group,
+ NULL,
+};
+
+/* Convert between user ABI and register values */
+static u64 arm_spe_event_to_pmscr(struct perf_event *event)
+{
+ struct perf_event_attr *attr = &event->attr;
+ u64 reg = 0;
+
+ reg |= ATTR_CFG_GET_FLD(attr, ts_enable) << PMSCR_EL1_TS_SHIFT;
+ reg |= ATTR_CFG_GET_FLD(attr, pa_enable) << PMSCR_EL1_PA_SHIFT;
+
+ if (!attr->exclude_user)
+ reg |= BIT(PMSCR_EL1_E0SPE_SHIFT);
+
+ if (!attr->exclude_kernel)
+ reg |= BIT(PMSCR_EL1_E1SPE_SHIFT);
+
+ if (IS_ENABLED(CONFIG_PID_IN_CONTEXTIDR))
+ reg |= BIT(PMSCR_EL1_CX_SHIFT);
+
+ return reg;
+}
+
+static void arm_spe_event_sanitise_period(struct perf_event *event)
+{
+ struct arm_spe_pmu *spe_pmu = to_spe_pmu(event->pmu);
+ u64 period = event->hw.sample_period & ~PMSIRR_EL1_IVAL_MASK;
+
+ if (period < spe_pmu->min_period)
+ period = spe_pmu->min_period;
+
+ event->hw.sample_period = period;
+}
+
+static u64 arm_spe_event_to_pmsirr(struct perf_event *event)
+{
+ struct perf_event_attr *attr = &event->attr;
+ u64 reg = 0;
+
+ arm_spe_event_sanitise_period(event);
+
+ reg |= ATTR_CFG_GET_FLD(attr, jitter) << PMSIRR_EL1_RND_SHIFT;
+ reg |= event->hw.sample_period;
+
+ return reg;
+}
+
+static u64 arm_spe_event_to_pmsfcr(struct perf_event *event)
+{
+ struct perf_event_attr *attr = &event->attr;
+ u64 reg = 0;
+
+ reg |= ATTR_CFG_GET_FLD(attr, load_filter) << PMSFCR_EL1_LD_SHIFT;
+ reg |= ATTR_CFG_GET_FLD(attr, store_filter) << PMSFCR_EL1_ST_SHIFT;
+ reg |= ATTR_CFG_GET_FLD(attr, branch_filter) << PMSFCR_EL1_B_SHIFT;
+
+ if (reg)
+ reg |= BIT(PMSFCR_EL1_FT_SHIFT);
+
+ if (ATTR_CFG_GET_FLD(attr, event_filter))
+ reg |= BIT(PMSFCR_EL1_FE_SHIFT);
+
+ if (ATTR_CFG_GET_FLD(attr, min_latency))
+ reg |= BIT(PMSFCR_EL1_FL_SHIFT);
+
+ return reg;
+}
+
+static u64 arm_spe_event_to_pmsevfr(struct perf_event *event)
+{
+ struct perf_event_attr *attr = &event->attr;
+ return ATTR_CFG_GET_FLD(attr, event_filter);
+}
+
+static u64 arm_spe_event_to_pmslatfr(struct perf_event *event)
+{
+ struct perf_event_attr *attr = &event->attr;
+ return ATTR_CFG_GET_FLD(attr, min_latency) << PMSLATFR_EL1_MINLAT_SHIFT;
+}
+
+static bool arm_spe_pmu_buffer_mgmt_pending(u64 pmbsr)
+{
+ const char *err_str;
+
+ /* Service required? */
+ if (!(pmbsr & BIT(PMBSR_EL1_S_SHIFT)))
+ return false;
+
+ /* We only expect buffer management events */
+ switch (pmbsr & (PMBSR_EL1_EC_MASK << PMBSR_EL1_EC_SHIFT)) {
+ case PMBSR_EL1_EC_BUF:
+ /* Handled below */
+ break;
+ case PMBSR_EL1_EC_FAULT_S1:
+ case PMBSR_EL1_EC_FAULT_S2:
+ err_str = "Unexpected buffer fault";
+ goto out_err;
+ default:
+ err_str = "Unknown error code";
+ goto out_err;
+ }
+
+ /* Buffer management event */
+ switch (pmbsr & (PMBSR_EL1_BUF_BSC_MASK << PMBSR_EL1_BUF_BSC_SHIFT)) {
+ case PMBSR_EL1_BUF_BSC_FULL:
+ return true;
+ default:
+ err_str = "Unknown buffer status code";
+ }
+
+out_err:
+ pr_err_ratelimited("%s on CPU %d [PMBSR=0x%08llx]\n", err_str,
+ smp_processor_id(), pmbsr);
+ return false;
+}
+
+static u64 arm_spe_pmu_next_snapshot_off(struct perf_output_handle *handle)
+{
+ struct arm_spe_pmu_buf *buf = perf_get_aux(handle);
+ struct arm_spe_pmu *spe_pmu = to_spe_pmu(handle->event->pmu);
+ u64 head = PERF_IDX2OFF(handle->head, buf);
+ u64 limit = buf->nr_pages * PAGE_SIZE;
+
+ /*
+ * The trace format isn't parseable in reverse, so clamp
+ * the limit to half of the buffer size in snapshot mode
+ * so that the worst case is half a buffer of records, as
+ * opposed to a single record.
+ */
+ if (head < limit >> 1)
+ limit >>= 1;
+
+ /*
+ * If we're within max_record_sz of the limit, we must
+ * pad, move the head index and recompute the limit.
+ */
+ if (limit - head < spe_pmu->max_record_sz) {
+ memset(buf->base + head, 0, limit - head);
+ handle->head = PERF_IDX2OFF(limit, buf);
+ limit = ((buf->nr_pages * PAGE_SIZE) >> 1) + handle->head;
+ }
+
+ return limit;
+}
+
+static u64 __arm_spe_pmu_next_off(struct perf_output_handle *handle)
+{
+ struct arm_spe_pmu_buf *buf = perf_get_aux(handle);
+ u64 head = PERF_IDX2OFF(handle->head, buf);
+ u64 tail = PERF_IDX2OFF(handle->head + handle->size, buf);
+ u64 wakeup = PERF_IDX2OFF(handle->wakeup, buf);
+ u64 limit = buf->nr_pages * PAGE_SIZE;
+
+ /*
+ * Set the limit pointer to either the watermark or the
+ * current tail pointer; whichever comes first.
+ */
+ if (handle->head + handle->size <= handle->wakeup) {
+ /* The tail is next, so check for wrapping */
+ if (tail >= head) {
+ /*
+ * No wrapping, but need to align downwards to
+ * avoid corrupting unconsumed data.
+ */
+ limit = round_down(tail, PAGE_SIZE);
+
+ }
+ } else if (wakeup >= head) {
+ /*
+ * The wakeup is next and doesn't wrap. Align upwards to
+ * ensure that we do indeed reach the watermark.
+ */
+ limit = round_up(wakeup, PAGE_SIZE);
+
+ /*
+ * If rounding up crosses the tail, then we have to
+ * round down to avoid corrupting unconsumed data.
+ * Hopefully the tail will have moved by the time we
+ * hit the new limit.
+ */
+ if (wakeup < tail && limit > tail)
+ limit = round_down(wakeup, PAGE_SIZE);
+ }
+
+ /*
+ * If rounding down crosses the head, then the buffer is full,
+ * so pad to tail and end the session.
+ */
+ if (limit <= head) {
+ memset(buf->base + head, 0, handle->size);
+ perf_aux_output_skip(handle, handle->size);
+ perf_aux_output_end(handle, 0, PERF_AUX_FLAG_TRUNCATED);
+ limit = 0;
+ }
+
+ return limit;
+}
+
+static u64 arm_spe_pmu_next_off(struct perf_output_handle *handle)
+{
+ struct arm_spe_pmu_buf *buf = perf_get_aux(handle);
+ struct arm_spe_pmu *spe_pmu = to_spe_pmu(handle->event->pmu);
+ u64 limit = __arm_spe_pmu_next_off(handle);
+ u64 head = PERF_IDX2OFF(handle->head, buf);
+
+ /*
+ * If the head has come too close to the end of the buffer,
+ * then pad to the end and recompute the limit.
+ */
+ if (limit && (limit - head < spe_pmu->max_record_sz)) {
+ memset(buf->base + head, 0, limit - head);
+ perf_aux_output_skip(handle, limit - head);
+ limit = __arm_spe_pmu_next_off(handle);
+ }
+
+ return limit;
+}
+
+static void arm_spe_perf_aux_output_begin(struct perf_output_handle *handle,
+ struct perf_event *event)
+{
+ u64 base, limit;
+ struct arm_spe_pmu_buf *buf;
+
+ /* Start a new aux session */
+ buf = perf_aux_output_begin(handle, event);
+ if (!buf) {
+ event->hw.state |= PERF_HES_STOPPED;
+ /*
+ * We still need to clear the limit pointer, since the
+ * profiler might only be disabled by virtue of a fault.
+ */
+ limit = 0;
+ goto out_write_limit;
+ }
+
+ limit = buf->snapshot ? arm_spe_pmu_next_snapshot_off(handle)
+ : arm_spe_pmu_next_off(handle);
+ if (limit)
+ limit |= BIT(PMBLIMITR_EL1_E_SHIFT);
+
+ base = (u64)buf->base + PERF_IDX2OFF(handle->head, buf);
+ write_sysreg_s(base, PMBPTR_EL1);
+ limit += (u64)buf->base;
+
+out_write_limit:
+ write_sysreg_s(limit, PMBLIMITR_EL1);
+}
+
+static bool arm_spe_perf_aux_output_end(struct perf_output_handle *handle,
+ struct perf_event *event,
+ bool resume)
+{
+ u64 pmbptr, pmbsr, offset, size;
+ struct arm_spe_pmu *spe_pmu = to_spe_pmu(event->pmu);
+ struct arm_spe_pmu_buf *buf = perf_get_aux(handle);
+ bool truncated, collided;
+
+ /*
+ * We can be called via IRQ work trying to disable the PMU after
+ * a buffer full event. In this case, the aux session has already
+ * been stopped, so there's nothing to do here.
+ */
+ if (!buf)
+ return false;
+
+ /*
+ * Work out how much data has been written since the last update
+ * to the head index.
+ */
+ pmbptr = round_down(read_sysreg_s(PMBPTR_EL1), spe_pmu->align);
+ offset = pmbptr - (u64)buf->base;
+ size = offset - PERF_IDX2OFF(handle->head, buf);
+
+ if (buf->snapshot)
+ handle->head = offset;
+
+ /*
+ * If there isn't a pending management event and we're not stopping
+ * the current session, then just leave everything alone.
+ */
+ pmbsr = read_sysreg_s(PMBSR_EL1);
+ if (!arm_spe_pmu_buffer_mgmt_pending(pmbsr) && resume)
+ return false; /* Spurious IRQ */
+
+ /*
+ * Either the buffer is full or we're stopping the session. Check
+ * that we didn't write a partial record, since this can result
+ * in unparseable trace and we must disable the event.
+ */
+ collided = pmbsr & BIT(PMBSR_EL1_COLL_SHIFT);
+ truncated = pmbsr & BIT(PMBSR_EL1_DL_SHIFT);
+ perf_aux_output_end(handle, size,
+ (truncated ? PERF_AUX_FLAG_TRUNCATED : 0) |
+ (collided ? PERF_AUX_FLAG_COLLISION : 0));
+
+ /*
+ * If we're not resuming the session, then we can clear the fault
+ * and we're done, otherwise we need to start a new session.
+ */
+ if (!resume)
+ write_sysreg_s(0, PMBSR_EL1);
+ else if (!truncated)
+ arm_spe_perf_aux_output_begin(handle, event);
+
+ return true;
+}
+
+/* IRQ handling */
+static irqreturn_t arm_spe_pmu_irq_handler(int irq, void *dev)
+{
+ struct perf_output_handle *handle = dev;
+
+ if (!perf_get_aux(handle))
+ return IRQ_NONE;
+
+ if (!arm_spe_perf_aux_output_end(handle, handle->event, true))
+ return IRQ_NONE;
+
+ irq_work_run();
+ isb(); /* Ensure the buffer is disabled if data loss has occurred */
+ write_sysreg_s(0, PMBSR_EL1);
+ return IRQ_HANDLED;
+}
+
+/* Perf callbacks */
+static int arm_spe_pmu_event_init(struct perf_event *event)
+{
+ u64 reg;
+ struct perf_event_attr *attr = &event->attr;
+ struct arm_spe_pmu *spe_pmu = to_spe_pmu(event->pmu);
+
+ /* This is, of course, deeply driver-specific */
+ if (attr->type != event->pmu->type)
+ return -ENOENT;
+
+ if (event->cpu >= 0 &&
+ !cpumask_test_cpu(event->cpu, &spe_pmu->supported_cpus))
+ return -ENOENT;
+
+ if (arm_spe_event_to_pmsevfr(event) & PMSEVFR_EL1_RES0)
+ return -EOPNOTSUPP;
+
+ if (event->hw.sample_period < spe_pmu->min_period ||
+ event->hw.sample_period & PMSIRR_EL1_IVAL_MASK)
+ return -EOPNOTSUPP;
+
+ if (attr->exclude_idle)
+ return -EOPNOTSUPP;
+
+ /*
+ * Feedback-directed frequency throttling doesn't work when we
+ * have a buffer of samples. We'd need to manually count the
+ * samples in the buffer when it fills up and adjust the event
+ * count to reflect that. Instead, force the user to specify a
+ * sample period instead.
+ */
+ if (attr->freq)
+ return -EINVAL;
+
+ if (is_kernel_in_hyp_mode()) {
+ if (attr->exclude_kernel != attr->exclude_hv)
+ return -EOPNOTSUPP;
+ } else if (!attr->exclude_hv) {
+ return -EOPNOTSUPP;
+ }
+
+ reg = arm_spe_event_to_pmsfcr(event);
+ if ((reg & BIT(PMSFCR_EL1_FE_SHIFT)) &&
+ !(spe_pmu->features & SPE_PMU_FEAT_FILT_EVT))
+ return -EOPNOTSUPP;
+
+ if ((reg & BIT(PMSFCR_EL1_FT_SHIFT)) &&
+ !(spe_pmu->features & SPE_PMU_FEAT_FILT_TYP))
+ return -EOPNOTSUPP;
+
+ if ((reg & BIT(PMSFCR_EL1_FL_SHIFT)) &&
+ !(spe_pmu->features & SPE_PMU_FEAT_FILT_LAT))
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static void arm_spe_pmu_start(struct perf_event *event, int flags)
+{
+ u64 reg;
+ struct arm_spe_pmu *spe_pmu = to_spe_pmu(event->pmu);
+ struct hw_perf_event *hwc = &event->hw;
+ struct perf_output_handle *handle = this_cpu_ptr(spe_pmu->handle);
+
+ hwc->state = 0;
+ arm_spe_perf_aux_output_begin(handle, event);
+ if (hwc->state)
+ return;
+
+ reg = arm_spe_event_to_pmsfcr(event);
+ write_sysreg_s(reg, PMSFCR_EL1);
+
+ reg = arm_spe_event_to_pmsevfr(event);
+ write_sysreg_s(reg, PMSEVFR_EL1);
+
+ reg = arm_spe_event_to_pmslatfr(event);
+ write_sysreg_s(reg, PMSLATFR_EL1);
+
+ if (flags & PERF_EF_RELOAD) {
+ reg = arm_spe_event_to_pmsirr(event);
+ write_sysreg_s(reg, PMSIRR_EL1);
+ isb();
+ reg = local64_read(&hwc->period_left);
+ write_sysreg_s(reg, PMSICR_EL1);
+ }
+
+ reg = arm_spe_event_to_pmscr(event);
+ isb();
+ write_sysreg_s(reg, PMSCR_EL1);
+}
+
+static void arm_spe_pmu_disable_and_drain_local(void)
+{
+ /* Disable profiling at EL0 and EL1 */
+ write_sysreg_s(0, PMSCR_EL1);
+ isb();
+
+ /* Drain any buffered data */
+ psb_csync();
+ dsb(nsh);
+
+ /* Disable the profiling buffer */
+ write_sysreg_s(0, PMBLIMITR_EL1);
+}
+
+static void arm_spe_pmu_stop(struct perf_event *event, int flags)
+{
+ struct arm_spe_pmu *spe_pmu = to_spe_pmu(event->pmu);
+ struct hw_perf_event *hwc = &event->hw;
+ struct perf_output_handle *handle = this_cpu_ptr(spe_pmu->handle);
+
+ /* If we're already stopped, then nothing to do */
+ if (hwc->state & PERF_HES_STOPPED)
+ return;
+
+ /* Stop all trace generation */
+ arm_spe_pmu_disable_and_drain_local();
+
+ if (flags & PERF_EF_UPDATE) {
+ /* Ensure hardware updates to PMBPTR_EL1 are visible */
+ isb();
+ arm_spe_perf_aux_output_end(handle, event, false);
+ /*
+ * This may also contain ECOUNT, but nobody else should
+ * be looking at period_left, since we forbid frequency
+ * based sampling.
+ */
+ local64_set(&hwc->period_left, read_sysreg_s(PMSICR_EL1));
+ hwc->state |= PERF_HES_UPTODATE;
+ }
+
+ hwc->state |= PERF_HES_STOPPED;
+}
+
+static int arm_spe_pmu_add(struct perf_event *event, int flags)
+{
+ int ret = 0;
+ struct arm_spe_pmu *spe_pmu = to_spe_pmu(event->pmu);
+ struct hw_perf_event *hwc = &event->hw;
+ int cpu = event->cpu == -1 ? smp_processor_id() : event->cpu;
+
+ if (!cpumask_test_cpu(cpu, &spe_pmu->supported_cpus))
+ return -ENOENT;
+
+ hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
+
+ if (flags & PERF_EF_START) {
+ arm_spe_pmu_start(event, PERF_EF_RELOAD);
+ if (hwc->state & PERF_HES_STOPPED)
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static void arm_spe_pmu_del(struct perf_event *event, int flags)
+{
+ arm_spe_pmu_stop(event, PERF_EF_UPDATE);
+}
+
+static void arm_spe_pmu_read(struct perf_event *event)
+{
+}
+
+static void *arm_spe_pmu_setup_aux(int cpu, void **pages, int nr_pages,
+ bool snapshot)
+{
+ int i;
+ struct page **pglist;
+ struct arm_spe_pmu_buf *buf;
+
+ /*
+ * We require an even number of pages for snapshot mode, so that
+ * we can effectively treat the buffer as consisting of two equal
+ * parts and give userspace a fighting chance of getting some
+ * useful data out of it.
+ */
+ if (!nr_pages || (snapshot && (nr_pages & 1)))
+ return NULL;
+
+ buf = kzalloc_node(sizeof(*buf), GFP_KERNEL, cpu_to_node(cpu));
+ if (!buf)
+ return NULL;
+
+ pglist = kcalloc(nr_pages, sizeof(*pglist), GFP_KERNEL);
+ if (!pglist)
+ goto out_free_buf;
+
+ for (i = 0; i < nr_pages; ++i) {
+ struct page *page = virt_to_page(pages[i]);
+
+ if (PagePrivate(page)) {
+ pr_warn("unexpected high-order page for auxbuf!");
+ goto out_free_pglist;
+ }
+
+ pglist[i] = virt_to_page(pages[i]);
+ }
+
+ buf->base = vmap(pglist, nr_pages, VM_MAP, PAGE_KERNEL);
+ if (!buf->base)
+ goto out_free_pglist;
+
+ buf->nr_pages = nr_pages;
+ buf->snapshot = snapshot;
+
+ kfree(pglist);
+ return buf;
+
+out_free_pglist:
+ kfree(pglist);
+out_free_buf:
+ kfree(buf);
+ return NULL;
+}
+
+static void arm_spe_pmu_free_aux(void *aux)
+{
+ struct arm_spe_pmu_buf *buf = aux;
+
+ vunmap(buf->base);
+ kfree(buf);
+}
+
+/* Initialisation and teardown functions */
+static int arm_spe_pmu_perf_init(struct arm_spe_pmu *spe_pmu)
+{
+ static atomic_t pmu_idx = ATOMIC_INIT(-1);
+
+ int idx;
+ char *name;
+ struct device *dev = &spe_pmu->pdev->dev;
+
+ spe_pmu->pmu = (struct pmu) {
+ .capabilities = PERF_PMU_CAP_EXCLUSIVE | PERF_PMU_CAP_ITRACE,
+ .attr_groups = arm_spe_pmu_attr_groups,
+ /*
+ * We hitch a ride on the software context here, so that
+ * we can support per-task profiling (which is not possible
+ * with the invalid context as it doesn't get sched callbacks).
+ * This requires that userspace either uses a dummy event for
+ * perf_event_open, since the aux buffer is not setup until
+ * a subsequent mmap, or creates the profiling event in a
+ * disabled state and explicitly PERF_EVENT_IOC_ENABLEs it
+ * once the buffer has been created.
+ */
+ .task_ctx_nr = perf_sw_context,
+ .event_init = arm_spe_pmu_event_init,
+ .add = arm_spe_pmu_add,
+ .del = arm_spe_pmu_del,
+ .start = arm_spe_pmu_start,
+ .stop = arm_spe_pmu_stop,
+ .read = arm_spe_pmu_read,
+ .setup_aux = arm_spe_pmu_setup_aux,
+ .free_aux = arm_spe_pmu_free_aux,
+ };
+
+ idx = atomic_inc_return(&pmu_idx);
+ name = devm_kasprintf(dev, GFP_KERNEL, "%s_%d", DRVNAME, idx);
+ return perf_pmu_register(&spe_pmu->pmu, name, -1);
+}
+
+static void arm_spe_pmu_perf_destroy(struct arm_spe_pmu *spe_pmu)
+{
+ perf_pmu_unregister(&spe_pmu->pmu);
+}
+
+static void __arm_spe_pmu_dev_probe(void *info)
+{
+ int fld;
+ u64 reg;
+ struct arm_spe_pmu *spe_pmu = info;
+ struct device *dev = &spe_pmu->pdev->dev;
+
+ fld = cpuid_feature_extract_unsigned_field(read_cpuid(ID_AA64DFR0_EL1),
+ ID_AA64DFR0_PMSVER_SHIFT);
+ if (!fld) {
+ dev_err(dev,
+ "unsupported ID_AA64DFR0_EL1.PMSVer [%d] on CPU %d\n",
+ fld, smp_processor_id());
+ return;
+ }
+
+ /* Read PMBIDR first to determine whether or not we have access */
+ reg = read_sysreg_s(PMBIDR_EL1);
+ if (reg & BIT(PMBIDR_EL1_P_SHIFT)) {
+ dev_err(dev,
+ "profiling buffer owned by higher exception level\n");
+ return;
+ }
+
+ /* Minimum alignment. If it's out-of-range, then fail the probe */
+ fld = reg >> PMBIDR_EL1_ALIGN_SHIFT & PMBIDR_EL1_ALIGN_MASK;
+ spe_pmu->align = 1 << fld;
+ if (spe_pmu->align > SZ_2K) {
+ dev_err(dev, "unsupported PMBIDR.Align [%d] on CPU %d\n",
+ fld, smp_processor_id());
+ return;
+ }
+
+ /* It's now safe to read PMSIDR and figure out what we've got */
+ reg = read_sysreg_s(PMSIDR_EL1);
+ if (reg & BIT(PMSIDR_EL1_FE_SHIFT))
+ spe_pmu->features |= SPE_PMU_FEAT_FILT_EVT;
+
+ if (reg & BIT(PMSIDR_EL1_FT_SHIFT))
+ spe_pmu->features |= SPE_PMU_FEAT_FILT_TYP;
+
+ if (reg & BIT(PMSIDR_EL1_FL_SHIFT))
+ spe_pmu->features |= SPE_PMU_FEAT_FILT_LAT;
+
+ if (reg & BIT(PMSIDR_EL1_ARCHINST_SHIFT))
+ spe_pmu->features |= SPE_PMU_FEAT_ARCH_INST;
+
+ if (reg & BIT(PMSIDR_EL1_LDS_SHIFT))
+ spe_pmu->features |= SPE_PMU_FEAT_LDS;
+
+ if (reg & BIT(PMSIDR_EL1_ERND_SHIFT))
+ spe_pmu->features |= SPE_PMU_FEAT_ERND;
+
+ /* This field has a spaced out encoding, so just use a look-up */
+ fld = reg >> PMSIDR_EL1_INTERVAL_SHIFT & PMSIDR_EL1_INTERVAL_MASK;
+ switch (fld) {
+ case 0:
+ spe_pmu->min_period = 256;
+ break;
+ case 2:
+ spe_pmu->min_period = 512;
+ break;
+ case 3:
+ spe_pmu->min_period = 768;
+ break;
+ case 4:
+ spe_pmu->min_period = 1024;
+ break;
+ case 5:
+ spe_pmu->min_period = 1536;
+ break;
+ case 6:
+ spe_pmu->min_period = 2048;
+ break;
+ case 7:
+ spe_pmu->min_period = 3072;
+ break;
+ default:
+ dev_warn(dev, "unknown PMSIDR_EL1.Interval [%d]; assuming 8\n",
+ fld);
+ /* Fallthrough */
+ case 8:
+ spe_pmu->min_period = 4096;
+ }
+
+ /* Maximum record size. If it's out-of-range, then fail the probe */
+ fld = reg >> PMSIDR_EL1_MAXSIZE_SHIFT & PMSIDR_EL1_MAXSIZE_MASK;
+ spe_pmu->max_record_sz = 1 << fld;
+ if (spe_pmu->max_record_sz > SZ_2K || spe_pmu->max_record_sz < 16) {
+ dev_err(dev, "unsupported PMSIDR_EL1.MaxSize [%d] on CPU %d\n",
+ fld, smp_processor_id());
+ return;
+ }
+
+ fld = reg >> PMSIDR_EL1_COUNTSIZE_SHIFT & PMSIDR_EL1_COUNTSIZE_MASK;
+ switch (fld) {
+ default:
+ dev_warn(dev, "unknown PMSIDR_EL1.CountSize [%d]; assuming 2\n",
+ fld);
+ /* Fallthrough */
+ case 2:
+ spe_pmu->cnt_width = 12;
+ }
+
+ dev_info(dev,
+ "probed for CPUs %*pbl [max_record_sz %u, align %u, features 0x%llx]\n",
+ cpumask_pr_args(&spe_pmu->supported_cpus),
+ spe_pmu->max_record_sz, spe_pmu->align, spe_pmu->features);
+
+ spe_pmu->features |= SPE_PMU_FEAT_DEV_PROBED;
+ return;
+}
+
+static void __arm_spe_pmu_reset_local(void)
+{
+ /*
+ * This is probably overkill, as we have no idea where we're
+ * draining any buffered data to...
+ */
+ arm_spe_pmu_disable_and_drain_local();
+
+ /* Reset the buffer base pointer */
+ write_sysreg_s(0, PMBPTR_EL1);
+ isb();
+
+ /* Clear any pending management interrupts */
+ write_sysreg_s(0, PMBSR_EL1);
+ isb();
+}
+
+static void __arm_spe_pmu_setup_one(void *info)
+{
+ struct arm_spe_pmu *spe_pmu = info;
+
+ __arm_spe_pmu_reset_local();
+ enable_percpu_irq(spe_pmu->irq, IRQ_TYPE_NONE);
+}
+
+static void __arm_spe_pmu_stop_one(void *info)
+{
+ struct arm_spe_pmu *spe_pmu = info;
+
+ disable_percpu_irq(spe_pmu->irq);
+ __arm_spe_pmu_reset_local();
+}
+
+static int arm_spe_pmu_cpu_startup(unsigned int cpu, struct hlist_node *node)
+{
+ struct arm_spe_pmu *spe_pmu;
+
+ spe_pmu = hlist_entry_safe(node, struct arm_spe_pmu, hotplug_node);
+ if (!cpumask_test_cpu(cpu, &spe_pmu->supported_cpus))
+ return 0;
+
+ __arm_spe_pmu_setup_one(spe_pmu);
+ return 0;
+}
+
+static int arm_spe_pmu_cpu_teardown(unsigned int cpu, struct hlist_node *node)
+{
+ struct arm_spe_pmu *spe_pmu;
+
+ spe_pmu = hlist_entry_safe(node, struct arm_spe_pmu, hotplug_node);
+ if (!cpumask_test_cpu(cpu, &spe_pmu->supported_cpus))
+ return 0;
+
+ __arm_spe_pmu_stop_one(spe_pmu);
+ return 0;
+}
+
+static int arm_spe_pmu_dev_init(struct arm_spe_pmu *spe_pmu)
+{
+ int ret;
+ cpumask_t *mask = &spe_pmu->supported_cpus;
+
+ /* Keep the hotplug state steady whilst we probe */
+ get_online_cpus();
+
+ /* Make sure we probe the hardware on a relevant CPU */
+ ret = smp_call_function_any(mask, __arm_spe_pmu_dev_probe, spe_pmu, 1);
+ if (ret || !(spe_pmu->features & SPE_PMU_FEAT_DEV_PROBED)) {
+ ret = -ENXIO;
+ goto out_put_cpus;
+ }
+
+ /* Request our PPIs (note that the IRQ is still disabled) */
+ ret = request_percpu_irq(spe_pmu->irq, arm_spe_pmu_irq_handler, DRVNAME,
+ spe_pmu->handle);
+ if (ret)
+ goto out_put_cpus;
+
+ /* Setup the CPUs in our mask -- this enables the IRQ */
+ on_each_cpu_mask(mask, __arm_spe_pmu_setup_one, spe_pmu, 1);
+
+ /* Register our hotplug notifier now so we don't miss any events */
+ ret = cpuhp_state_add_instance_nocalls(arm_spe_pmu_online,
+ &spe_pmu->hotplug_node);
+out_put_cpus:
+ put_online_cpus();
+ return ret;
+}
+
+/* Driver and device probing */
+static int arm_spe_pmu_irq_probe(struct arm_spe_pmu *spe_pmu)
+{
+ struct platform_device *pdev = spe_pmu->pdev;
+ int irq = platform_get_irq(pdev, 0);
+
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to get IRQ (%d)\n", irq);
+ return -ENXIO;
+ }
+
+ if (!irq_is_percpu(irq)) {
+ dev_err(&pdev->dev, "expected PPI but got SPI (%d)\n", irq);
+ return -EINVAL;
+ }
+
+ if (irq_get_percpu_devid_partition(irq, &spe_pmu->supported_cpus)) {
+ dev_err(&pdev->dev, "failed to get PPI partition (%d)\n", irq);
+ return -EINVAL;
+ }
+
+ spe_pmu->irq = irq;
+ return 0;
+}
+
+static const struct of_device_id arm_spe_pmu_of_match[] = {
+ { .compatible = "arm,arm-spe-pmu-v1", .data = (void *)1 },
+};
+
+static int arm_spe_pmu_device_dt_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct arm_spe_pmu *spe_pmu;
+ struct device *dev = &pdev->dev;
+
+ spe_pmu = devm_kzalloc(dev, sizeof(*spe_pmu), GFP_KERNEL);
+ if (!spe_pmu) {
+ dev_err(dev, "failed to allocate spe_pmu\n");
+ return -ENOMEM;
+ }
+
+ spe_pmu->handle = alloc_percpu(typeof(*spe_pmu->handle));
+ if (!spe_pmu->handle)
+ return -ENOMEM;
+
+ spe_pmu->pdev = pdev;
+ platform_set_drvdata(pdev, spe_pmu);
+
+ ret = arm_spe_pmu_irq_probe(spe_pmu);
+ if (ret)
+ goto out_free_handle;
+
+ ret = arm_spe_pmu_dev_init(spe_pmu);
+ if (ret)
+ goto out_free_handle;
+
+ ret = arm_spe_pmu_perf_init(spe_pmu);
+ if (ret)
+ goto out_free_handle;
+
+ return 0;
+
+out_free_handle:
+ free_percpu(spe_pmu->handle);
+ return ret;
+}
+
+static int arm_spe_pmu_device_remove(struct platform_device *pdev)
+{
+ struct arm_spe_pmu *spe_pmu = platform_get_drvdata(pdev);
+ cpumask_t *mask = &spe_pmu->supported_cpus;
+
+ arm_spe_pmu_perf_destroy(spe_pmu);
+
+ get_online_cpus();
+ cpuhp_state_remove_instance_nocalls(arm_spe_pmu_online,
+ &spe_pmu->hotplug_node);
+ on_each_cpu_mask(mask, __arm_spe_pmu_stop_one, spe_pmu, 1);
+ free_percpu_irq(spe_pmu->irq, spe_pmu->handle);
+ free_percpu(spe_pmu->handle);
+ put_online_cpus();
+
+ return 0;
+}
+
+static struct platform_driver arm_spe_pmu_driver = {
+ .driver = {
+ .name = DRVNAME,
+ .of_match_table = of_match_ptr(arm_spe_pmu_of_match),
+ },
+ .probe = arm_spe_pmu_device_dt_probe,
+ .remove = arm_spe_pmu_device_remove,
+};
+
+static int __init arm_spe_pmu_init(void)
+{
+ int ret;
+
+ ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, DRVNAME,
+ arm_spe_pmu_cpu_startup,
+ arm_spe_pmu_cpu_teardown);
+ if (ret < 0)
+ return ret;
+ arm_spe_pmu_online = ret;
+
+ ret = platform_driver_register(&arm_spe_pmu_driver);
+ if (ret)
+ cpuhp_remove_multi_state(arm_spe_pmu_online);
+
+ return ret;
+}
+
+static void __exit arm_spe_pmu_exit(void)
+{
+ platform_driver_unregister(&arm_spe_pmu_driver);
+ cpuhp_remove_multi_state(arm_spe_pmu_online);
+}
+
+module_init(arm_spe_pmu_init);
+module_exit(arm_spe_pmu_exit);
+
+MODULE_DESCRIPTION("Perf driver for the ARMv8.2 Statistical Profiling Extension");
+MODULE_AUTHOR("Will Deacon <will.deacon@arm.com>");
+MODULE_LICENSE("GPL v2");
--
2.1.4
^ permalink raw reply related
* [RFC PATCH 08/10] perf/core: Add PERF_AUX_FLAG_COLLISION to report colliding samples
From: Will Deacon @ 2017-01-03 18:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483467027-14547-1-git-send-email-will.deacon@arm.com>
The ARM SPE architecture permits an implementation to ignore a sample
if the sample is due to be taken whilst another sample is already being
produced. In this case, it is desirable to report the collision to
userspace, as they may want to lower the sample period.
This patch adds a PERF_AUX_FLAG_COLLISION flag, so that such events can
be relayed to userspace.
Signed-off-by: Will Deacon <will.deacon@arm.com>
---
include/uapi/linux/perf_event.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h
index c66a485a24ac..68a4e542968e 100644
--- a/include/uapi/linux/perf_event.h
+++ b/include/uapi/linux/perf_event.h
@@ -885,6 +885,7 @@ enum perf_callchain_context {
*/
#define PERF_AUX_FLAG_TRUNCATED 0x01 /* record was truncated to fit */
#define PERF_AUX_FLAG_OVERWRITE 0x02 /* snapshot from overwrite mode */
+#define PERF_AUX_FLAG_COLLISION 0x03 /* sample collided with another */
#define PERF_FLAG_FD_NO_GROUP (1UL << 0)
#define PERF_FLAG_FD_OUTPUT (1UL << 1)
--
2.1.4
^ permalink raw reply related
* [RFC PATCH 07/10] perf: Directly pass PERF_AUX_* flags to perf_aux_output_end
From: Will Deacon @ 2017-01-03 18:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483467027-14547-1-git-send-email-will.deacon@arm.com>
In preparation for adding additional flags to perf AUX records, allow
the flags for a session to be passed directly to perf_aux_output_end,
rather than extend the function to take a bool for each one.
Signed-off-by: Will Deacon <will.deacon@arm.com>
---
arch/x86/events/intel/bts.c | 11 ++++++-----
arch/x86/events/intel/pt.c | 11 +++++++----
drivers/hwtracing/coresight/coresight-etm-perf.c | 5 +++--
include/linux/perf_event.h | 4 ++--
kernel/events/ring_buffer.c | 12 +++++-------
5 files changed, 23 insertions(+), 20 deletions(-)
diff --git a/arch/x86/events/intel/bts.c b/arch/x86/events/intel/bts.c
index 982c9e31daca..2aa63190f01e 100644
--- a/arch/x86/events/intel/bts.c
+++ b/arch/x86/events/intel/bts.c
@@ -276,7 +276,7 @@ static void bts_event_start(struct perf_event *event, int flags)
return;
fail_end_stop:
- perf_aux_output_end(&bts->handle, 0, false);
+ perf_aux_output_end(&bts->handle, 0, 0);
fail_stop:
event->hw.state = PERF_HES_STOPPED;
@@ -319,9 +319,9 @@ static void bts_event_stop(struct perf_event *event, int flags)
bts->handle.head =
local_xchg(&buf->data_size,
buf->nr_pages << PAGE_SHIFT);
-
perf_aux_output_end(&bts->handle, local_xchg(&buf->data_size, 0),
- !!local_xchg(&buf->lost, 0));
+ local_xchg(&buf->lost, 0) ?
+ PERF_AUX_FLAG_TRUNCATED : 0);
}
cpuc->ds->bts_index = bts->ds_back.bts_buffer_base;
@@ -485,7 +485,8 @@ int intel_bts_interrupt(void)
return handled;
perf_aux_output_end(&bts->handle, local_xchg(&buf->data_size, 0),
- !!local_xchg(&buf->lost, 0));
+ local_xchg(&buf->lost, 0) ?
+ PERF_AUX_FLAG_OVERWRITE : 0);
buf = perf_aux_output_begin(&bts->handle, event);
if (buf)
@@ -500,7 +501,7 @@ int intel_bts_interrupt(void)
* cleared handle::event
*/
barrier();
- perf_aux_output_end(&bts->handle, 0, false);
+ perf_aux_output_end(&bts->handle, 0, 0);
}
}
diff --git a/arch/x86/events/intel/pt.c b/arch/x86/events/intel/pt.c
index 1c1b9fe705c8..e229f675114d 100644
--- a/arch/x86/events/intel/pt.c
+++ b/arch/x86/events/intel/pt.c
@@ -1187,7 +1187,8 @@ void intel_pt_interrupt(void)
pt_update_head(pt);
perf_aux_output_end(&pt->handle, local_xchg(&buf->data_size, 0),
- local_xchg(&buf->lost, 0));
+ local_xchg(&buf->lost, 0) ?
+ PERF_AUX_FLAG_TRUNCATED : 0);
if (!event->hw.state) {
int ret;
@@ -1202,7 +1203,8 @@ void intel_pt_interrupt(void)
/* snapshot counters don't use PMI, so it's safe */
ret = pt_buffer_reset_markers(buf, &pt->handle);
if (ret) {
- perf_aux_output_end(&pt->handle, 0, true);
+ perf_aux_output_end(&pt->handle, 0,
+ PERF_AUX_FLAG_TRUNCATED);
return;
}
@@ -1274,7 +1276,7 @@ static void pt_event_start(struct perf_event *event, int mode)
return;
fail_end_stop:
- perf_aux_output_end(&pt->handle, 0, true);
+ perf_aux_output_end(&pt->handle, 0, PERF_AUX_FLAG_TRUNCATED);
fail_stop:
hwc->state = PERF_HES_STOPPED;
}
@@ -1316,7 +1318,8 @@ static void pt_event_stop(struct perf_event *event, int mode)
local_xchg(&buf->data_size,
buf->nr_pages << PAGE_SHIFT);
perf_aux_output_end(&pt->handle, local_xchg(&buf->data_size, 0),
- local_xchg(&buf->lost, 0));
+ local_xchg(&buf->lost, 0) ?
+ PERF_AUX_FLAG_TRUNCATED : 0);
}
}
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c
index 17741969026e..4a425b2f62ee 100644
--- a/drivers/hwtracing/coresight/coresight-etm-perf.c
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
@@ -301,7 +301,7 @@ static void etm_event_start(struct perf_event *event, int flags)
return;
fail_end_stop:
- perf_aux_output_end(handle, 0, true);
+ perf_aux_output_end(handle, 0, PERF_AUX_FLAG_TRUNCATED);
fail:
event->hw.state = PERF_HES_STOPPED;
goto out;
@@ -350,7 +350,8 @@ static void etm_event_stop(struct perf_event *event, int mode)
event_data->snk_config,
&lost);
- perf_aux_output_end(handle, size, lost);
+ perf_aux_output_end(handle, size,
+ lost ? PERF_AUX_FLAG_TRUNCATED : 0);
}
/* Disabling the path make its elements available to other sessions */
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 4741ecdb9817..473e052e6208 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -847,7 +847,7 @@ perf_cgroup_from_task(struct task_struct *task, struct perf_event_context *ctx)
extern void *perf_aux_output_begin(struct perf_output_handle *handle,
struct perf_event *event);
extern void perf_aux_output_end(struct perf_output_handle *handle,
- unsigned long size, bool truncated);
+ unsigned long size, u64 flags);
extern int perf_aux_output_skip(struct perf_output_handle *handle,
unsigned long size);
extern void *perf_get_aux(struct perf_output_handle *handle);
@@ -1265,7 +1265,7 @@ perf_aux_output_begin(struct perf_output_handle *handle,
struct perf_event *event) { return NULL; }
static inline void
perf_aux_output_end(struct perf_output_handle *handle, unsigned long size,
- bool truncated) { }
+ u64 flags) { }
static inline int
perf_aux_output_skip(struct perf_output_handle *handle,
unsigned long size) { return -EINVAL; }
diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c
index 13b8a46bd517..ef9326ce1c24 100644
--- a/kernel/events/ring_buffer.c
+++ b/kernel/events/ring_buffer.c
@@ -410,15 +410,11 @@ EXPORT_SYMBOL(perf_aux_output_begin);
* transaction must be stopped and therefore drop the AUX reference count.
*/
void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size,
- bool truncated)
+ u64 flags)
{
struct ring_buffer *rb = handle->rb;
- bool wakeup = truncated;
+ bool wakeup = !!flags;
unsigned long aux_head;
- u64 flags = 0;
-
- if (truncated)
- flags |= PERF_AUX_FLAG_TRUNCATED;
/* in overwrite mode, driver provides aux_head via handle */
if (rb->aux_overwrite) {
@@ -427,6 +423,8 @@ void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size,
aux_head = handle->head;
local_set(&rb->aux_head, aux_head);
} else {
+ flags &= ~PERF_AUX_FLAG_OVERWRITE;
+
aux_head = local_read(&rb->aux_head);
local_add(size, &rb->aux_head);
}
@@ -447,7 +445,7 @@ void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size,
}
if (wakeup) {
- if (truncated)
+ if (flags & PERF_AUX_FLAG_TRUNCATED)
handle->event->pending_disable = 1;
perf_output_wakeup(handle);
}
--
2.1.4
^ permalink raw reply related
* [RFC PATCH 06/10] perf/core: Export AUX buffer helpers to modules
From: Will Deacon @ 2017-01-03 18:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483467027-14547-1-git-send-email-will.deacon@arm.com>
Perf PMU drivers using AUX buffers cannot be built as modules unless
the AUX helpers are exported.
This patch exports perf_aux_output_{begin,end,skip} and perf_get_aux to
modules.
Cc: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Will Deacon <will.deacon@arm.com>
---
kernel/events/ring_buffer.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c
index 257fa460b846..13b8a46bd517 100644
--- a/kernel/events/ring_buffer.c
+++ b/kernel/events/ring_buffer.c
@@ -397,6 +397,7 @@ void *perf_aux_output_begin(struct perf_output_handle *handle,
return NULL;
}
+EXPORT_SYMBOL(perf_aux_output_begin);
/*
* Commit the data written by hardware into the ring buffer by adjusting
@@ -458,6 +459,7 @@ void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size,
rb_free_aux(rb);
ring_buffer_put(rb);
}
+EXPORT_SYMBOL(perf_aux_output_end);
/*
* Skip over a given number of bytes in the AUX buffer, due to, for example,
@@ -486,6 +488,7 @@ int perf_aux_output_skip(struct perf_output_handle *handle, unsigned long size)
return 0;
}
+EXPORT_SYMBOL(perf_aux_output_skip);
void *perf_get_aux(struct perf_output_handle *handle)
{
@@ -495,6 +498,7 @@ void *perf_get_aux(struct perf_output_handle *handle)
return handle->rb->aux_priv;
}
+EXPORT_SYMBOL(perf_get_aux);
#define PERF_AUX_GFP (GFP_KERNEL | __GFP_ZERO | __GFP_NOWARN | __GFP_NORETRY)
--
2.1.4
^ permalink raw reply related
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