* [PATCH RESEND 1/2] memory: aemif: allow passing device lookup table as platform data
From: Santosh Shilimkar @ 2017-01-13 16:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484301626-30345-2-git-send-email-bgolaszewski@baylibre.com>
Hi Greg,
On 1/13/2017 2:00 AM, Bartosz Golaszewski wrote:
> TI aemif driver creates its own subnodes of the device tree in order
> to guarantee that all child devices are probed after the AEMIF timing
> parameters are configured.
>
> Some devices (e.g. da850) use struct of_dev_auxdata for clock lookup
> but nodes created from within the aemif driver can't access the lookup
> table.
>
> Create a platform data structure that holds a pointer to
> of_dev_auxdata so that we can use it with of_platform_populate().
>
> Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
> Acked-by: Sekhar Nori <nsekhar@ti.com>
> Acked-by: Santosh Shilimkar <santosh.shilimkar@oracle.com>
> ---
Can you please take this one patch via your driver core ?
Regards,
Santosh
^ permalink raw reply
* [PATCH 06/37] PCI: dwc: Rename cfg_read/cfg_write to read/write
From: Joao Pinto @ 2017-01-13 16:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484216786-17292-7-git-send-email-kishon@ti.com>
Hi,
?s 10:25 AM de 1/12/2017, Kishon Vijay Abraham I escreveu:
> No functional change. dw_pcie_cfg_read/dw_pcie_cfg_write doesn't do
> anything specific to access configuration space. It can be just renamed
> to dw_pcie_read/dw_pcie_write and used to read/write data to dbi space.
> This is in preparation for added endpoint support to linux kernel.
>
> Cc: Jingoo Han <jingoohan1@gmail.com>
> Cc: Murali Karicheri <m-karicheri2@ti.com>
> Cc: Joao Pinto <Joao.Pinto@synopsys.com>
> Cc: Stanimir Varbanov <svarbanov@mm-sol.com>
> Cc: Pratyush Anand <pratyush.anand@gmail.com>
> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
> ---
> drivers/pci/dwc/pci-dra7xx.c | 16 ++++++++--------
> drivers/pci/dwc/pci-exynos.c | 4 ++--
> drivers/pci/dwc/pci-keystone-dw.c | 4 ++--
> drivers/pci/dwc/pcie-designware.c | 12 ++++++------
> drivers/pci/dwc/pcie-designware.h | 4 ++--
> drivers/pci/dwc/pcie-qcom.c | 2 +-
> drivers/pci/dwc/pcie-spear13xx.c | 24 ++++++++++++------------
> 7 files changed, 33 insertions(+), 33 deletions(-)
>
> diff --git a/drivers/pci/dwc/pci-dra7xx.c b/drivers/pci/dwc/pci-dra7xx.c
> index aeeab74..38b0c9a 100644
> --- a/drivers/pci/dwc/pci-dra7xx.c
> +++ b/drivers/pci/dwc/pci-dra7xx.c
> @@ -114,22 +114,22 @@ static int dra7xx_pcie_establish_link(struct dra7xx_pcie *dra7xx)
> }
>
> if (dra7xx->link_gen == 1) {
> - dw_pcie_cfg_read(pp->dbi_base + exp_cap_off + PCI_EXP_LNKCAP,
> - 4, ®);
> + dw_pcie_read(pp->dbi_base + exp_cap_off + PCI_EXP_LNKCAP,
> + 4, ®);
> if ((reg & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) {
> reg &= ~((u32)PCI_EXP_LNKCAP_SLS);
> reg |= PCI_EXP_LNKCAP_SLS_2_5GB;
> - dw_pcie_cfg_write(pp->dbi_base + exp_cap_off +
> - PCI_EXP_LNKCAP, 4, reg);
> + dw_pcie_write(pp->dbi_base + exp_cap_off +
> + PCI_EXP_LNKCAP, 4, reg);
> }
>
> - dw_pcie_cfg_read(pp->dbi_base + exp_cap_off + PCI_EXP_LNKCTL2,
> - 2, ®);
> + dw_pcie_read(pp->dbi_base + exp_cap_off + PCI_EXP_LNKCTL2,
> + 2, ®);
> if ((reg & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) {
> reg &= ~((u32)PCI_EXP_LNKCAP_SLS);
> reg |= PCI_EXP_LNKCAP_SLS_2_5GB;
> - dw_pcie_cfg_write(pp->dbi_base + exp_cap_off +
> - PCI_EXP_LNKCTL2, 2, reg);
> + dw_pcie_write(pp->dbi_base + exp_cap_off +
> + PCI_EXP_LNKCTL2, 2, reg);
> }
> }
>
> diff --git a/drivers/pci/dwc/pci-exynos.c b/drivers/pci/dwc/pci-exynos.c
> index c179e7a..e3fbff4 100644
> --- a/drivers/pci/dwc/pci-exynos.c
> +++ b/drivers/pci/dwc/pci-exynos.c
> @@ -429,7 +429,7 @@ static int exynos_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
> int ret;
>
> exynos_pcie_sideband_dbi_r_mode(exynos_pcie, true);
> - ret = dw_pcie_cfg_read(pp->dbi_base + where, size, val);
> + ret = dw_pcie_read(pp->dbi_base + where, size, val);
> exynos_pcie_sideband_dbi_r_mode(exynos_pcie, false);
> return ret;
> }
> @@ -441,7 +441,7 @@ static int exynos_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
> int ret;
>
> exynos_pcie_sideband_dbi_w_mode(exynos_pcie, true);
> - ret = dw_pcie_cfg_write(pp->dbi_base + where, size, val);
> + ret = dw_pcie_write(pp->dbi_base + where, size, val);
> exynos_pcie_sideband_dbi_w_mode(exynos_pcie, false);
> return ret;
> }
> diff --git a/drivers/pci/dwc/pci-keystone-dw.c b/drivers/pci/dwc/pci-keystone-dw.c
> index 9397c46..4875334 100644
> --- a/drivers/pci/dwc/pci-keystone-dw.c
> +++ b/drivers/pci/dwc/pci-keystone-dw.c
> @@ -444,7 +444,7 @@ int ks_dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
>
> addr = ks_pcie_cfg_setup(ks_pcie, bus_num, devfn);
>
> - return dw_pcie_cfg_read(addr + where, size, val);
> + return dw_pcie_read(addr + where, size, val);
> }
>
> int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
> @@ -456,7 +456,7 @@ int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
>
> addr = ks_pcie_cfg_setup(ks_pcie, bus_num, devfn);
>
> - return dw_pcie_cfg_write(addr + where, size, val);
> + return dw_pcie_write(addr + where, size, val);
> }
>
> /**
> diff --git a/drivers/pci/dwc/pcie-designware.c b/drivers/pci/dwc/pcie-designware.c
> index 0b928dc..d0ea310 100644
> --- a/drivers/pci/dwc/pcie-designware.c
> +++ b/drivers/pci/dwc/pcie-designware.c
> @@ -33,7 +33,7 @@
>
> static struct pci_ops dw_pcie_ops;
>
> -int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val)
> +int dw_pcie_read(void __iomem *addr, int size, u32 *val)
> {
> if ((uintptr_t)addr & (size - 1)) {
> *val = 0;
> @@ -54,7 +54,7 @@ int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val)
> return PCIBIOS_SUCCESSFUL;
> }
>
> -int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val)
> +int dw_pcie_write(void __iomem *addr, int size, u32 val)
> {
> if ((uintptr_t)addr & (size - 1))
> return PCIBIOS_BAD_REGISTER_NUMBER;
> @@ -108,7 +108,7 @@ static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
> if (pp->ops->rd_own_conf)
> return pp->ops->rd_own_conf(pp, where, size, val);
>
> - return dw_pcie_cfg_read(pp->dbi_base + where, size, val);
> + return dw_pcie_read(pp->dbi_base + where, size, val);
> }
>
> static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
> @@ -117,7 +117,7 @@ static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
> if (pp->ops->wr_own_conf)
> return pp->ops->wr_own_conf(pp, where, size, val);
>
> - return dw_pcie_cfg_write(pp->dbi_base + where, size, val);
> + return dw_pcie_write(pp->dbi_base + where, size, val);
> }
>
> static void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index,
> @@ -638,7 +638,7 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
> dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1,
> type, cpu_addr,
> busdev, cfg_size);
> - ret = dw_pcie_cfg_read(va_cfg_base + where, size, val);
> + ret = dw_pcie_read(va_cfg_base + where, size, val);
> if (pp->num_viewport <= 2)
> dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1,
> PCIE_ATU_TYPE_IO, pp->io_base,
> @@ -676,7 +676,7 @@ static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
> dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1,
> type, cpu_addr,
> busdev, cfg_size);
> - ret = dw_pcie_cfg_write(va_cfg_base + where, size, val);
> + ret = dw_pcie_write(va_cfg_base + where, size, val);
> if (pp->num_viewport <= 2)
> dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1,
> PCIE_ATU_TYPE_IO, pp->io_base,
> diff --git a/drivers/pci/dwc/pcie-designware.h b/drivers/pci/dwc/pcie-designware.h
> index a6cf9262..b6ddb05 100644
> --- a/drivers/pci/dwc/pcie-designware.h
> +++ b/drivers/pci/dwc/pcie-designware.h
> @@ -146,8 +146,8 @@ struct pcie_host_ops {
>
> u32 dw_pcie_readl_rc(struct pcie_port *pp, u32 reg);
> void dw_pcie_writel_rc(struct pcie_port *pp, u32 reg, u32 val);
> -int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val);
> -int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val);
> +int dw_pcie_read(void __iomem *addr, int size, u32 *val);
> +int dw_pcie_write(void __iomem *addr, int size, u32 val);
> irqreturn_t dw_handle_msi_irq(struct pcie_port *pp);
> void dw_pcie_msi_init(struct pcie_port *pp);
> int dw_pcie_wait_for_link(struct pcie_port *pp);
> diff --git a/drivers/pci/dwc/pcie-qcom.c b/drivers/pci/dwc/pcie-qcom.c
> index 2c24c45..d75fc02 100644
> --- a/drivers/pci/dwc/pcie-qcom.c
> +++ b/drivers/pci/dwc/pcie-qcom.c
> @@ -630,7 +630,7 @@ static int qcom_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
> return PCIBIOS_SUCCESSFUL;
> }
>
> - return dw_pcie_cfg_read(pp->dbi_base + where, size, val);
> + return dw_pcie_read(pp->dbi_base + where, size, val);
> }
>
> static struct pcie_host_ops qcom_pcie_dw_ops = {
> diff --git a/drivers/pci/dwc/pcie-spear13xx.c b/drivers/pci/dwc/pcie-spear13xx.c
> index 5970566..7acf91e 100644
> --- a/drivers/pci/dwc/pcie-spear13xx.c
> +++ b/drivers/pci/dwc/pcie-spear13xx.c
> @@ -91,34 +91,34 @@ static int spear13xx_pcie_establish_link(struct spear13xx_pcie *spear13xx_pcie)
> * default value in capability register is 512 bytes. So force
> * it to 128 here.
> */
> - dw_pcie_cfg_read(pp->dbi_base + exp_cap_off + PCI_EXP_DEVCTL, 2, &val);
> + dw_pcie_read(pp->dbi_base + exp_cap_off + PCI_EXP_DEVCTL, 2, &val);
> val &= ~PCI_EXP_DEVCTL_READRQ;
> - dw_pcie_cfg_write(pp->dbi_base + exp_cap_off + PCI_EXP_DEVCTL, 2, val);
> + dw_pcie_write(pp->dbi_base + exp_cap_off + PCI_EXP_DEVCTL, 2, val);
>
> - dw_pcie_cfg_write(pp->dbi_base + PCI_VENDOR_ID, 2, 0x104A);
> - dw_pcie_cfg_write(pp->dbi_base + PCI_DEVICE_ID, 2, 0xCD80);
> + dw_pcie_write(pp->dbi_base + PCI_VENDOR_ID, 2, 0x104A);
> + dw_pcie_write(pp->dbi_base + PCI_DEVICE_ID, 2, 0xCD80);
>
> /*
> * if is_gen1 is set then handle it, so that some buggy card
> * also works
> */
> if (spear13xx_pcie->is_gen1) {
> - dw_pcie_cfg_read(pp->dbi_base + exp_cap_off + PCI_EXP_LNKCAP,
> - 4, &val);
> + dw_pcie_read(pp->dbi_base + exp_cap_off + PCI_EXP_LNKCAP,
> + 4, &val);
> if ((val & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) {
> val &= ~((u32)PCI_EXP_LNKCAP_SLS);
> val |= PCI_EXP_LNKCAP_SLS_2_5GB;
> - dw_pcie_cfg_write(pp->dbi_base + exp_cap_off +
> - PCI_EXP_LNKCAP, 4, val);
> + dw_pcie_write(pp->dbi_base + exp_cap_off +
> + PCI_EXP_LNKCAP, 4, val);
> }
>
> - dw_pcie_cfg_read(pp->dbi_base + exp_cap_off + PCI_EXP_LNKCTL2,
> - 2, &val);
> + dw_pcie_read(pp->dbi_base + exp_cap_off + PCI_EXP_LNKCTL2,
> + 2, &val);
> if ((val & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) {
> val &= ~((u32)PCI_EXP_LNKCAP_SLS);
> val |= PCI_EXP_LNKCAP_SLS_2_5GB;
> - dw_pcie_cfg_write(pp->dbi_base + exp_cap_off +
> - PCI_EXP_LNKCTL2, 2, val);
> + dw_pcie_write(pp->dbi_base + exp_cap_off +
> + PCI_EXP_LNKCTL2, 2, val);
> }
> }
>
>
Make sense.
Reviewed-By: Joao Pinto <jpinto@synopsys.com>
^ permalink raw reply
* [PATCH 04/37] PCI: dwc: designware: Move the register defines to designware header file
From: Joao Pinto @ 2017-01-13 16:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484216786-17292-5-git-send-email-kishon@ti.com>
?s 10:25 AM de 1/12/2017, Kishon Vijay Abraham I escreveu:
> No functional change. Move the register defines and other macros from
> pcie-designware.c to pcie-designware.h. This is in preparation to
> split the pcie-designware.c file into designware core file and host
> specific file.
>
> While at that also fix a checkpatch warning.
>
> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
> ---
> drivers/pci/dwc/pcie-designware.c | 70 ------------------------------------
> drivers/pci/dwc/pcie-designware.h | 71 +++++++++++++++++++++++++++++++++++++
> 2 files changed, 71 insertions(+), 70 deletions(-)
>
> diff --git a/drivers/pci/dwc/pcie-designware.c b/drivers/pci/dwc/pcie-designware.c
> index d68bc7b..0b928dc 100644
> --- a/drivers/pci/dwc/pcie-designware.c
> +++ b/drivers/pci/dwc/pcie-designware.c
> @@ -25,76 +25,6 @@
>
> #include "pcie-designware.h"
Make sense.
Reviewed-By: Joao Pinto <jpinto@synopsys.com>
^ permalink raw reply
* [PATCH 02/37] PCI: dwc: designware: Add new *ops* for cpu addr fixup
From: Joao Pinto @ 2017-01-13 16:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484216786-17292-3-git-send-email-kishon@ti.com>
Hi Kishon,
?s 10:25 AM de 1/12/2017, Kishon Vijay Abraham I escreveu:
> Some platforms (like dra7xx) require only the least 28 bits of the
> corresponding 32 bit CPU address to be programmed in the address
> translation unit. This modified address is stored in io_base/mem_base/
> cfg0_base/cfg1_base in dra7xx_pcie_host_init. While this is okay for
> host mode where the address range is fixed, device mode requires
> different addresses to be programmed based on the host buffer address.
> Add a new ops to get the least 28 bits of the corresponding 32 bit
> CPU address and invoke it before programming the address translation
> unit.
>
> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
> ---
> drivers/pci/dwc/pcie-designware.c | 3 +++
> drivers/pci/dwc/pcie-designware.h | 1 +
> 2 files changed, 4 insertions(+)
>
> diff --git a/drivers/pci/dwc/pcie-designware.c b/drivers/pci/dwc/pcie-designware.c
> index bed1999..d68bc7b 100644
> --- a/drivers/pci/dwc/pcie-designware.c
> +++ b/drivers/pci/dwc/pcie-designware.c
> @@ -195,6 +195,9 @@ static void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index,
> {
> u32 retries, val;
>
> + if (pp->ops->cpu_addr_fixup)
> + cpu_addr = pp->ops->cpu_addr_fixup(cpu_addr);
> +
> if (pp->iatu_unroll_enabled) {
> dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_LOWER_BASE,
> lower_32_bits(cpu_addr));
> diff --git a/drivers/pci/dwc/pcie-designware.h b/drivers/pci/dwc/pcie-designware.h
> index a567ea2..32f4602 100644
> --- a/drivers/pci/dwc/pcie-designware.h
> +++ b/drivers/pci/dwc/pcie-designware.h
> @@ -54,6 +54,7 @@ struct pcie_port {
> };
>
> struct pcie_host_ops {
> + u64 (*cpu_addr_fixup)(u64 cpu_addr);
> u32 (*readl_rc)(struct pcie_port *pp, u32 reg);
> void (*writel_rc)(struct pcie_port *pp, u32 reg, u32 val);
> int (*rd_own_conf)(struct pcie_port *pp, int where, int size, u32 *val);
>
I think this is an acceptable fixup, I am ok with it.
Reviewed-By: Joao Pinto <jpinto@synopsys.com>
Joao
^ permalink raw reply
* [PATCH v1 2/2] arm: dts: mt2701: add nor flash node
From: Boris Brezillon @ 2017-01-13 16:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <b850b751-1d8d-0053-cf1f-d5314542e5c3@gmail.com>
On Fri, 13 Jan 2017 17:13:55 +0100
Marek Vasut <marek.vasut@gmail.com> wrote:
> On 01/13/2017 04:12 PM, Matthias Brugger wrote:
> >
> >
> > On 13/01/17 15:17, Boris Brezillon wrote:
> >> On Fri, 13 Jan 2017 15:13:29 +0800
> >> Guochun Mao <guochun.mao@mediatek.com> wrote:
> >>
> >>> Add Mediatek nor flash node.
> >>>
> >>> Signed-off-by: Guochun Mao <guochun.mao@mediatek.com>
> >>> ---
> >>> arch/arm/boot/dts/mt2701-evb.dts | 25 +++++++++++++++++++++++++
> >>> arch/arm/boot/dts/mt2701.dtsi | 12 ++++++++++++
> >>> 2 files changed, 37 insertions(+)
> >>>
> >>> diff --git a/arch/arm/boot/dts/mt2701-evb.dts
> >>> b/arch/arm/boot/dts/mt2701-evb.dts
> >>> index 082ca88..85e5ae8 100644
> >>> --- a/arch/arm/boot/dts/mt2701-evb.dts
> >>> +++ b/arch/arm/boot/dts/mt2701-evb.dts
> >>> @@ -24,6 +24,31 @@
> >>> };
> >>> };
> >>>
> >>> +&nor_flash {
> >>> + pinctrl-names = "default";
> >>> + pinctrl-0 = <&nor_pins_default>;
> >>> + status = "okay";
> >>> + flash at 0 {
> >>> + compatible = "jedec,spi-nor";
> >>> + reg = <0>;
> >>> + };
> >>> +};
> >>> +
> >>> +&pio {
> >>> + nor_pins_default: nor {
> >>> + pins1 {
> >>> + pinmux = <MT2701_PIN_240_EXT_XCS__FUNC_EXT_XCS>,
> >>> + <MT2701_PIN_241_EXT_SCK__FUNC_EXT_SCK>,
> >>> + <MT2701_PIN_239_EXT_SDIO0__FUNC_EXT_SDIO0>,
> >>> + <MT2701_PIN_238_EXT_SDIO1__FUNC_EXT_SDIO1>,
> >>> + <MT2701_PIN_237_EXT_SDIO2__FUNC_EXT_SDIO2>,
> >>> + <MT2701_PIN_236_EXT_SDIO3__FUNC_EXT_SDIO3>;
> >>> + drive-strength = <MTK_DRIVE_4mA>;
> >>> + bias-pull-up;
> >>> + };
> >>> + };
> >>> +};
> >>> +
> >>> &uart0 {
> >>> status = "okay";
> >>> };
> >>> diff --git a/arch/arm/boot/dts/mt2701.dtsi
> >>> b/arch/arm/boot/dts/mt2701.dtsi
> >>> index bdf8954..1eefce4 100644
> >>> --- a/arch/arm/boot/dts/mt2701.dtsi
> >>> +++ b/arch/arm/boot/dts/mt2701.dtsi
> >>> @@ -227,6 +227,18 @@
> >>> status = "disabled";
> >>> };
> >>>
> >>> + nor_flash: spi at 11014000 {
> >>> + compatible = "mediatek,mt2701-nor",
> >>> + "mediatek,mt8173-nor";
> >>
> >> Why define both here? Is "mediatek,mt8173-nor" really providing a
> >> subset of the features supported by "mediatek,mt2701-nor"?
> >>
> >
> > I think even if the ip block is the same, we should provide both
> > bindings, just in case in the future we find out that mt2701 has some
> > hidden bug, feature or bug-feature. This way even if we update the
> > driver, we stay compatible with older device tree blobs in the wild.
> >
> > We can drop the mt2701-nor in the bindings definition if you want.
Oh, sorry, I misunderstood. What I meant is that if you want to
list/support all possible compatibles, maybe you should just put one
compatible in your DT and patch your driver (+ binding doc) to define
all of them.
>
> This exactly. We should have a DT compat in the form:
> compatible = "vendor,<soc>-block", "vendor,<oldest-compat-soc>-block";
> Then if we find a problem in the future, we can match on the
> "vendor,<soc>-block" and still support the old DTs.
Not sure it's only in term of whose IP appeared first. My understanding
is that it's a way to provide inheritance. For example:
"<soc-vendor>,<ip-version>", "<ip-vendor>,<ip-version>";
or
"<soc-vendor>,<full-featured-ip-version>","<soc-vendor>,<basic-feature-ip-version>";
BTW, which one is the oldest between mt8173 and mt2701? :-)
^ permalink raw reply
* [PATCH v3 1/8] arm: put types.h in uapi
From: Russell King - ARM Linux @ 2017-01-13 16:19 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <4633e475-47f2-5627-81a9-a1747dfddbc0@6wind.com>
On Fri, Jan 13, 2017 at 05:01:01PM +0100, Nicolas Dichtel wrote:
> Please, do not remove the email subject when you reply. I restore it to
> ease the thread follow-up.
I mentioned it to David, and he says it's because the long list of
recipients is breaking his mailer. I've already posed the question
about whether that's exploitable!
> Le 13/01/2017 ? 16:36, David Howells a ?crit :
> > Nicolas Dichtel <nicolas.dichtel@6wind.com> wrote:
> >
> >> This header file is exported, thus move it to uapi.
> >
> > Exported how?
>
> It is listed in include/uapi/asm-generic/Kbuild.asm, which is included by
> arch/arm/include/uapi/asm/Kbuild.
We really should not be installing non-uapi header files to userland
under _any_ circumstance - this to me sounds like a bug in kbuild.
The assumption is that headers outside of uapi directories are not
part of the user visible API, and so can be freely modified - which
in the presence of this bug is untrue.
However, as it's happening, and this header has been there since 2013
(commit 09096f6a0ee2 - "ARM: 7822/1: add workaround for ambiguous C99
stdint.h types") it's now well and truely part of the user API whether
we intended it to be or not, so your patch looks to me like the correct
thing to do.
I think it needs further evaluation to make sure kbuild isn't going to
do something else silly, like subsitute include/asm-generic/types.h for
the now missing arch/arm/include/asm/types.h
I wonder how many more headers are unintentionally exported.
... what a mess. :(
--
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.
^ permalink raw reply
* [PATCH v1 2/2] arm: dts: mt2701: add nor flash node
From: Marek Vasut @ 2017-01-13 16:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <d7b866ad-5b2f-62d3-3aa3-6fa42803d625@gmail.com>
On 01/13/2017 04:12 PM, Matthias Brugger wrote:
>
>
> On 13/01/17 15:17, Boris Brezillon wrote:
>> On Fri, 13 Jan 2017 15:13:29 +0800
>> Guochun Mao <guochun.mao@mediatek.com> wrote:
>>
>>> Add Mediatek nor flash node.
>>>
>>> Signed-off-by: Guochun Mao <guochun.mao@mediatek.com>
>>> ---
>>> arch/arm/boot/dts/mt2701-evb.dts | 25 +++++++++++++++++++++++++
>>> arch/arm/boot/dts/mt2701.dtsi | 12 ++++++++++++
>>> 2 files changed, 37 insertions(+)
>>>
>>> diff --git a/arch/arm/boot/dts/mt2701-evb.dts
>>> b/arch/arm/boot/dts/mt2701-evb.dts
>>> index 082ca88..85e5ae8 100644
>>> --- a/arch/arm/boot/dts/mt2701-evb.dts
>>> +++ b/arch/arm/boot/dts/mt2701-evb.dts
>>> @@ -24,6 +24,31 @@
>>> };
>>> };
>>>
>>> +&nor_flash {
>>> + pinctrl-names = "default";
>>> + pinctrl-0 = <&nor_pins_default>;
>>> + status = "okay";
>>> + flash at 0 {
>>> + compatible = "jedec,spi-nor";
>>> + reg = <0>;
>>> + };
>>> +};
>>> +
>>> +&pio {
>>> + nor_pins_default: nor {
>>> + pins1 {
>>> + pinmux = <MT2701_PIN_240_EXT_XCS__FUNC_EXT_XCS>,
>>> + <MT2701_PIN_241_EXT_SCK__FUNC_EXT_SCK>,
>>> + <MT2701_PIN_239_EXT_SDIO0__FUNC_EXT_SDIO0>,
>>> + <MT2701_PIN_238_EXT_SDIO1__FUNC_EXT_SDIO1>,
>>> + <MT2701_PIN_237_EXT_SDIO2__FUNC_EXT_SDIO2>,
>>> + <MT2701_PIN_236_EXT_SDIO3__FUNC_EXT_SDIO3>;
>>> + drive-strength = <MTK_DRIVE_4mA>;
>>> + bias-pull-up;
>>> + };
>>> + };
>>> +};
>>> +
>>> &uart0 {
>>> status = "okay";
>>> };
>>> diff --git a/arch/arm/boot/dts/mt2701.dtsi
>>> b/arch/arm/boot/dts/mt2701.dtsi
>>> index bdf8954..1eefce4 100644
>>> --- a/arch/arm/boot/dts/mt2701.dtsi
>>> +++ b/arch/arm/boot/dts/mt2701.dtsi
>>> @@ -227,6 +227,18 @@
>>> status = "disabled";
>>> };
>>>
>>> + nor_flash: spi at 11014000 {
>>> + compatible = "mediatek,mt2701-nor",
>>> + "mediatek,mt8173-nor";
>>
>> Why define both here? Is "mediatek,mt8173-nor" really providing a
>> subset of the features supported by "mediatek,mt2701-nor"?
>>
>
> I think even if the ip block is the same, we should provide both
> bindings, just in case in the future we find out that mt2701 has some
> hidden bug, feature or bug-feature. This way even if we update the
> driver, we stay compatible with older device tree blobs in the wild.
>
> We can drop the mt2701-nor in the bindings definition if you want.
This exactly. We should have a DT compat in the form:
compatible = "vendor,<soc>-block", "vendor,<oldest-compat-soc>-block";
Then if we find a problem in the future, we can match on the
"vendor,<soc>-block" and still support the old DTs.
The question is, does the "vendor,<soc>-block" go into the binding
document as well or do we only have "vendor,<oldest-compat-soc>-block"
there ?
--
Best regards,
Marek Vasut
^ permalink raw reply
* [PATCH v3 3/5] arm64: Create and use __tlbi_dsb() macros
From: Will Deacon @ 2017-01-13 16:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <8f8057f5-d33f-15b3-de9f-6e718c23e97d@codeaurora.org>
On Fri, Jan 13, 2017 at 10:12:36AM -0500, Christopher Covington wrote:
> On 01/12/2017 11:58 AM, Will Deacon wrote:
> > On Wed, Jan 11, 2017 at 09:41:16AM -0500, Christopher Covington wrote:
> >> +#define __tlbi_asm_dsb(as, op, attr, ...) do { \
> >> + __TLBI_FOR(op, ##__VA_ARGS__) \
> >> + asm (__TLBI_INSTR(op, ##__VA_ARGS__) \
> >> + __TLBI_IO(op, ##__VA_ARGS__)); \
> >> + asm volatile ( as "\ndsb " #attr "\n" \
> >> + : : : "memory"); } while (0)
> >> +
> >> +#define __tlbi_dsb(...) __tlbi_asm_dsb("", ##__VA_ARGS__)
> >
> > I can't deny that this is cool, but ultimately it's completely unreadable.
> > What I was thinking you'd do would be make __tlbi expand to:
> >
> > tlbi
> > dsb
> > tlbi
> > dsb
> >
> > for Falkor, and:
> >
> > tlbi
> > nop
> > nop
> > nop
> >
> > for everybody else.
>
> Thanks for the suggestion. So would __tlbi take a dsb sharability argument in
> your proposal? Or would it be communicated in some other fashion, maybe inferred
> from the tlbi argument? Or would the workaround dsbs all be the worst/broadest
> case?
I think always using inner-shareable should be ok. If you wanted to optimise
this, you'd want to avoid the workaround altogether for non-shareable
invalidation, but that's fairly rare and I doubt you'd be able to measure
the impact.
> > Wouldn't that localise this change sufficiently that you wouldn't need
> > to change all the callers and encode the looping in your cpp macros?
> >
> > I realise you get an extra dsb in some places with that change, but I'd
> > like to see numbers for the impact of that on top of the workaround. If
> > it's an issue, then an alternative sequence would be:
> >
> > tlbi
> > dsb
> > tlbi
> >
> > and you'd rely on the existing dsb to complete that.
> >
> > Having said that, I don't understand how your current loop code works
> > when the workaround is applied. AFAICT, you end up emitting something
> > like:
> >
> > dsb ishst
> > for i in 0 to n
> > tlbi va+i
> > dsb
> > tlbi va+n
> > dsb
> >
> > which looks wrong to me. Am I misreading something here?
>
> You're right, I am off by 1 << (PAGE_SHIFT - 12) here. I would need to
> increment, compare, not take the loop branch (regular for loop stuff),
> then decrement (missing) and perform TLB invalidation again (present but
> using incorrect value).
It also strikes me as odd that you only need one extra TLBI after the loop
has finished, as opposed to a tlbi; dsb; tlbi loop body (which is what you'd
get if you modified __tlbi as I suggest).
Is it sufficient to have one extra TLBI after the loop and, if so, is the
performance impact of my suggestion therefore unacceptable?
Will
^ permalink raw reply
* [PATCH v3 4/8] x86: stop exporting msr-index.h to userland
From: Nicolas Dichtel @ 2017-01-13 16:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <25483.1484322229@warthog.procyon.org.uk>
Le 13/01/2017 ? 16:43, David Howells a ?crit :
>> -header-y += msr-index.h
>
> I see it on my desktop as /usr/include/asm/msr-index.h and it's been there at
> least four years - and as such it's part of the UAPI. I don't think you can
> remove it unless you can guarantee there are no userspace users.
I keep it in the v2 of the series, but the maintainer, Borislav Petkov, asks me
to un-export it.
I will follow the maintainer decision.
Regards,
Nicolas
^ permalink raw reply
* [RFC PATCH v2 10/10] dt-bindings: Document devicetree binding for ARM SPE
From: Will Deacon @ 2017-01-13 16:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484323429-15231-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 v2 09/10] drivers/perf: Add support for ARMv8.2 Statistical Profiling Extension
From: Will Deacon @ 2017-01-13 16:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484323429-15231-1-git-send-email-will.deacon@arm.com>
The ARMv8.2 architecture introduces the optional Statistical Profiling
Extension (SPE).
SPE can be used to profile a population of operations in the CPU pipeline
after instruction decode. These are either architected instructions (i.e.
a dynamic instruction trace) or CPU-specific uops and the choice is fixed
statically in the hardware and advertised to userspace via caps/. Sampling
is controlled using a sampling interval, similar to a regular PMU counter,
but also with an optional random perturbation to avoid falling into patterns
where you continuously profile the same instruction in a hot loop.
After each operation is decoded, the interval counter is decremented. When
it hits zero, an operation is chosen for profiling and tracked within the
pipeline until it retires. Along the way, information such as TLB lookups,
cache misses, time spent to issue etc is captured in the form of a sample.
The sample is then filtered according to certain criteria (e.g. load
latency) that can be specified in the event config (described under
format/) and, if the sample satisfies the filter, it is written out to
memory as a record, otherwise it is discarded. Only one operation can
be sampled at a time.
The in-memory buffer is linear and virtually addressed, raising an
interrupt when it fills up. The PMU driver handles these interrupts to
give the appearance of a ring buffer, as expected by the AUX code.
The in-memory trace-like format is self-describing (though not parseable
in reverse) and written as a series of records, with each record
corresponding to a sample and consisting of a sequence of packets. These
packets are defined by the architecture, although some have CPU-specific
fields for recording information specific to the microarchitecture.
As a simple example, a record generated for a branch instruction may
consist of the following packets:
0 (Address) : Virtual PC of the branch instruction
1 (Type) : Conditional direct branch
2 (Counter) : Number of cycles taken from Dispatch to Issue
3 (Address) : Virtual branch target + condition flags
4 (Counter) : Number of cycles taken from Dispatch to Complete
5 (Events) : Mispredicted as not-taken
6 (END) : End of record
It is also possible to toggle properties such as timestamp packets in
each record.
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 | 1244 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1253 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..67703efad772
--- /dev/null
+++ b/drivers/perf/arm_spe_pmu.c
@@ -0,0 +1,1244 @@
+/*
+ * 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))
+
+/* 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 v2 08/10] perf/core: Add PERF_AUX_FLAG_COLLISION to report colliding samples
From: Will Deacon @ 2017-01-13 16:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484323429-15231-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 v2 07/10] perf: Directly pass PERF_AUX_* flags to perf_aux_output_end
From: Will Deacon @ 2017-01-13 16:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484323429-15231-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 64158071690c..2c8af2e75953 100644
--- a/kernel/events/ring_buffer.c
+++ b/kernel/events/ring_buffer.c
@@ -410,15 +410,11 @@ EXPORT_SYMBOL_GPL(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 v2 06/10] perf/core: Export AUX buffer helpers to modules
From: Will Deacon @ 2017-01-13 16:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484323429-15231-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..64158071690c 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_GPL(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_GPL(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_GPL(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_GPL(perf_get_aux);
#define PERF_AUX_GFP (GFP_KERNEL | __GFP_ZERO | __GFP_NOWARN | __GFP_NORETRY)
--
2.1.4
^ permalink raw reply related
* [RFC PATCH v2 05/10] genirq: export irq_get_percpu_devid_partition to modules
From: Will Deacon @ 2017-01-13 16:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484323429-15231-1-git-send-email-will.deacon@arm.com>
Any modular driver using cluster-affine PPIs needs to be able to call
irq_get_percpu_devid_partition so that it can enable the IRQ on the
correct subset of CPUs.
This patch exports the symbol so that it can be called from within a
module.
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Will Deacon <will.deacon@arm.com>
---
kernel/irq/irqdesc.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
index 00bb0aeea1d0..1e6ae73eae59 100644
--- a/kernel/irq/irqdesc.c
+++ b/kernel/irq/irqdesc.c
@@ -856,6 +856,7 @@ int irq_get_percpu_devid_partition(unsigned int irq, struct cpumask *affinity)
return 0;
}
+EXPORT_SYMBOL_GPL(irq_get_percpu_devid_partition);
void kstat_incr_irq_this_cpu(unsigned int irq)
{
--
2.1.4
^ permalink raw reply related
* [RFC PATCH v2 04/10] arm64: head.S: Enable EL1 (host) access to SPE when entered at EL2
From: Will Deacon @ 2017-01-13 16:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484323429-15231-1-git-send-email-will.deacon@arm.com>
The SPE architecture requires each exception level to enable access
to the SPE controls for the exception level below it, since additional
context-switch logic may be required to handle the buffer safely.
This patch allows EL1 (host) access to the SPE controls when entered at
EL2.
Cc: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
---
arch/arm64/kernel/head.S | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
index 4b1abac3485a..6a97831dcf3b 100644
--- a/arch/arm64/kernel/head.S
+++ b/arch/arm64/kernel/head.S
@@ -592,8 +592,8 @@ CPU_LE( movk x0, #0x30d0, lsl #16 ) // Clear EE and E0E on LE systems
#endif
/* EL2 debug */
- mrs x0, id_aa64dfr0_el1 // Check ID_AA64DFR0_EL1 PMUVer
- sbfx x0, x0, #8, #4
+ mrs x1, id_aa64dfr0_el1 // Check ID_AA64DFR0_EL1 PMUVer
+ sbfx x0, x1, #8, #4
cmp x0, #1
b.lt 4f // Skip if no PMU present
mrs x0, pmcr_el0 // Disable debug access traps
@@ -601,6 +601,16 @@ CPU_LE( movk x0, #0x30d0, lsl #16 ) // Clear EE and E0E on LE systems
4:
csel x0, xzr, x0, lt // all PMU counters from EL1
msr mdcr_el2, x0 // (if they exist)
+ /* Statistical profiling */
+ ubfx x0, x1, #32, #4 // Check ID_AA64DFR0_EL1 PMSVer
+ cbz x0, 5f // Skip if SPE not present
+ mrs x0, mdcr_el2 // Preserve HPMN field
+ cmp x2, xzr // If VHE is not enabled,
+ mov x1, #3 // use EL1&0 translations,
+ cinc x1, x1, ne // otherwise use EL2 and
+ bfi x0, x1, #12, #3 // enable/disable access
+ msr mdcr_el2, x0 // traps accordingly.
+5:
/* Stage-2 translation */
msr vttbr_el2, xzr
--
2.1.4
^ permalink raw reply related
* [RFC PATCH v2 03/10] arm64: KVM: Save/restore the host SPE state when entering/leaving a VM
From: Will Deacon @ 2017-01-13 16:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484323429-15231-1-git-send-email-will.deacon@arm.com>
The SPE buffer is virtually addressed, using the page tables of the CPU
MMU. Unusually, this means that the EL0/1 page table may be live whilst
we're executing at EL2 on non-VHE configurations. When VHE is in use,
we can use the same property to profile the guest behind its back.
This patch adds the relevant disabling and flushing code to KVM so that
the host can make use of SPE without corrupting guest memory, and any
attempts by a guest to use SPE will result in a trap.
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Alex Benn?e <alex.bennee@linaro.org>
Cc: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Will Deacon <will.deacon@arm.com>
---
arch/arm64/include/asm/kvm_arm.h | 3 ++
arch/arm64/include/asm/kvm_host.h | 7 ++++-
arch/arm64/kvm/debug.c | 6 ++++
arch/arm64/kvm/hyp/debug-sr.c | 66 +++++++++++++++++++++++++++++++++++++--
arch/arm64/kvm/hyp/switch.c | 13 +++++++-
5 files changed, 91 insertions(+), 4 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h
index 2a2752b5b6aa..6e99978e83bd 100644
--- a/arch/arm64/include/asm/kvm_arm.h
+++ b/arch/arm64/include/asm/kvm_arm.h
@@ -188,6 +188,9 @@
#define CPTR_EL2_DEFAULT 0x000033ff
/* Hyp Debug Configuration Register bits */
+#define MDCR_EL2_TPMS (1 << 14)
+#define MDCR_EL2_E2PB_MASK (UL(0x3))
+#define MDCR_EL2_E2PB_SHIFT (UL(12))
#define MDCR_EL2_TDRA (1 << 11)
#define MDCR_EL2_TDOSA (1 << 10)
#define MDCR_EL2_TDA (1 << 9)
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index e5050388e062..443b387021f2 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -229,7 +229,12 @@ struct kvm_vcpu_arch {
/* Pointer to host CPU context */
kvm_cpu_context_t *host_cpu_context;
- struct kvm_guest_debug_arch host_debug_state;
+ struct {
+ /* {Break,watch}point registers */
+ struct kvm_guest_debug_arch regs;
+ /* Statistical profiling extension */
+ u64 pmscr_el1;
+ } host_debug_state;
/* VGIC state */
struct vgic_cpu vgic_cpu;
diff --git a/arch/arm64/kvm/debug.c b/arch/arm64/kvm/debug.c
index 47e5f0feaee8..dbadfaf850a7 100644
--- a/arch/arm64/kvm/debug.c
+++ b/arch/arm64/kvm/debug.c
@@ -95,6 +95,7 @@ void kvm_arm_reset_debug_ptr(struct kvm_vcpu *vcpu)
* - Performance monitors (MDCR_EL2_TPM/MDCR_EL2_TPMCR)
* - Debug ROM Address (MDCR_EL2_TDRA)
* - OS related registers (MDCR_EL2_TDOSA)
+ * - Statistical profiler (MDCR_EL2_TPMS/MDCR_EL2_E2PB)
*
* Additionally, KVM only traps guest accesses to the debug registers if
* the guest is not actively using them (see the KVM_ARM64_DEBUG_DIRTY
@@ -110,8 +111,13 @@ void kvm_arm_setup_debug(struct kvm_vcpu *vcpu)
trace_kvm_arm_setup_debug(vcpu, vcpu->guest_debug);
+ /*
+ * This also clears MDCR_EL2_E2PB_MASK to disable guest access
+ * to the profiling buffer.
+ */
vcpu->arch.mdcr_el2 = __this_cpu_read(mdcr_el2) & MDCR_EL2_HPMN_MASK;
vcpu->arch.mdcr_el2 |= (MDCR_EL2_TPM |
+ MDCR_EL2_TPMS |
MDCR_EL2_TPMCR |
MDCR_EL2_TDRA |
MDCR_EL2_TDOSA);
diff --git a/arch/arm64/kvm/hyp/debug-sr.c b/arch/arm64/kvm/hyp/debug-sr.c
index 4ba5c9095d03..f5154ed3da6c 100644
--- a/arch/arm64/kvm/hyp/debug-sr.c
+++ b/arch/arm64/kvm/hyp/debug-sr.c
@@ -65,6 +65,66 @@
default: write_debug(ptr[0], reg, 0); \
}
+#define PMSCR_EL1 sys_reg(3, 0, 9, 9, 0)
+
+#define PMBLIMITR_EL1 sys_reg(3, 0, 9, 10, 0)
+#define PMBLIMITR_EL1_E BIT(0)
+
+#define PMBIDR_EL1 sys_reg(3, 0, 9, 10, 7)
+#define PMBIDR_EL1_P BIT(4)
+
+#define psb_csync() asm volatile("hint #17")
+
+static void __hyp_text __debug_save_spe_vhe(u64 *pmscr_el1)
+{
+ /* The vcpu can run. but it can't hide. */
+}
+
+static void __hyp_text __debug_save_spe_nvhe(u64 *pmscr_el1)
+{
+ u64 reg;
+
+ /* SPE present on this CPU? */
+ if (!cpuid_feature_extract_unsigned_field(read_sysreg(id_aa64dfr0_el1),
+ ID_AA64DFR0_PMSVER_SHIFT))
+ return;
+
+ /* Yes; is it owned by EL3? */
+ reg = read_sysreg_s(PMBIDR_EL1);
+ if (reg & PMBIDR_EL1_P)
+ return;
+
+ /* No; is the host actually using the thing? */
+ reg = read_sysreg_s(PMBLIMITR_EL1);
+ if (!(reg & PMBLIMITR_EL1_E))
+ return;
+
+ /* Yes; save the control register and disable data generation */
+ *pmscr_el1 = read_sysreg_s(PMSCR_EL1);
+ write_sysreg_s(0, PMSCR_EL1);
+ isb();
+
+ /* Now drain all buffered data to memory */
+ psb_csync();
+ dsb(nsh);
+}
+
+static hyp_alternate_select(__debug_save_spe,
+ __debug_save_spe_nvhe, __debug_save_spe_vhe,
+ ARM64_HAS_VIRT_HOST_EXTN);
+
+static void __hyp_text __debug_restore_spe(u64 pmscr_el1)
+{
+ if (!pmscr_el1)
+ return;
+
+ /* The host page table is installed, but not yet synchronised */
+ isb();
+
+ /* Re-enable data generation */
+ write_sysreg_s(pmscr_el1, PMSCR_EL1);
+}
+
void __hyp_text __debug_save_state(struct kvm_vcpu *vcpu,
struct kvm_guest_debug_arch *dbg,
struct kvm_cpu_context *ctxt)
@@ -118,13 +178,15 @@ void __hyp_text __debug_cond_save_host_state(struct kvm_vcpu *vcpu)
(vcpu->arch.ctxt.sys_regs[MDSCR_EL1] & DBG_MDSCR_MDE))
vcpu->arch.debug_flags |= KVM_ARM64_DEBUG_DIRTY;
- __debug_save_state(vcpu, &vcpu->arch.host_debug_state,
+ __debug_save_state(vcpu, &vcpu->arch.host_debug_state.regs,
kern_hyp_va(vcpu->arch.host_cpu_context));
+ __debug_save_spe()(&vcpu->arch.host_debug_state.pmscr_el1);
}
void __hyp_text __debug_cond_restore_host_state(struct kvm_vcpu *vcpu)
{
- __debug_restore_state(vcpu, &vcpu->arch.host_debug_state,
+ __debug_restore_spe(vcpu->arch.host_debug_state.pmscr_el1);
+ __debug_restore_state(vcpu, &vcpu->arch.host_debug_state.regs,
kern_hyp_va(vcpu->arch.host_cpu_context));
if (vcpu->arch.debug_flags & KVM_ARM64_DEBUG_DIRTY)
diff --git a/arch/arm64/kvm/hyp/switch.c b/arch/arm64/kvm/hyp/switch.c
index 75e83dd40d43..1ee5b06d81e5 100644
--- a/arch/arm64/kvm/hyp/switch.c
+++ b/arch/arm64/kvm/hyp/switch.c
@@ -103,7 +103,13 @@ static void __hyp_text __activate_traps(struct kvm_vcpu *vcpu)
static void __hyp_text __deactivate_traps_vhe(void)
{
extern char vectors[]; /* kernel exception vectors */
+ u64 mdcr_el2 = read_sysreg(mdcr_el2);
+ mdcr_el2 &= MDCR_EL2_HPMN_MASK |
+ MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT |
+ MDCR_EL2_TPMS;
+
+ write_sysreg(mdcr_el2, mdcr_el2);
write_sysreg(HCR_HOST_VHE_FLAGS, hcr_el2);
write_sysreg(CPACR_EL1_FPEN, cpacr_el1);
write_sysreg(vectors, vbar_el1);
@@ -111,6 +117,12 @@ static void __hyp_text __deactivate_traps_vhe(void)
static void __hyp_text __deactivate_traps_nvhe(void)
{
+ u64 mdcr_el2 = read_sysreg(mdcr_el2);
+
+ mdcr_el2 &= MDCR_EL2_HPMN_MASK;
+ mdcr_el2 |= MDCR_EL2_E2PB_MASK << MDCR_EL2_E2PB_SHIFT;
+
+ write_sysreg(mdcr_el2, mdcr_el2);
write_sysreg(HCR_RW, hcr_el2);
write_sysreg(CPTR_EL2_DEFAULT, cptr_el2);
}
@@ -132,7 +144,6 @@ static void __hyp_text __deactivate_traps(struct kvm_vcpu *vcpu)
__deactivate_traps_arch()();
write_sysreg(0, hstr_el2);
- write_sysreg(read_sysreg(mdcr_el2) & MDCR_EL2_HPMN_MASK, mdcr_el2);
write_sysreg(0, pmuserenr_el0);
}
--
2.1.4
^ permalink raw reply related
* [RFC PATCH v2 02/10] arm64: cpufeature: Don't enforce system-wide SPE capability
From: Will Deacon @ 2017-01-13 16:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484323429-15231-1-git-send-email-will.deacon@arm.com>
The statistical profiling extension (SPE) is an optional feature of
ARMv8.1 and is unlikely to be supported by all of the CPUs in a
heterogeneous system.
This patch updates the cpufeature checks so that such systems are not
tainted as unsupported.
Acked-by: Mark Rutland <mark.rutland@arm.com>
Reviewed-by: Suzuki Poulose <suzuki.poulose@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
---
arch/arm64/include/asm/sysreg.h | 1 +
arch/arm64/kernel/cpufeature.c | 3 ++-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
index 98ae03f8eedd..e156e7793a65 100644
--- a/arch/arm64/include/asm/sysreg.h
+++ b/arch/arm64/include/asm/sysreg.h
@@ -190,6 +190,7 @@
#define ID_AA64MMFR2_CNP_SHIFT 0
/* id_aa64dfr0 */
+#define ID_AA64DFR0_PMSVER_SHIFT 32
#define ID_AA64DFR0_CTX_CMPS_SHIFT 28
#define ID_AA64DFR0_WRPS_SHIFT 20
#define ID_AA64DFR0_BRPS_SHIFT 12
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 47d0226620e8..c18eb78d3a00 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -180,7 +180,8 @@ static const struct arm64_ftr_bits ftr_id_mmfr0[] = {
};
static const struct arm64_ftr_bits ftr_id_aa64dfr0[] = {
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 32, 32, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 36, 28, 0),
+ ARM64_FTR_BITS(FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64DFR0_PMSVER_SHIFT, 4, 0),
ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_CTX_CMPS_SHIFT, 4, 0),
ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_WRPS_SHIFT, 4, 0),
ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_BRPS_SHIFT, 4, 0),
--
2.1.4
^ permalink raw reply related
* [RFC PATCH v2 01/10] arm64: cpufeature: allow for version discrepancy in PMU implementations
From: Will Deacon @ 2017-01-13 16:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484323429-15231-1-git-send-email-will.deacon@arm.com>
Perf already supports multiple PMU instances for heterogeneous systems,
so there's no need to be strict in the cpufeature checking, particularly
as the PMU extension is optional in the architecture.
Acked-by: Mark Rutland <mark.rutland@arm.com>
Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
---
arch/arm64/kernel/cpufeature.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index fdf8f045929f..47d0226620e8 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -184,7 +184,11 @@ static const struct arm64_ftr_bits ftr_id_aa64dfr0[] = {
ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_CTX_CMPS_SHIFT, 4, 0),
ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_WRPS_SHIFT, 4, 0),
ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_BRPS_SHIFT, 4, 0),
- S_ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
+ /*
+ * We can instantiate multiple PMU instances with different levels
+ * of support.
+ * */
+ S_ARM64_FTR_BITS(FTR_NONSTRICT, FTR_EXACT, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64DFR0_TRACEVER_SHIFT, 4, 0),
ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
ARM64_FTR_END,
--
2.1.4
^ permalink raw reply related
* [RFC PATCH v2 00/10] Add support for the ARMv8.2 Statistical Profiling Extension
From: Will Deacon @ 2017-01-13 16:03 UTC (permalink / raw)
To: linux-arm-kernel
Hello,
This is RFCv2 of the patches originally posted here:
http://lists.infradead.org/pipermail/linux-arm-kernel/2017-January/476450.html
Changes since RFCv1 include:
* Use EXPORT_SYMBOL_GPL instead of EXPORT_SYMBOL
* Remove unused CPP macro
* Added tags for the first couple of patches (which are now queued in arm64)
* Added a brief overview of SPE to the commit log for the perf driver
All feedback welcome,
Will
--->8
Will Deacon (10):
arm64: cpufeature: allow for version discrepancy in PMU
implementations
arm64: cpufeature: Don't enforce system-wide SPE capability
arm64: KVM: Save/restore the host SPE state when entering/leaving a VM
arm64: head.S: Enable EL1 (host) access to SPE when entered at EL2
genirq: export irq_get_percpu_devid_partition to modules
perf/core: Export AUX buffer helpers to modules
perf: Directly pass PERF_AUX_* flags to perf_aux_output_end
perf/core: Add PERF_AUX_FLAG_COLLISION to report colliding samples
drivers/perf: Add support for ARMv8.2 Statistical Profiling Extension
dt-bindings: Document devicetree binding for ARM SPE
Documentation/devicetree/bindings/arm/spe-pmu.txt | 20 +
arch/arm64/include/asm/kvm_arm.h | 3 +
arch/arm64/include/asm/kvm_host.h | 7 +-
arch/arm64/include/asm/sysreg.h | 1 +
arch/arm64/kernel/cpufeature.c | 9 +-
arch/arm64/kernel/head.S | 14 +-
arch/arm64/kvm/debug.c | 6 +
arch/arm64/kvm/hyp/debug-sr.c | 66 +-
arch/arm64/kvm/hyp/switch.c | 13 +-
arch/x86/events/intel/bts.c | 11 +-
arch/x86/events/intel/pt.c | 11 +-
drivers/hwtracing/coresight/coresight-etm-perf.c | 5 +-
drivers/perf/Kconfig | 8 +
drivers/perf/Makefile | 1 +
drivers/perf/arm_spe_pmu.c | 1244 +++++++++++++++++++++
include/linux/perf_event.h | 4 +-
include/uapi/linux/perf_event.h | 1 +
kernel/events/ring_buffer.c | 16 +-
kernel/irq/irqdesc.c | 1 +
19 files changed, 1413 insertions(+), 28 deletions(-)
create mode 100644 Documentation/devicetree/bindings/arm/spe-pmu.txt
create mode 100644 drivers/perf/arm_spe_pmu.c
--
2.1.4
^ permalink raw reply
* [PATCH v3 1/8] arm: put types.h in uapi
From: Nicolas Dichtel @ 2017-01-13 16:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <25063.1484321803@warthog.procyon.org.uk>
Please, do not remove the email subject when you reply. I restore it to ease the
thread follow-up.
Le 13/01/2017 ? 16:36, David Howells a ?crit :
> Nicolas Dichtel <nicolas.dichtel@6wind.com> wrote:
>
>> This header file is exported, thus move it to uapi.
>
> Exported how?
It is listed in include/uapi/asm-generic/Kbuild.asm, which is included by
arch/arm/include/uapi/asm/Kbuild.
You can also have a look at patch #5 to see why it was exported even if it was
not in an uapi directory.
Regards,
Nicolas
^ permalink raw reply
* [PATCH 3/3] rtc: stm32: use 32-bit cast for BIT() macro
From: Amelie DELAUNAY @ 2017-01-13 15:56 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170113153311.2611510-3-arnd@arndb.de>
Hi Arnd,
On 01/13/2017 04:32 PM, Arnd Bergmann wrote:
> Using the ~ operator on a BIT() constant results in a large 'unsigned long'
> constant that won't fit into an 'unsigned int' function argument on 64-bit
> architectures, resulting in a harmless build warning in x86 allmodconfig:
>
> drivers/rtc/rtc-stm32.c: In function 'stm32_rtc_probe':
> drivers/rtc/rtc-stm32.c:651:51: error: large integer implicitly truncated to unsigned type [-Werror=overflow]
> regmap_update_bits(rtc->dbp, PWR_CR, PWR_CR_DBP, ~PWR_CR_DBP);
I thought I would fix this warning by replacing all ~PWR_CR_DBP by 0,
because the mask PWR_CR_DBP prevents other bits to be cleared.
In this way, I avoid the ugly cast...
>
> This works around the warning by adding an explict cast to 'u32', but
> that is unfortunately a bit ugly and I feel there should be a better
> way to do this, possibly with some changes to either the bitops.h
> header or the regmap API.
>
> Cc: Mark Brown <broonie@kernel.org>
> Fixes: 4e64350f42e2 ("rtc: add STM32 RTC driver")
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> ---
> drivers/rtc/rtc-stm32.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/rtc/rtc-stm32.c b/drivers/rtc/rtc-stm32.c
> index 8c599f52124c..05d3dc89e55f 100644
> --- a/drivers/rtc/rtc-stm32.c
> +++ b/drivers/rtc/rtc-stm32.c
> @@ -92,7 +92,7 @@
> /* STM32_PWR_CR */
> #define PWR_CR 0x00
> /* STM32_PWR_CR bit field */
> -#define PWR_CR_DBP BIT(8)
> +#define PWR_CR_DBP (u32)BIT(8)
>
> struct stm32_rtc {
> struct rtc_device *rtc_dev;
>
Regards,
Amelie
^ permalink raw reply
* [PATCH 3/3] rtc: stm32: use 32-bit cast for BIT() macro
From: Alexandre Belloni @ 2017-01-13 15:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170113155229.GS14217@n2100.armlinux.org.uk>
On 13/01/2017 at 15:52:29 +0000, Russell King - ARM Linux wrote :
> On Fri, Jan 13, 2017 at 04:32:53PM +0100, Arnd Bergmann wrote:
> > -#define PWR_CR_DBP BIT(8)
> > +#define PWR_CR_DBP (u32)BIT(8)
>
> Shouldn't that have parens around it as it's no longer a simple expression.
>
Yes, at least checkpatch complains about it.
--
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
^ permalink raw reply
* [PATCH 3/3] rtc: stm32: use 32-bit cast for BIT() macro
From: Russell King - ARM Linux @ 2017-01-13 15:52 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170113153311.2611510-3-arnd@arndb.de>
On Fri, Jan 13, 2017 at 04:32:53PM +0100, Arnd Bergmann wrote:
> -#define PWR_CR_DBP BIT(8)
> +#define PWR_CR_DBP (u32)BIT(8)
Shouldn't that have parens around it as it's no longer a simple expression.
--
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.
^ permalink raw reply
* [PATCH 1/4] video: add HDMI state notifier support
From: Philipp Zabel @ 2017-01-13 15:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161213150813.37966-2-hverkuil@xs4all.nl>
Am Dienstag, den 13.12.2016, 16:08 +0100 schrieb Hans Verkuil:
> From: Hans Verkuil <hans.verkuil@cisco.com>
>
> Add support for HDMI hotplug and EDID notifiers, which is used to convey
> information from HDMI drivers to their CEC and audio counterparts.
>
> Based on an earlier version from Russell King:
>
> https://patchwork.kernel.org/patch/9277043/
>
> The hdmi_notifier is a reference counted object containing the HDMI state
> of an HDMI device.
>
> When a new notifier is registered the current state will be reported to
> that notifier at registration time.
>
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Acked-by: Philipp Zabel <p.zabel@pengutronix.de>
regards
Philipp
> ---
> drivers/video/Kconfig | 3 +
> drivers/video/Makefile | 1 +
> drivers/video/hdmi-notifier.c | 134 ++++++++++++++++++++++++++++++++++++++++++
> include/linux/hdmi-notifier.h | 109 ++++++++++++++++++++++++++++++++++
> 4 files changed, 247 insertions(+)
> create mode 100644 drivers/video/hdmi-notifier.c
> create mode 100644 include/linux/hdmi-notifier.h
>
> diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
> index 3c20af9..1ee7b9f 100644
> --- a/drivers/video/Kconfig
> +++ b/drivers/video/Kconfig
> @@ -36,6 +36,9 @@ config VIDEOMODE_HELPERS
> config HDMI
> bool
>
> +config HDMI_NOTIFIERS
> + bool
> +
> if VT
> source "drivers/video/console/Kconfig"
> endif
> diff --git a/drivers/video/Makefile b/drivers/video/Makefile
> index 9ad3c17..65f5649 100644
> --- a/drivers/video/Makefile
> +++ b/drivers/video/Makefile
> @@ -1,5 +1,6 @@
> obj-$(CONFIG_VGASTATE) += vgastate.o
> obj-$(CONFIG_HDMI) += hdmi.o
> +obj-$(CONFIG_HDMI_NOTIFIERS) += hdmi-notifier.o
>
> obj-$(CONFIG_VT) += console/
> obj-$(CONFIG_LOGO) += logo/
> diff --git a/drivers/video/hdmi-notifier.c b/drivers/video/hdmi-notifier.c
> new file mode 100644
> index 0000000..29e4225
> --- /dev/null
> +++ b/drivers/video/hdmi-notifier.c
> @@ -0,0 +1,134 @@
> +#include <linux/export.h>
> +#include <linux/hdmi-notifier.h>
> +#include <linux/string.h>
> +#include <linux/slab.h>
> +#include <linux/list.h>
> +
> +static LIST_HEAD(hdmi_notifiers);
> +static DEFINE_MUTEX(hdmi_notifiers_lock);
> +
> +struct hdmi_notifier *hdmi_notifier_get(struct device *dev)
> +{
> + struct hdmi_notifier *n;
> +
> + mutex_lock(&hdmi_notifiers_lock);
> + list_for_each_entry(n, &hdmi_notifiers, head) {
> + if (n->dev == dev) {
> + mutex_unlock(&hdmi_notifiers_lock);
> + kref_get(&n->kref);
> + return n;
> + }
> + }
> + n = kzalloc(sizeof(*n), GFP_KERNEL);
> + if (!n)
> + goto unlock;
> + n->dev = dev;
> + mutex_init(&n->lock);
> + BLOCKING_INIT_NOTIFIER_HEAD(&n->notifiers);
> + kref_init(&n->kref);
> + list_add_tail(&n->head, &hdmi_notifiers);
> +unlock:
> + mutex_unlock(&hdmi_notifiers_lock);
> + return n;
> +}
> +EXPORT_SYMBOL_GPL(hdmi_notifier_get);
> +
> +static void hdmi_notifier_release(struct kref *kref)
> +{
> + struct hdmi_notifier *n =
> + container_of(kref, struct hdmi_notifier, kref);
> +
> + mutex_lock(&hdmi_notifiers_lock);
> + list_del(&n->head);
> + mutex_unlock(&hdmi_notifiers_lock);
> + kfree(n->edid);
> + kfree(n);
> +}
> +
> +void hdmi_notifier_put(struct hdmi_notifier *n)
> +{
> + kref_put(&n->kref, hdmi_notifier_release);
> +}
> +EXPORT_SYMBOL_GPL(hdmi_notifier_put);
> +
> +int hdmi_notifier_register(struct hdmi_notifier *n, struct notifier_block *nb)
> +{
> + int ret = blocking_notifier_chain_register(&n->notifiers, nb);
> +
> + if (ret)
> + return ret;
> + kref_get(&n->kref);
> + mutex_lock(&n->lock);
> + if (n->connected) {
> + blocking_notifier_call_chain(&n->notifiers, HDMI_CONNECTED, n);
> + if (n->edid_size)
> + blocking_notifier_call_chain(&n->notifiers, HDMI_NEW_EDID, n);
> + if (n->has_eld)
> + blocking_notifier_call_chain(&n->notifiers, HDMI_NEW_ELD, n);
> + }
> + mutex_unlock(&n->lock);
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(hdmi_notifier_register);
> +
> +int hdmi_notifier_unregister(struct hdmi_notifier *n, struct notifier_block *nb)
> +{
> + int ret = blocking_notifier_chain_unregister(&n->notifiers, nb);
> +
> + if (ret == 0)
> + hdmi_notifier_put(n);
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(hdmi_notifier_unregister);
> +
> +void hdmi_event_connect(struct hdmi_notifier *n)
> +{
> + mutex_lock(&n->lock);
> + n->connected = true;
> + blocking_notifier_call_chain(&n->notifiers, HDMI_CONNECTED, n);
> + mutex_unlock(&n->lock);
> +}
> +EXPORT_SYMBOL_GPL(hdmi_event_connect);
> +
> +void hdmi_event_disconnect(struct hdmi_notifier *n)
> +{
> + mutex_lock(&n->lock);
> + n->connected = false;
> + n->has_eld = false;
> + n->edid_size = 0;
> + blocking_notifier_call_chain(&n->notifiers, HDMI_DISCONNECTED, n);
> + mutex_unlock(&n->lock);
> +}
> +EXPORT_SYMBOL_GPL(hdmi_event_disconnect);
> +
> +int hdmi_event_new_edid(struct hdmi_notifier *n, const void *edid, size_t size)
> +{
> + mutex_lock(&n->lock);
> + if (n->edid_allocated_size < size) {
> + void *p = kmalloc(size, GFP_KERNEL);
> +
> + if (p == NULL) {
> + mutex_unlock(&n->lock);
> + return -ENOMEM;
> + }
> + kfree(n->edid);
> + n->edid = p;
> + n->edid_allocated_size = size;
> + }
> + memcpy(n->edid, edid, size);
> + n->edid_size = size;
> + blocking_notifier_call_chain(&n->notifiers, HDMI_NEW_EDID, n);
> + mutex_unlock(&n->lock);
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(hdmi_event_new_edid);
> +
> +void hdmi_event_new_eld(struct hdmi_notifier *n, const u8 eld[128])
> +{
> + mutex_lock(&n->lock);
> + memcpy(n->eld, eld, sizeof(n->eld));
> + n->has_eld = true;
> + blocking_notifier_call_chain(&n->notifiers, HDMI_NEW_ELD, n);
> + mutex_unlock(&n->lock);
> +}
> +EXPORT_SYMBOL_GPL(hdmi_event_new_eld);
> diff --git a/include/linux/hdmi-notifier.h b/include/linux/hdmi-notifier.h
> new file mode 100644
> index 0000000..1d88db0
> --- /dev/null
> +++ b/include/linux/hdmi-notifier.h
> @@ -0,0 +1,109 @@
> +/*
> + * hdmi-notifier.h - notify interested parties of (dis)connect and EDID
> + * events
> + *
> + * Copyright 2016 Russell King <rmk+kernel@arm.linux.org.uk>
> + * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
> + */
> +
> +#ifndef LINUX_HDMI_NOTIFIER_H
> +#define LINUX_HDMI_NOTIFIER_H
> +
> +#include <linux/types.h>
> +#include <linux/notifier.h>
> +#include <linux/kref.h>
> +
> +enum {
> + HDMI_CONNECTED,
> + HDMI_DISCONNECTED,
> + HDMI_NEW_EDID,
> + HDMI_NEW_ELD,
> +};
> +
> +struct device;
> +
> +struct hdmi_notifier {
> + struct mutex lock;
> + struct list_head head;
> + struct kref kref;
> + struct blocking_notifier_head notifiers;
> + struct device *dev;
> +
> + /* Current state */
> + bool connected;
> + bool has_eld;
> + unsigned char eld[128];
> + void *edid;
> + size_t edid_size;
> + size_t edid_allocated_size;
> +};
> +
> +/**
> + * hdmi_notifier_get - find or create a new hdmi_notifier for the given device.
> + * @dev: device that sends the events.
> + *
> + * If a notifier for device @dev already exists, then increase the refcount
> + * and return that notifier.
> + *
> + * If it doesn't exist, then allocate a new notifier struct and return a
> + * pointer to that new struct.
> + *
> + * Return NULL if the memory could not be allocated.
> + */
> +struct hdmi_notifier *hdmi_notifier_get(struct device *dev);
> +
> +/**
> + * hdmi_notifier_put - decrease refcount and delete when the refcount reaches 0.
> + * @n: notifier
> + */
> +void hdmi_notifier_put(struct hdmi_notifier *n);
> +
> +/**
> + * hdmi_notifier_register - register the notifier with the notifier_block.
> + * @n: the HDMI notifier
> + * @nb: the notifier_block
> + */
> +int hdmi_notifier_register(struct hdmi_notifier *n, struct notifier_block *nb);
> +
> +/**
> + * hdmi_notifier_unregister - unregister the notifier with the notifier_block.
> + * @n: the HDMI notifier
> + * @nb: the notifier_block
> + */
> +int hdmi_notifier_unregister(struct hdmi_notifier *n, struct notifier_block *nb);
> +
> +/**
> + * hdmi_event_connect - send a connect event.
> + * @n: the HDMI notifier
> + *
> + * Send an HDMI_CONNECTED event to any registered parties.
> + */
> +void hdmi_event_connect(struct hdmi_notifier *n);
> +
> +/**
> + * hdmi_event_disconnect - send a disconnect event.
> + * @n: the HDMI notifier
> + *
> + * Send an HDMI_DISCONNECTED event to any registered parties.
> + */
> +void hdmi_event_disconnect(struct hdmi_notifier *n);
> +
> +/**
> + * hdmi_event_new_edid - send a new EDID event.
> + * @n: the HDMI notifier
> + *
> + * Send an HDMI_NEW_EDID event to any registered parties.
> + * This function will make a copy the EDID so it can return -ENOMEM if
> + * no memory could be allocated.
> + */
> +int hdmi_event_new_edid(struct hdmi_notifier *n, const void *edid, size_t size);
> +
> +/**
> + * hdmi_event_new_eld - send a new ELD event.
> + * @n: the HDMI notifier
> + *
> + * Send an HDMI_NEW_ELD event to any registered parties.
> + */
> +void hdmi_event_new_eld(struct hdmi_notifier *n, const u8 eld[128]);
> +
> +#endif
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox