Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] Add DMA support for ti_am335x_adc driver
From: Mugunthan V N @ 2016-09-21 16:11 UTC (permalink / raw)
  To: linux-arm-kernel

The ADC has a 64 work depth fifo length which holds the ADC data
till the CPU reads. So when a user program needs a large ADC data
to operate on, then it has to do multiple reads to get its
buffer. Currently if the application asks for 4 samples per
channel with all 8 channels are enabled, kernel can provide only
3 samples per channel when all 8 channels are enabled (logs at
[1]). So with DMA support user can request for large number of
samples at a time (logs at [2]).

Tested the patch on AM437x-gp-evm and AM335x Boneblack with the
patch [3] to enable ADC and pushed a branch for testing [4]

[1] - http://pastebin.ubuntu.com/23211490/
[2] - http://pastebin.ubuntu.com/23211492/
[3] - http://pastebin.ubuntu.com/23211494/
[4] - git://git.ti.com/~mugunthanvnm/ti-linux-kernel/linux.git iio-dma

Mugunthan V N (4):
  mfd: ti_am335x_tscadc: store physical address
  drivers: iio: ti_am335x_adc: add dma support
  ARM: dts: am33xx: add DMA properties for tscadc
  ARM: dts: am4372: add DMA properties for tscadc

 arch/arm/boot/dts/am33xx.dtsi        |   2 +
 arch/arm/boot/dts/am4372.dtsi        |   2 +
 drivers/iio/adc/ti_am335x_adc.c      | 160 ++++++++++++++++++++++++++++++++++-
 drivers/mfd/ti_am335x_tscadc.c       |   1 +
 include/linux/mfd/ti_am335x_tscadc.h |   8 ++
 5 files changed, 170 insertions(+), 3 deletions(-)

-- 
2.10.0.129.g35f6318

^ permalink raw reply

* [PATCH 2/2] MAINTAINERS: add ARM and arm64 EFI specific files to EFI subsystem
From: Will Deacon @ 2016-09-21 15:44 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1474472114-12973-2-git-send-email-ard.biesheuvel@linaro.org>

On Wed, Sep 21, 2016 at 04:35:14PM +0100, Ard Biesheuvel wrote:
> Since I will be co-maintaining the EFI subsystem, it makes sense to
> mention the ARM and arm64 EFI bits in the EFI section in MAINTAINERS
> so that Matt, the list and I get cc'ed on proposed changes.
> 
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Will Deacon <will.deacon@arm.com>
> Cc: Russell King <rmk+kernel@armlinux.org.uk>
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> ---
>  MAINTAINERS | 6 ++++--
>  1 file changed, 4 insertions(+), 2 deletions(-)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 224518556a84..cc8b36699f94 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -4562,12 +4562,14 @@ L:	linux-efi at vger.kernel.org
>  T:	git git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi.git
>  S:	Maintained
>  F:	Documentation/efi-stub.txt
> -F:	arch/ia64/kernel/efi.c
> +F:	arch/*/kernel/efi.c
>  F:	arch/x86/boot/compressed/eboot.[ch]
> -F:	arch/x86/include/asm/efi.h
> +F:	arch/*/include/asm/efi.h
>  F:	arch/x86/platform/efi/
>  F:	drivers/firmware/efi/
>  F:	include/linux/efi*.h
> +F:	arch/arm/boot/compressed/efi-header.S
> +F:	arch/arm64/kernel/efi-entry.S

For the arm64 bit,

Acked-by: Will Deacon <will.deacon@arm.com>

Thanks,

Will

^ permalink raw reply

* [PATCH 1/2] MAINTAINERS: add myself as EFI maintainer
From: Matt Fleming @ 2016-09-21 15:43 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1474472114-12973-1-git-send-email-ard.biesheuvel@linaro.org>

On Wed, 21 Sep, at 04:35:13PM, Ard Biesheuvel wrote:
> At the request of Matt, I am taking up co-maintainership of the EFI
> subsystem. So add my name to the EFI section in MAINTAINERS, and
> change the SCM tree reference to point to the new shared Git repo.
> 
> Cc: Matt Fleming <matt@codeblueprint.co.uk>
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> ---
>  MAINTAINERS | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)

Acked-by: Matt Fleming <matt@codeblueprint.co.uk>

^ permalink raw reply

* [PATCH 1/2] MAINTAINERS: add myself as EFI maintainer
From: Will Deacon @ 2016-09-21 15:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1474472114-12973-1-git-send-email-ard.biesheuvel@linaro.org>

On Wed, Sep 21, 2016 at 04:35:13PM +0100, Ard Biesheuvel wrote:
> At the request of Matt, I am taking up co-maintainership of the EFI
> subsystem. So add my name to the EFI section in MAINTAINERS, and
> change the SCM tree reference to point to the new shared Git repo.
> 
> Cc: Matt Fleming <matt@codeblueprint.co.uk>
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> ---
>  MAINTAINERS | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)

I don't know if "congratulations" is the right word to use, but:

Acked-by: Will Deacon <will.deacon@arm.com>

Will

^ permalink raw reply

* [PATCH 2/2] MAINTAINERS: add ARM and arm64 EFI specific files to EFI subsystem
From: Ard Biesheuvel @ 2016-09-21 15:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1474472114-12973-1-git-send-email-ard.biesheuvel@linaro.org>

Since I will be co-maintaining the EFI subsystem, it makes sense to
mention the ARM and arm64 EFI bits in the EFI section in MAINTAINERS
so that Matt, the list and I get cc'ed on proposed changes.

Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Russell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
 MAINTAINERS | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 224518556a84..cc8b36699f94 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4562,12 +4562,14 @@ L:	linux-efi at vger.kernel.org
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi.git
 S:	Maintained
 F:	Documentation/efi-stub.txt
-F:	arch/ia64/kernel/efi.c
+F:	arch/*/kernel/efi.c
 F:	arch/x86/boot/compressed/eboot.[ch]
-F:	arch/x86/include/asm/efi.h
+F:	arch/*/include/asm/efi.h
 F:	arch/x86/platform/efi/
 F:	drivers/firmware/efi/
 F:	include/linux/efi*.h
+F:	arch/arm/boot/compressed/efi-header.S
+F:	arch/arm64/kernel/efi-entry.S
 
 EFI VARIABLE FILESYSTEM
 M:	Matthew Garrett <matthew.garrett@nebula.com>
-- 
2.7.4

^ permalink raw reply related

* [PATCH 1/2] MAINTAINERS: add myself as EFI maintainer
From: Ard Biesheuvel @ 2016-09-21 15:35 UTC (permalink / raw)
  To: linux-arm-kernel

At the request of Matt, I am taking up co-maintainership of the EFI
subsystem. So add my name to the EFI section in MAINTAINERS, and
change the SCM tree reference to point to the new shared Git repo.

Cc: Matt Fleming <matt@codeblueprint.co.uk>
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
 MAINTAINERS | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 007d05acbb5f..224518556a84 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4557,8 +4557,9 @@ F:	sound/usb/misc/ua101.c
 
 EXTENSIBLE FIRMWARE INTERFACE (EFI)
 M:	Matt Fleming <matt@codeblueprint.co.uk>
+M:	Ard Biesheuvel <ard.biesheuvel@linaro.org>
 L:	linux-efi at vger.kernel.org
-T:	git git://git.kernel.org/pub/scm/linux/kernel/git/mfleming/efi.git
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi.git
 S:	Maintained
 F:	Documentation/efi-stub.txt
 F:	arch/ia64/kernel/efi.c
-- 
2.7.4

^ permalink raw reply related

* [PATCH -next] soc: mediatek: PMIC wrap: fix missing clk_disable_unprepare() on error in pwrap_probe()
From: Wei Yongjun @ 2016-09-21 15:08 UTC (permalink / raw)
  To: linux-arm-kernel

From: Wei Yongjun <weiyongjun1@huawei.com>

Fix the missing clk_disable_unprepare() before return
from pwrap_probe() in the error handling case.

Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
---
 drivers/soc/mediatek/mtk-pmic-wrap.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c
index a5f1093..4da3bb2 100644
--- a/drivers/soc/mediatek/mtk-pmic-wrap.c
+++ b/drivers/soc/mediatek/mtk-pmic-wrap.c
@@ -1200,7 +1200,8 @@ static int pwrap_probe(struct platform_device *pdev)
 
 	if (!(pwrap_readl(wrp, PWRAP_WACS2_RDATA) & PWRAP_STATE_INIT_DONE0)) {
 		dev_dbg(wrp->dev, "initialization isn't finished\n");
-		return -ENODEV;
+		ret = -ENODEV;
+		goto err_out2;
 	}
 
 	/* Initialize watchdog, may not be done by the bootloader */

^ permalink raw reply related

* [PATCH v5] soc: qcom: add l2 cache perf events driver
From: Neil Leeder @ 2016-09-21 15:05 UTC (permalink / raw)
  To: linux-arm-kernel

Adds perf events support for L2 cache PMU.

The L2 cache PMU driver is named 'l2cache_0' and can be used
with perf events to profile L2 events such as cache hits
and misses.

Signed-off-by: Neil Leeder <nleeder@codeaurora.org>
---
v5:
Fold the header and l2-accessors into .c file
Use multi-instance framework for hotplug
Change terminology from slice to cluster for clarity
Remove unnecessary rmw sequence for enable registers
Use prev_count in hwc rather than in slice
Enforce all events in same group on same CPU
Add comments, rename variables for clarity

v4:
Replace notifier with hotplug statemachine
Allocate PMU struct dynamically

v3:
Remove exports from l2-accessors
Change l2-accessors Kconfig to make it not user-selectable
Reorder and remove unnecessary includes

v2:
Add the l2-accessors patch to this patchset, previously posted separately.
Remove sampling and per-task functionality for this uncore PMU.
Use cpumask to replace code which filtered events to one cpu per slice.
Replace manual event filtering with filter_match callback.
Use a separate used_mask for event groups.
Add hotplug notifier for CPU and irq migration.
Remove extraneous synchronisation instructions.
Other miscellaneous cleanup.

 drivers/soc/qcom/Kconfig         |   9 +
 drivers/soc/qcom/Makefile        |   1 +
 drivers/soc/qcom/perf_event_l2.c | 948 +++++++++++++++++++++++++++++++++++++++
 include/linux/cpuhotplug.h       |   1 +
 4 files changed, 959 insertions(+)
 create mode 100644 drivers/soc/qcom/perf_event_l2.c

diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 461b387..4c32646 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -10,6 +10,15 @@ config QCOM_GSBI
           functions for connecting the underlying serial UART, SPI, and I2C
           devices to the output pins.
 
+config QCOM_PERF_EVENTS_L2
+	bool "Qualcomm Technologies L2-cache perf events"
+	depends on ARCH_QCOM && HW_PERF_EVENTS
+	  help
+	  Provides support for the L2 cache performance monitor unit (PMU)
+	  in Qualcomm Technologies processors.
+	  Adds the L2 cache PMU into the perf events subsystem for
+	  monitoring L2 cache events.
+
 config QCOM_PM
 	bool "Qualcomm Power Management"
 	depends on ARCH_QCOM && !ARM64
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index fdd664e..4c9df3b 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -1,4 +1,5 @@
 obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
+obj-$(CONFIG_QCOM_PERF_EVENTS_L2)	+= perf_event_l2.o
 obj-$(CONFIG_QCOM_PM)	+=	spm.o
 obj-$(CONFIG_QCOM_SMD) +=	smd.o
 obj-$(CONFIG_QCOM_SMD_RPM)	+= smd-rpm.o
diff --git a/drivers/soc/qcom/perf_event_l2.c b/drivers/soc/qcom/perf_event_l2.c
new file mode 100644
index 0000000..bbf47c9
--- /dev/null
+++ b/drivers/soc/qcom/perf_event_l2.c
@@ -0,0 +1,948 @@
+/* Copyright (c) 2015,2016 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/acpi.h>
+#include <linux/interrupt.h>
+#include <linux/perf_event.h>
+#include <linux/platform_device.h>
+
+#define MAX_L2_CTRS             9
+
+#define L2PMCR_NUM_EV_SHIFT     11
+#define L2PMCR_NUM_EV_MASK      0x1F
+
+#define L2PMCR                  0x400
+#define L2PMCNTENCLR            0x403
+#define L2PMCNTENSET            0x404
+#define L2PMINTENCLR            0x405
+#define L2PMINTENSET            0x406
+#define L2PMOVSCLR              0x407
+#define L2PMOVSSET              0x408
+#define L2PMCCNTCR              0x409
+#define L2PMCCNTR               0x40A
+#define L2PMCCNTSR              0x40C
+#define L2PMRESR                0x410
+#define IA_L2PMXEVCNTCR_BASE    0x420
+#define IA_L2PMXEVCNTR_BASE     0x421
+#define IA_L2PMXEVFILTER_BASE   0x423
+#define IA_L2PMXEVTYPER_BASE    0x424
+
+#define IA_L2_REG_OFFSET        0x10
+
+#define L2PMXEVFILTER_SUFILTER_ALL      0x000E0000
+#define L2PMXEVFILTER_ORGFILTER_IDINDEP 0x00000004
+#define L2PMXEVFILTER_ORGFILTER_ALL     0x00000003
+
+#define L2PM_CC_ENABLE          0x80000000
+
+#define L2EVTYPER_REG_SHIFT     3
+
+#define L2PMRESR_GROUP_BITS     8
+#define L2PMRESR_GROUP_MASK     GENMASK(7, 0)
+
+#define L2CYCLE_CTR_BIT         31
+#define L2CYCLE_CTR_RAW_CODE    0xFE
+
+#define L2PMCR_RESET_ALL        0x6
+#define L2PMCR_COUNTERS_ENABLE  0x1
+#define L2PMCR_COUNTERS_DISABLE 0x0
+
+#define L2PMRESR_EN             ((u64)1 << 63)
+
+#define L2_EVT_MASK             0x00000FFF
+#define L2_EVT_CODE_MASK        0x00000FF0
+#define L2_EVT_GRP_MASK         0x0000000F
+#define L2_EVT_CODE_SHIFT       4
+#define L2_EVT_GRP_SHIFT        0
+
+#define L2_EVT_CODE(event)   (((event) & L2_EVT_CODE_MASK) >> L2_EVT_CODE_SHIFT)
+#define L2_EVT_GROUP(event)  (((event) & L2_EVT_GRP_MASK) >> L2_EVT_GRP_SHIFT)
+
+#define L2_EVT_GROUP_MAX        7
+
+#define L2_MAX_PERIOD           U32_MAX
+#define L2_CNT_PERIOD           (U32_MAX - GENMASK(26, 0))
+
+#define L2CPUSRSELR_EL1         S3_3_c15_c0_6
+#define L2CPUSRDR_EL1           S3_3_c15_c0_7
+
+static DEFINE_RAW_SPINLOCK(l2_access_lock);
+
+/**
+ * set_l2_indirect_reg: write value to an L2 register
+ * @reg: Address of L2 register.
+ * @value: Value to be written to register.
+ *
+ * Use architecturally required barriers for ordering between system register
+ * accesses
+ */
+static void set_l2_indirect_reg(u64 reg, u64 val)
+{
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&l2_access_lock, flags);
+	write_sysreg(reg, L2CPUSRSELR_EL1);
+	isb();
+	write_sysreg(val, L2CPUSRDR_EL1);
+	isb();
+	raw_spin_unlock_irqrestore(&l2_access_lock, flags);
+}
+
+/**
+ * get_l2_indirect_reg: read an L2 register value
+ * @reg: Address of L2 register.
+ *
+ * Use architecturally required barriers for ordering between system register
+ * accesses
+ */
+static u64 get_l2_indirect_reg(u64 reg)
+{
+	u64 val;
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&l2_access_lock, flags);
+	write_sysreg(reg, L2CPUSRSELR_EL1);
+	isb();
+	val = read_sysreg(L2CPUSRDR_EL1);
+	raw_spin_unlock_irqrestore(&l2_access_lock, flags);
+
+	return val;
+}
+
+/*
+ * Aggregate PMU. Implements the core pmu functions and manages
+ * the hardware PMUs.
+ */
+struct l2cache_pmu {
+	struct hlist_node node;
+	u32 num_pmus;
+	struct pmu pmu;
+	int num_counters;
+	cpumask_t cpumask;
+	struct platform_device *pdev;
+};
+
+/*
+ * The cache is made up of one or more clusters, each cluster has its own PMU.
+ * Each cluster is associated with one or more CPUs.
+ * This structure represents one of the hardware PMUs.
+ *
+ * Events can be envisioned as a 2-dimensional array. Each column represents
+ * a group of events. There are 8 groups. Only one entry from each
+ * group can be in use at a time. When an event is assigned a counter
+ * by *_event_add(), the counter index is assigned to group_to_counter[group].
+ * This allows *filter_match() to detect and reject conflicting events in
+ * the same group.
+ * Events are specified as 0xCCG, where CC is 2 hex digits specifying
+ * the code (array row) and G specifies the group (column).
+ *
+ * In addition there is a cycle counter event specified by L2CYCLE_CTR_RAW_CODE
+ * which is outside the above scheme.
+ */
+struct hml2_pmu {
+	struct perf_event *events[MAX_L2_CTRS];
+	struct l2cache_pmu *l2cache_pmu;
+	DECLARE_BITMAP(used_counters, MAX_L2_CTRS);
+	DECLARE_BITMAP(used_groups, L2_EVT_GROUP_MAX + 1);
+	int group_to_counter[L2_EVT_GROUP_MAX + 1];
+	int irq;
+	/* The CPU that is used for collecting events on this cluster */
+	int on_cpu;
+	/* All the CPUs associated with this cluster */
+	cpumask_t cluster_cpus;
+	spinlock_t pmu_lock;
+};
+
+#define to_l2cache_pmu(p) (container_of(p, struct l2cache_pmu, pmu))
+
+static DEFINE_PER_CPU(struct hml2_pmu *, pmu_cluster);
+static u32 l2_cycle_ctr_idx;
+static u32 l2_counter_present_mask;
+
+static inline u32 idx_to_reg_bit(u32 idx)
+{
+	if (idx == l2_cycle_ctr_idx)
+		return BIT(L2CYCLE_CTR_BIT);
+
+	return BIT(idx);
+}
+
+static inline struct hml2_pmu *get_hml2_pmu(int cpu)
+{
+	return per_cpu(pmu_cluster, cpu);
+}
+
+static void hml2_pmu__reset_on_cluster(void *x)
+{
+	/* Reset all ctrs */
+	set_l2_indirect_reg(L2PMCR, L2PMCR_RESET_ALL);
+	set_l2_indirect_reg(L2PMCNTENCLR, l2_counter_present_mask);
+	set_l2_indirect_reg(L2PMINTENCLR, l2_counter_present_mask);
+	set_l2_indirect_reg(L2PMOVSCLR, l2_counter_present_mask);
+}
+
+static inline void hml2_pmu__reset(struct hml2_pmu *cluster)
+{
+	cpumask_t *mask = &cluster->cluster_cpus;
+
+	if (smp_call_function_any(mask, hml2_pmu__reset_on_cluster, NULL, 1))
+		dev_err(&cluster->l2cache_pmu->pdev->dev,
+			"Failed to reset on cluster with cpu %d\n",
+			cpumask_first(&cluster->cluster_cpus));
+}
+
+static inline void hml2_pmu__enable(void)
+{
+	set_l2_indirect_reg(L2PMCR, L2PMCR_COUNTERS_ENABLE);
+}
+
+static inline void hml2_pmu__disable(void)
+{
+	set_l2_indirect_reg(L2PMCR, L2PMCR_COUNTERS_DISABLE);
+}
+
+static inline void hml2_pmu__counter_set_value(u32 idx, u64 value)
+{
+	u32 counter_reg;
+
+	if (idx == l2_cycle_ctr_idx) {
+		set_l2_indirect_reg(L2PMCCNTR, value);
+	} else {
+		counter_reg = (idx * IA_L2_REG_OFFSET) + IA_L2PMXEVCNTR_BASE;
+		set_l2_indirect_reg(counter_reg, value & GENMASK(31, 0));
+	}
+}
+
+static inline u64 hml2_pmu__counter_get_value(u32 idx)
+{
+	u64 value;
+	u32 counter_reg;
+
+	if (idx == l2_cycle_ctr_idx) {
+		value = get_l2_indirect_reg(L2PMCCNTR);
+	} else {
+		counter_reg = (idx * IA_L2_REG_OFFSET) + IA_L2PMXEVCNTR_BASE;
+		value = get_l2_indirect_reg(counter_reg);
+	}
+
+	return value;
+}
+
+static inline void hml2_pmu__counter_enable(u32 idx)
+{
+	set_l2_indirect_reg(L2PMCNTENSET, idx_to_reg_bit(idx));
+}
+
+static inline void hml2_pmu__counter_disable(u32 idx)
+{
+	set_l2_indirect_reg(L2PMCNTENCLR, idx_to_reg_bit(idx));
+}
+
+static inline void hml2_pmu__counter_enable_interrupt(u32 idx)
+{
+	set_l2_indirect_reg(L2PMINTENSET, idx_to_reg_bit(idx));
+}
+
+static inline void hml2_pmu__counter_disable_interrupt(u32 idx)
+{
+	set_l2_indirect_reg(L2PMINTENCLR, idx_to_reg_bit(idx));
+}
+
+static inline void hml2_pmu__set_evccntcr(u32 val)
+{
+	set_l2_indirect_reg(L2PMCCNTCR, val);
+}
+
+static inline void hml2_pmu__set_evcntcr(u32 ctr, u32 val)
+{
+	u32 evtcr_reg = (ctr * IA_L2_REG_OFFSET) + IA_L2PMXEVCNTCR_BASE;
+
+	set_l2_indirect_reg(evtcr_reg, val);
+}
+
+static inline void hml2_pmu__set_evtyper(u32 ctr, u32 val)
+{
+	u32 evtype_reg = (ctr * IA_L2_REG_OFFSET) + IA_L2PMXEVTYPER_BASE;
+
+	set_l2_indirect_reg(evtype_reg, val);
+}
+
+static void hml2_pmu__set_resr(struct hml2_pmu *cluster,
+			       u32 event_group, u32 event_cc)
+{
+	u64 field;
+	u64 resr_val;
+	u32 shift;
+	unsigned long flags;
+
+	shift = L2PMRESR_GROUP_BITS * event_group;
+	field = ((u64)(event_cc & L2PMRESR_GROUP_MASK) << shift) | L2PMRESR_EN;
+
+	spin_lock_irqsave(&cluster->pmu_lock, flags);
+
+	resr_val = get_l2_indirect_reg(L2PMRESR);
+	resr_val &= ~(L2PMRESR_GROUP_MASK << shift);
+	resr_val |= field;
+	set_l2_indirect_reg(L2PMRESR, resr_val);
+
+	spin_unlock_irqrestore(&cluster->pmu_lock, flags);
+}
+
+/*
+ * Hardware allows filtering of events based on the originating
+ * CPU. Turn this off by setting filter bits to allow events from
+ * all CPUS, subunits and ID independent events in this cluster.
+ */
+static inline void hml2_pmu__set_evfilter_sys_mode(u32 ctr)
+{
+	u32 reg = (ctr * IA_L2_REG_OFFSET) + IA_L2PMXEVFILTER_BASE;
+	u32 val =  L2PMXEVFILTER_SUFILTER_ALL |
+		   L2PMXEVFILTER_ORGFILTER_IDINDEP |
+		   L2PMXEVFILTER_ORGFILTER_ALL;
+
+	set_l2_indirect_reg(reg, val);
+}
+
+static inline u32 hml2_pmu__getreset_ovsr(void)
+{
+	u32 result = get_l2_indirect_reg(L2PMOVSSET);
+
+	set_l2_indirect_reg(L2PMOVSCLR, result);
+	return result;
+}
+
+static inline bool hml2_pmu__has_overflowed(u32 ovsr)
+{
+	return !!(ovsr & l2_counter_present_mask);
+}
+
+static inline bool hml2_pmu__counter_has_overflowed(u32 ovsr, u32 idx)
+{
+	return !!(ovsr & idx_to_reg_bit(idx));
+}
+
+static void l2_cache__event_update_from_cluster(struct perf_event *event,
+						struct hml2_pmu *cluster)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	u64 delta64, prev, now;
+	u32 delta;
+	u32 idx = hwc->idx;
+
+	do {
+		prev = local64_read(&hwc->prev_count);
+		now = hml2_pmu__counter_get_value(idx);
+	} while (local64_cmpxchg(&hwc->prev_count, prev, now) != prev);
+
+	if (idx == l2_cycle_ctr_idx) {
+		/*
+		 * The cycle counter is 64-bit so needs separate handling
+		 * of 64-bit delta.
+		 */
+		delta64 = now - prev;
+		local64_add(delta64, &event->count);
+	} else {
+		/*
+		 * 32-bit counters need the unsigned 32-bit math to handle
+		 * overflow and now < prev
+		 */
+		delta = now - prev;
+		local64_add(delta, &event->count);
+	}
+}
+
+static void l2_cache__cluster_set_period(struct hml2_pmu *cluster,
+				       struct hw_perf_event *hwc)
+{
+	u64 base = L2_MAX_PERIOD - (L2_CNT_PERIOD - 1);
+	u32 idx = hwc->idx;
+	u64 prev = local64_read(&hwc->prev_count);
+	u64 value;
+
+	/*
+	 * Limit the maximum period to prevent the counter value
+	 * from overtaking the one we are about to program.
+	 * Use a starting value which is high enough that after
+	 * an overflow, interrupt latency will not cause the count
+	 * to reach the base value. If the previous value
+	 * is below the base, increase it to be above the base
+	 * and update prev_count accordingly. Otherwise if
+	 * the previous value is already above the base
+	 * nothing needs to be done to prev_count.
+	 */
+	if (prev < base) {
+		value = base + prev;
+		local64_set(&hwc->prev_count, value);
+	} else {
+		value = prev;
+	}
+
+	hml2_pmu__counter_set_value(idx, value);
+}
+
+static int l2_cache__get_event_idx(struct hml2_pmu *cluster,
+				   struct perf_event *event)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	int idx;
+
+	if (hwc->config_base == L2CYCLE_CTR_RAW_CODE) {
+		if (test_and_set_bit(l2_cycle_ctr_idx, cluster->used_counters))
+			return -EAGAIN;
+
+		return l2_cycle_ctr_idx;
+	}
+
+	for (idx = 0; idx < cluster->l2cache_pmu->num_counters - 1; idx++) {
+		if (!test_and_set_bit(idx, cluster->used_counters)) {
+			set_bit(L2_EVT_GROUP(hwc->config_base),
+				cluster->used_groups);
+			return idx;
+		}
+	}
+
+	/* The counters are all in use. */
+	return -EAGAIN;
+}
+
+static void l2_cache__clear_event_idx(struct hml2_pmu *cluster,
+				      struct perf_event *event)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	int idx = hwc->idx;
+
+	clear_bit(idx, cluster->used_counters);
+	if (hwc->config_base != L2CYCLE_CTR_RAW_CODE)
+		clear_bit(L2_EVT_GROUP(hwc->config_base), cluster->used_groups);
+}
+
+static irqreturn_t l2_cache__handle_irq(int irq_num, void *data)
+{
+	struct hml2_pmu *cluster = data;
+	int num_counters = cluster->l2cache_pmu->num_counters;
+	u32 ovsr;
+	int idx;
+
+	ovsr = hml2_pmu__getreset_ovsr();
+	if (!hml2_pmu__has_overflowed(ovsr))
+		return IRQ_NONE;
+
+	for_each_set_bit(idx, cluster->used_counters, num_counters) {
+		struct perf_event *event = cluster->events[idx];
+		struct hw_perf_event *hwc;
+
+		if (!hml2_pmu__counter_has_overflowed(ovsr, idx))
+			continue;
+
+		l2_cache__event_update_from_cluster(event, cluster);
+		hwc = &event->hw;
+
+		l2_cache__cluster_set_period(cluster, hwc);
+	}
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Implementation of abstract pmu functionality required by
+ * the core perf events code.
+ */
+
+static void l2_cache__pmu_enable(struct pmu *pmu)
+{
+	/*
+	 * Although there is only one PMU (per socket) controlling multiple
+	 * physical PMUs (per cluster), because we do not support per-task mode
+	 * each event is associated with a CPU. Each event has pmu_enable
+	 * called on its CPU, so here it is only necessary to enable the
+	 * counters for the current CPU.
+	 */
+
+	hml2_pmu__enable();
+}
+
+static void l2_cache__pmu_disable(struct pmu *pmu)
+{
+	hml2_pmu__disable();
+}
+
+static int l2_cache__event_init(struct perf_event *event)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	struct hml2_pmu *cluster;
+	struct perf_event *sibling;
+	struct l2cache_pmu *l2cache_pmu;
+
+	if (event->attr.type != event->pmu->type)
+		return -ENOENT;
+
+	l2cache_pmu = to_l2cache_pmu(event->pmu);
+
+	if (hwc->sample_period) {
+		dev_warn(&l2cache_pmu->pdev->dev, "Sampling not supported\n");
+		return -EOPNOTSUPP;
+	}
+
+	if (event->cpu < 0) {
+		dev_warn(&l2cache_pmu->pdev->dev, "Per-task mode not supported\n");
+		return -EOPNOTSUPP;
+	}
+
+	/* We cannot filter accurately so we just don't allow it. */
+	if (event->attr.exclude_user || event->attr.exclude_kernel ||
+	    event->attr.exclude_hv || event->attr.exclude_idle) {
+		dev_warn(&l2cache_pmu->pdev->dev, "Can't exclude execution levels\n");
+		return -EOPNOTSUPP;
+	}
+
+	if (((L2_EVT_GROUP(event->attr.config) > L2_EVT_GROUP_MAX) ||
+	     ((event->attr.config & ~L2_EVT_MASK) != 0)) &&
+	    (event->attr.config != L2CYCLE_CTR_RAW_CODE)) {
+		dev_warn(&l2cache_pmu->pdev->dev, "Invalid config %llx\n",
+			 event->attr.config);
+		return -EINVAL;
+	}
+
+	/* Don't allow groups with mixed PMUs, except for s/w events */
+	if (event->group_leader->pmu != event->pmu &&
+	    !is_software_event(event->group_leader)) {
+		dev_warn(&l2cache_pmu->pdev->dev,
+			 "Can't create mixed PMU group\n");
+		return -EINVAL;
+	}
+
+	list_for_each_entry(sibling, &event->group_leader->sibling_list,
+			    group_entry)
+		if (sibling->pmu != event->pmu &&
+		    !is_software_event(sibling)) {
+			dev_warn(&l2cache_pmu->pdev->dev,
+				 "Can't create mixed PMU group\n");
+			return -EINVAL;
+		}
+
+	/* Ensure all events in a group are on the same cpu */
+	cluster = get_hml2_pmu(event->cpu);
+	if ((event->group_leader != event) &&
+	    (cluster->on_cpu != event->group_leader->cpu)) {
+		dev_warn(&l2cache_pmu->pdev->dev,
+			 "Can't create group on CPUs %d and %d",
+			 event->cpu, event->group_leader->cpu);
+		return -EINVAL;
+	}
+
+	hwc->idx = -1;
+	hwc->config_base = event->attr.config;
+
+	/*
+	 * Ensure all events are on the same cpu so all events are in the
+	 * same cpu context, to avoid races on pmu_enable etc.
+	 */
+	event->cpu = cluster->on_cpu;
+
+	return 0;
+}
+
+static void l2_cache__event_start(struct perf_event *event, int flags)
+{
+	struct hml2_pmu *cluster;
+	struct hw_perf_event *hwc = &event->hw;
+	int idx = hwc->idx;
+	u32 config;
+	u32 event_cc, event_group;
+
+	hwc->state = 0;
+
+	cluster = get_hml2_pmu(event->cpu);
+	l2_cache__cluster_set_period(cluster, hwc);
+
+	if (hwc->config_base == L2CYCLE_CTR_RAW_CODE) {
+		hml2_pmu__set_evccntcr(0x0);
+	} else {
+		config = hwc->config_base;
+		event_cc    = L2_EVT_CODE(config);
+		event_group = L2_EVT_GROUP(config);
+
+		hml2_pmu__set_evcntcr(idx, 0x0);
+		hml2_pmu__set_evtyper(idx, event_group);
+		hml2_pmu__set_resr(cluster, event_group, event_cc);
+		hml2_pmu__set_evfilter_sys_mode(idx);
+	}
+
+	hml2_pmu__counter_enable_interrupt(idx);
+	hml2_pmu__counter_enable(idx);
+}
+
+static void l2_cache__event_stop(struct perf_event *event, int flags)
+{
+	struct hml2_pmu *cluster;
+	struct hw_perf_event *hwc = &event->hw;
+	int idx = hwc->idx;
+
+	if (!(hwc->state & PERF_HES_STOPPED)) {
+		cluster = get_hml2_pmu(event->cpu);
+		hml2_pmu__counter_disable_interrupt(idx);
+		hml2_pmu__counter_disable(idx);
+
+		if (flags & PERF_EF_UPDATE)
+			l2_cache__event_update_from_cluster(event, cluster);
+		hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
+	}
+}
+
+static int l2_cache__event_add(struct perf_event *event, int flags)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	int idx;
+	int err = 0;
+	struct hml2_pmu *cluster;
+
+	cluster = get_hml2_pmu(event->cpu);
+
+	idx = l2_cache__get_event_idx(cluster, event);
+	if (idx < 0) {
+		err = idx;
+		return err;
+	}
+
+	hwc->idx = idx;
+	hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
+	cluster->events[idx] = event;
+	cluster->group_to_counter[L2_EVT_GROUP(hwc->config_base)] = idx;
+	local64_set(&hwc->prev_count, 0ULL);
+
+	if (flags & PERF_EF_START)
+		l2_cache__event_start(event, flags);
+
+	/* Propagate changes to the userspace mapping. */
+	perf_event_update_userpage(event);
+
+	return err;
+}
+
+static void l2_cache__event_del(struct perf_event *event, int flags)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	struct hml2_pmu *cluster;
+	int idx = hwc->idx;
+
+	cluster = get_hml2_pmu(event->cpu);
+	l2_cache__event_stop(event, flags | PERF_EF_UPDATE);
+	cluster->events[idx] = NULL;
+	l2_cache__clear_event_idx(cluster, event);
+
+	perf_event_update_userpage(event);
+}
+
+static void l2_cache__event_read(struct perf_event *event)
+{
+	l2_cache__event_update_from_cluster(event, get_hml2_pmu(event->cpu));
+}
+
+static int l2_cache_filter_match(struct perf_event *event)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	struct hml2_pmu *cluster = get_hml2_pmu(event->cpu);
+	unsigned int group = L2_EVT_GROUP(hwc->config_base);
+
+	/* check for column exclusion: group already in use by another event */
+	if (test_bit(group, cluster->used_groups) &&
+	    cluster->events[cluster->group_to_counter[group]] != event)
+		return 0;
+
+	return 1;
+}
+
+static ssize_t l2_cache_pmu_cpumask_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	struct l2cache_pmu *l2cache_pmu = to_l2cache_pmu(dev_get_drvdata(dev));
+
+	return cpumap_print_to_pagebuf(true, buf, &l2cache_pmu->cpumask);
+}
+
+static struct device_attribute l2_cache_pmu_cpumask_attr =
+		__ATTR(cpumask, S_IRUGO, l2_cache_pmu_cpumask_show, NULL);
+
+static struct attribute *l2_cache_pmu_cpumask_attrs[] = {
+	&l2_cache_pmu_cpumask_attr.attr,
+	NULL,
+};
+
+static struct attribute_group l2_cache_pmu_cpumask_group = {
+	.attrs = l2_cache_pmu_cpumask_attrs,
+};
+
+/* CCG format for perf RAW codes. */
+PMU_FORMAT_ATTR(l2_code,   "config:4-11");
+PMU_FORMAT_ATTR(l2_group,  "config:0-3");
+static struct attribute *l2_cache_pmu_formats[] = {
+	&format_attr_l2_code.attr,
+	&format_attr_l2_group.attr,
+	NULL,
+};
+
+static struct attribute_group l2_cache_pmu_format_group = {
+	.name = "format",
+	.attrs = l2_cache_pmu_formats,
+};
+
+static const struct attribute_group *l2_cache_pmu_attr_grps[] = {
+	&l2_cache_pmu_format_group,
+	&l2_cache_pmu_cpumask_group,
+	NULL,
+};
+
+/*
+ * Generic device handlers
+ */
+
+static const struct acpi_device_id l2_cache_pmu_acpi_match[] = {
+	{ "QCOM8130", },
+	{ }
+};
+
+static int get_num_counters(void)
+{
+	int val;
+
+	val = get_l2_indirect_reg(L2PMCR);
+
+	/*
+	 * Read number of counters from L2PMCR and add 1
+	 * for the cycle counter.
+	 */
+	return ((val >> L2PMCR_NUM_EV_SHIFT) & L2PMCR_NUM_EV_MASK) + 1;
+}
+
+static int l2cache_pmu_online_cpu(unsigned int cpu, struct hlist_node *node)
+{
+	struct hml2_pmu *cluster;
+	cpumask_t cluster_online_cpus;
+	struct l2cache_pmu *l2cache_pmu;
+
+	l2cache_pmu = hlist_entry_safe(node, struct l2cache_pmu, node);
+	cluster = get_hml2_pmu(cpu);
+	cpumask_and(&cluster_online_cpus, &cluster->cluster_cpus,
+		    cpu_online_mask);
+
+	if (cpumask_weight(&cluster_online_cpus) == 1) {
+		/* all CPUs on this cluster were down, use this one */
+		cluster->on_cpu = cpu;
+		cpumask_set_cpu(cpu, &l2cache_pmu->cpumask);
+		WARN_ON(irq_set_affinity(cluster->irq, cpumask_of(cpu)));
+	}
+
+	return 0;
+}
+
+static int l2cache_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node)
+{
+	struct hml2_pmu *cluster;
+	struct l2cache_pmu *l2cache_pmu;
+	cpumask_t cluster_online_cpus;
+	unsigned int target;
+
+	l2cache_pmu = hlist_entry_safe(node, struct l2cache_pmu, node);
+
+	if (!cpumask_test_and_clear_cpu(cpu, &l2cache_pmu->cpumask))
+		return 0;
+	cluster = get_hml2_pmu(cpu);
+	cpumask_and(&cluster_online_cpus, &cluster->cluster_cpus,
+		    cpu_online_mask);
+
+	/* Any other CPU for this cluster which is still online */
+	target = cpumask_any_but(&cluster_online_cpus, cpu);
+	if (target >= nr_cpu_ids)
+		return 0;
+
+	perf_pmu_migrate_context(&l2cache_pmu->pmu, cpu, target);
+	cluster->on_cpu = target;
+	cpumask_set_cpu(target, &l2cache_pmu->cpumask);
+	WARN_ON(irq_set_affinity(cluster->irq, cpumask_of(target)));
+
+	return 0;
+}
+
+static int l2_cache_pmu_probe_cluster(struct device *dev, void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev->parent);
+	struct platform_device *sdev = to_platform_device(dev);
+	struct l2cache_pmu *l2cache_pmu = data;
+	struct hml2_pmu *cluster;
+	struct acpi_device *device;
+	unsigned long fw_cluster_id;
+	int cpu;
+	int err;
+	int irq;
+
+	if (acpi_bus_get_device(ACPI_HANDLE(dev), &device))
+		return -ENODEV;
+
+	if (kstrtol(device->pnp.unique_id, 10, &fw_cluster_id) < 0) {
+		dev_err(&pdev->dev, "unable to read ACPI uid\n");
+		return -ENODEV;
+	}
+
+	irq = platform_get_irq(sdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev,
+			"Failed to get valid irq for cluster %ld\n",
+			fw_cluster_id);
+		return irq;
+	}
+
+	cluster = devm_kzalloc(&pdev->dev, sizeof(*cluster), GFP_KERNEL);
+	if (!cluster)
+		return -ENOMEM;
+
+	cluster->l2cache_pmu = l2cache_pmu;
+	for_each_present_cpu(cpu) {
+		if (topology_physical_package_id(cpu) == fw_cluster_id) {
+			cpumask_set_cpu(cpu, &cluster->cluster_cpus);
+			per_cpu(pmu_cluster, cpu) = cluster;
+		}
+	}
+	cluster->irq = irq;
+
+	if (cpumask_empty(&cluster->cluster_cpus)) {
+		dev_err(&pdev->dev, "No CPUs found for L2 cache instance %ld\n",
+			fw_cluster_id);
+		return -ENODEV;
+	}
+
+	/* Pick one CPU to be the preferred one to use in the cluster */
+	cluster->on_cpu = cpumask_first(&cluster->cluster_cpus);
+
+	if (irq_set_affinity(irq, cpumask_of(cluster->on_cpu))) {
+		dev_err(&pdev->dev,
+			"Unable to set irq affinity (irq=%d, cpu=%d)\n",
+			irq, cluster->on_cpu);
+		return -ENODEV;
+	}
+
+	err = devm_request_irq(&pdev->dev, irq, l2_cache__handle_irq,
+			       IRQF_NOBALANCING, "l2-cache-pmu", cluster);
+	if (err) {
+		dev_err(&pdev->dev,
+			"Unable to request IRQ%d for L2 PMU counters\n", irq);
+		return err;
+	}
+
+	dev_info(&pdev->dev,
+		 "Registered L2 cache PMU instance %ld with %d CPUs\n",
+		 fw_cluster_id, cpumask_weight(&cluster->cluster_cpus));
+
+	cluster->pmu_lock = __SPIN_LOCK_UNLOCKED(cluster->pmu_lock);
+	cpumask_set_cpu(cluster->on_cpu, &l2cache_pmu->cpumask);
+
+	hml2_pmu__reset(cluster);
+	l2cache_pmu->num_pmus++;
+
+	return 0;
+}
+
+static int l2_cache_pmu_probe(struct platform_device *pdev)
+{
+	int err;
+	struct l2cache_pmu *l2cache_pmu;
+
+	l2cache_pmu =
+		devm_kzalloc(&pdev->dev, sizeof(*l2cache_pmu), GFP_KERNEL);
+	if (!l2cache_pmu)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, l2cache_pmu);
+	l2cache_pmu->pmu = (struct pmu) {
+		/* suffix is instance id for future use with multiple sockets */
+		.name		= "l2cache_0",
+		.task_ctx_nr    = perf_invalid_context,
+		.pmu_enable	= l2_cache__pmu_enable,
+		.pmu_disable	= l2_cache__pmu_disable,
+		.event_init	= l2_cache__event_init,
+		.add		= l2_cache__event_add,
+		.del		= l2_cache__event_del,
+		.start		= l2_cache__event_start,
+		.stop		= l2_cache__event_stop,
+		.read		= l2_cache__event_read,
+		.attr_groups	= l2_cache_pmu_attr_grps,
+		.filter_match   = l2_cache_filter_match,
+	};
+
+	l2cache_pmu->num_counters = get_num_counters();
+	l2cache_pmu->pdev = pdev;
+	l2_cycle_ctr_idx = l2cache_pmu->num_counters - 1;
+	l2_counter_present_mask = GENMASK(l2cache_pmu->num_counters - 2, 0) |
+		L2PM_CC_ENABLE;
+
+	cpumask_clear(&l2cache_pmu->cpumask);
+
+	/* Read cluster info and initialize each cluster */
+	err = device_for_each_child(&pdev->dev, l2cache_pmu,
+				    l2_cache_pmu_probe_cluster);
+	if (err < 0)
+		return err;
+
+	if (l2cache_pmu->num_pmus == 0) {
+		dev_err(&pdev->dev, "No hardware L2 cache PMUs found\n");
+		return -ENODEV;
+	}
+
+	err = perf_pmu_register(&l2cache_pmu->pmu, l2cache_pmu->pmu.name, -1);
+	if (err < 0) {
+		dev_err(&pdev->dev, "Error %d registering L2 cache PMU\n", err);
+		return err;
+	}
+
+	dev_info(&pdev->dev, "Registered L2 cache PMU using %d HW PMUs\n",
+		 l2cache_pmu->num_pmus);
+
+	err = cpuhp_state_add_instance_nocalls(CPUHP_AP_PERF_ARM_QCOM_L2_ONLINE,
+					       &l2cache_pmu->node);
+
+	return err;
+}
+
+static int l2_cache_pmu_remove(struct platform_device *pdev)
+{
+	struct l2cache_pmu *l2cache_pmu =
+		to_l2cache_pmu(platform_get_drvdata(pdev));
+
+	cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_QCOM_L2_ONLINE,
+					       &l2cache_pmu->node);
+	perf_pmu_unregister(&l2cache_pmu->pmu);
+	return 0;
+}
+
+static struct platform_driver l2_cache_pmu_driver = {
+	.driver = {
+		.name = "qcom-l2cache-pmu",
+		.owner = THIS_MODULE,
+		.acpi_match_table = ACPI_PTR(l2_cache_pmu_acpi_match),
+	},
+	.probe = l2_cache_pmu_probe,
+	.remove = l2_cache_pmu_remove,
+};
+
+static int __init register_l2_cache_pmu_driver(void)
+{
+	int err;
+
+	err = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_QCOM_L2_ONLINE,
+				      "AP_PERF_ARM_QCOM_L2_ONLINE",
+				      l2cache_pmu_online_cpu,
+				      l2cache_pmu_offline_cpu);
+	if (err)
+		return err;
+
+	return platform_driver_register(&l2_cache_pmu_driver);
+}
+device_initcall(register_l2_cache_pmu_driver);
diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
index 5dbb26e..eb73c21 100644
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -102,6 +102,7 @@ enum cpuhp_state {
 	CPUHP_AP_PERF_ARM_CCI_ONLINE,
 	CPUHP_AP_PERF_ARM_CCN_ONLINE,
 	CPUHP_AP_PERF_ARM_L2X0_ONLINE,
+	CPUHP_AP_PERF_ARM_QCOM_L2_ONLINE,
 	CPUHP_AP_WORKQUEUE_ONLINE,
 	CPUHP_AP_RCUTREE_ONLINE,
 	CPUHP_AP_NOTIFY_ONLINE,
-- 
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project.

^ permalink raw reply related

* [PATCH -next] phy: rockchip-typec: fix non static symbol warnings
From: Wei Yongjun @ 2016-09-21 15:05 UTC (permalink / raw)
  To: linux-arm-kernel

From: Wei Yongjun <weiyongjun1@huawei.com>

Fixes the following sparse warnings:

drivers/phy/phy-rockchip-typec.c:295:16: warning:
 symbol 'usb3_pll_cfg' was not declared. Should it be static?
drivers/phy/phy-rockchip-typec.c:312:16: warning:
 symbol 'dp_pll_cfg' was not declared. Should it be static?

Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
---
 drivers/phy/phy-rockchip-typec.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/phy/phy-rockchip-typec.c b/drivers/phy/phy-rockchip-typec.c
index 7cfb0f8..555604c 100644
--- a/drivers/phy/phy-rockchip-typec.c
+++ b/drivers/phy/phy-rockchip-typec.c
@@ -292,7 +292,7 @@ struct phy_reg {
 	u32 addr;
 };
 
-struct phy_reg usb3_pll_cfg[] = {
+static struct phy_reg usb3_pll_cfg[] = {
 	{ 0xf0,		CMN_PLL0_VCOCAL_INIT },
 	{ 0x18,		CMN_PLL0_VCOCAL_ITER },
 	{ 0xd0,		CMN_PLL0_INTDIV },
@@ -309,7 +309,7 @@ struct phy_reg usb3_pll_cfg[] = {
 	{ 0x8,		CMN_DIAG_PLL0_LF_PROG },
 };
 
-struct phy_reg dp_pll_cfg[] = {
+static struct phy_reg dp_pll_cfg[] = {
 	{ 0xf0,		CMN_PLL1_VCOCAL_INIT },
 	{ 0x18,		CMN_PLL1_VCOCAL_ITER },
 	{ 0x30b9,	CMN_PLL1_VCOCAL_START },

^ permalink raw reply related

* [PATCH v2] drivers: psci: PSCI checker module
From: Kevin Brodsky @ 2016-09-21 14:39 UTC (permalink / raw)
  To: linux-arm-kernel

On arm and arm64, PSCI is one of the possible firmware interfaces
used for power management. This includes both turning CPUs on and off,
and suspending them (entering idle states).

This patch adds a PSCI checker module that enables basic testing of
PSCI operations during startup. There are two main tests: CPU
hotplugging and suspending.

In the hotplug tests, the hotplug API is used to turn off and on again
all CPUs in the system, and then all CPUs in each cluster, checking
the consistency of the return codes.

In the suspend tests, a high-priority thread is created on each core
and uses low-level cpuidle functionalities to enter suspend, in all
the possible states and multiple times. This should allow a maximum
number of CPUs to enter the same sleep state at the same or slightly
different time.

In essence, the suspend tests use a principle similar to that of the
intel_powerclamp driver (drivers/thermal/intel_powerclamp.c), but the
threads are only kept for the duration of the test (they are already
gone when userspace is started).

While in theory power management PSCI functions (CPU_{ON,OFF,SUSPEND})
could be directly called, this proved too difficult as it would imply
the duplication of all the logic used by the kernel to allow for a
clean shutdown/bringup/suspend of the CPU (the deepest sleep states
implying potentially the shutdown of the CPU).

Note that this file cannot be compiled as a loadable module, since it
uses a number of non-exported identifiers (essentially for
PSCI-specific checks and direct use of cpuidle) and relies on the
absence of userspace to avoid races when calling hotplug and cpuidle
functions.

Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Kevin Hilman <khilman@kernel.org>
Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Kevin Brodsky <kevin.brodsky@arm.com>
---
Changelog v1..v2:
* Do not count tick_broadcast_enter() failures as errors, as they may
  be unavoidable. When it happens, fall back to WFI.
* Do not count unexpected sleep states as errors (currently, the only
  case is when falling back to WFI). Instead, report the number of
  times it happens before the suspend thread exits.
* Use usecs_to_jiffies() to compute the suspend timeout. The previous
  version resulted in a zero timeout if the target residency was
  shorter than a jiffy.
* Various cleanup.

Thanks to Lorenzo for his help with improving this patch!

Kevin

 drivers/firmware/Kconfig        |   7 +
 drivers/firmware/Makefile       |   1 +
 drivers/firmware/psci_checker.c | 488 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 496 insertions(+)
 create mode 100644 drivers/firmware/psci_checker.c

diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 0e22f241403b..0033c8755104 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -206,6 +206,13 @@ config QCOM_SCM_64
 config HAVE_ARM_SMCCC
 	bool
 
+config PSCI_CHECKER
+	bool "PSCI checker"
+	depends on ARM_PSCI_FW && HOTPLUG_CPU
+	help
+	  Run the PSCI checker during startup. This checks that hotplug and
+	  suspend operations work correctly when using PSCI.
+
 source "drivers/firmware/broadcom/Kconfig"
 source "drivers/firmware/google/Kconfig"
 source "drivers/firmware/efi/Kconfig"
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 44a59dcfc398..3efc794906c6 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_QCOM_SCM)		+= qcom_scm.o
 obj-$(CONFIG_QCOM_SCM_64)	+= qcom_scm-64.o
 obj-$(CONFIG_QCOM_SCM_32)	+= qcom_scm-32.o
 CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQUIRES_SEC=1) -march=armv7-a
+obj-$(CONFIG_PSCI_CHECKER)	+= psci_checker.o
 
 obj-y				+= broadcom/
 obj-$(CONFIG_GOOGLE_FIRMWARE)	+= google/
diff --git a/drivers/firmware/psci_checker.c b/drivers/firmware/psci_checker.c
new file mode 100644
index 000000000000..c8992c0be630
--- /dev/null
+++ b/drivers/firmware/psci_checker.c
@@ -0,0 +1,488 @@
+/*
+ * 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.
+ *
+ * Copyright (C) 2016 ARM Limited
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/atomic.h>
+#include <linux/completion.h>
+#include <linux/cpu.h>
+#include <linux/cpuidle.h>
+#include <linux/cpu_pm.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/preempt.h>
+#include <linux/psci.h>
+#include <linux/slab.h>
+#include <linux/tick.h>
+#include <linux/topology.h>
+
+#include <asm/cpuidle.h>
+
+#include <uapi/linux/psci.h>
+
+#define NUM_SUSPEND_CYCLE (10)
+
+static unsigned int nb_available_cpus;
+static int tos_resident_cpu = -1;
+
+static atomic_t nb_active_threads;
+static struct completion suspend_threads_started =
+	COMPLETION_INITIALIZER(suspend_threads_started);
+static struct completion suspend_threads_done =
+	COMPLETION_INITIALIZER(suspend_threads_done);
+
+/*
+ * We assume that PSCI operations are used if they are available. This is not
+ * necessarily true on arm64, since the decision is based on the
+ * "enable-method" property of each CPU in the DT, but given that there is no
+ * arch-specific way to check this, we assume that the DT is sensible.
+ */
+static int psci_ops_check(void)
+{
+	int migrate_type = -1;
+	int cpu;
+
+	if (!(psci_ops.cpu_off && psci_ops.cpu_on && psci_ops.cpu_suspend)) {
+		pr_warn("Missing PSCI operations, aborting tests\n");
+		return -EOPNOTSUPP;
+	}
+
+	if (psci_ops.migrate_info_type)
+		migrate_type = psci_ops.migrate_info_type();
+
+	if (migrate_type == PSCI_0_2_TOS_UP_MIGRATE ||
+	    migrate_type == PSCI_0_2_TOS_UP_NO_MIGRATE) {
+		/* There is a UP Trusted OS, find on which core it resides. */
+		for_each_online_cpu(cpu)
+			if (psci_tos_resident_on(cpu)) {
+				tos_resident_cpu = cpu;
+				break;
+			}
+		if (tos_resident_cpu == -1)
+			pr_warn("UP Trusted OS resides on no online CPU\n");
+	}
+
+	return 0;
+}
+
+static int find_clusters(const struct cpumask *cpus,
+			 const struct cpumask **clusters)
+{
+	unsigned int nb = 0;
+	cpumask_var_t tmp;
+
+	if (!alloc_cpumask_var(&tmp, GFP_KERNEL))
+		return -ENOMEM;
+	cpumask_copy(tmp, cpus);
+
+	while (!cpumask_empty(tmp)) {
+		const struct cpumask *cluster =
+			topology_core_cpumask(cpumask_any(tmp));
+
+		clusters[nb++] = cluster;
+		cpumask_andnot(tmp, tmp, cluster);
+	}
+
+	free_cpumask_var(tmp);
+	return nb;
+}
+
+/*
+ * offlined_cpus is a temporary array but passing it as an argument avoids
+ * multiple allocations.
+ */
+static unsigned int down_and_up_cpus(const struct cpumask *cpus,
+				     struct cpumask *offlined_cpus)
+{
+	int cpu;
+	int err = 0;
+
+	cpumask_clear(offlined_cpus);
+
+	/* Try to power down all CPUs in the mask. */
+	for_each_cpu(cpu, cpus) {
+		int ret = cpu_down(cpu);
+
+		/*
+		 * cpu_down() checks the number of online CPUs before the TOS
+		 * resident CPU.
+		 */
+		if (cpumask_weight(offlined_cpus) + 1 == nb_available_cpus) {
+			if (ret != -EBUSY) {
+				pr_err("Unexpected return code %d while trying "
+				       "to power down last online CPU %d\n",
+				       ret, cpu);
+				++err;
+			}
+		} else if (cpu == tos_resident_cpu) {
+			if (ret != -EPERM) {
+				pr_err("Unexpected return code %d while trying "
+				       "to power down TOS resident CPU %d\n",
+				       ret, cpu);
+				++err;
+			}
+		} else if (ret != 0) {
+			pr_err("Error occurred (%d) while trying "
+			       "to power down CPU %d\n", ret, cpu);
+			++err;
+		}
+
+		if (ret == 0)
+			cpumask_set_cpu(cpu, offlined_cpus);
+	}
+
+	/* Try to power up all the CPUs that have been offlined. */
+	for_each_cpu(cpu, offlined_cpus) {
+		int ret = cpu_up(cpu);
+
+		if (ret != 0) {
+			pr_err("Error occurred (%d) while trying "
+			       "to power up CPU %d\n", ret, cpu);
+			++err;
+		} else {
+			cpumask_clear_cpu(cpu, offlined_cpus);
+		}
+	}
+
+	/*
+	 * Something went bad at some point and some CPUs could not be turned
+	 * back on.
+	 */
+	WARN_ON(!cpumask_empty(offlined_cpus) ||
+		num_online_cpus() != nb_available_cpus);
+
+	return err;
+}
+
+static int hotplug_tests(void)
+{
+	int err;
+	cpumask_var_t offlined_cpus;
+	int i, nb_cluster;
+	const struct cpumask **clusters;
+	char *page_buf;
+
+	err = -ENOMEM;
+	if (!alloc_cpumask_var(&offlined_cpus, GFP_KERNEL))
+		return err;
+	/* We may have up to nb_available_cpus clusters. */
+	clusters = kmalloc_array(nb_available_cpus, sizeof(*clusters),
+				 GFP_KERNEL);
+	if (!clusters)
+		goto out_free_cpus;
+	page_buf = (char *)__get_free_page(GFP_KERNEL);
+	if (!page_buf)
+		goto out_free_clusters;
+
+	err = 0;
+	nb_cluster = find_clusters(cpu_online_mask, clusters);
+
+	/*
+	 * Of course the last CPU cannot be powered down and cpu_down() should
+	 * refuse doing that.
+	 */
+	pr_info("Trying to turn off and on again all CPUs\n");
+	err += down_and_up_cpus(cpu_online_mask, offlined_cpus);
+
+	/*
+	 * Take down CPUs by cluster this time. When the last CPU is turned
+	 * off, the cluster itself should shut down.
+	 */
+	for (i = 0; i < nb_cluster; ++i) {
+		int cluster_id =
+			topology_physical_package_id(cpumask_any(clusters[i]));
+		ssize_t len = cpumap_print_to_pagebuf(true, page_buf,
+						      clusters[i]);
+		/* Remove trailing newline. */
+		page_buf[len - 1] = '\0';
+		pr_info("Trying to turn off and on again cluster %d "
+			"(CPUs %s)\n", cluster_id, page_buf);
+		err += down_and_up_cpus(clusters[i], offlined_cpus);
+	}
+
+	free_page((unsigned long)page_buf);
+out_free_clusters:
+	kfree(clusters);
+out_free_cpus:
+	free_cpumask_var(offlined_cpus);
+	return err;
+}
+
+static void dummy_callback(unsigned long ignored) {}
+
+static int suspend_cpu(int index, bool broadcast)
+{
+	int ret;
+
+	arch_cpu_idle_enter();
+
+	if (broadcast) {
+		/*
+		 * The local timer will be shut down, we need to enter tick
+		 * broadcast.
+		 */
+		ret = tick_broadcast_enter();
+		if (ret) {
+			/*
+			 * In the absence of hardware broadcast mechanism,
+			 * this CPU might be used to broadcast wakeups, which
+			 * may be why entering tick broadcast has failed.
+			 * There is little the kernel can do to work around
+			 * that, so enter WFI instead (idle state 0).
+			 */
+			cpu_do_idle();
+			ret = 0;
+			goto out_arch_exit;
+		}
+	}
+
+	/*
+	 * Replicate the common ARM cpuidle enter function
+	 * (arm_enter_idle_state).
+	 */
+	ret = CPU_PM_CPU_IDLE_ENTER(arm_cpuidle_suspend, index);
+
+	if (broadcast)
+		tick_broadcast_exit();
+
+out_arch_exit:
+	arch_cpu_idle_exit();
+
+	return ret;
+}
+
+static int suspend_stress_thread(void *arg)
+{
+	int cpu = (long)arg;
+	int i, nb_suspend = 0, nb_shallow_sleep = 0, nb_err = 0;
+	struct sched_param sched_priority = { .sched_priority = MAX_RT_PRIO-1 };
+	struct cpuidle_device *dev;
+	struct cpuidle_driver *drv;
+	/* No need for an actual callback, we just want to wake up the CPU. */
+	struct timer_list wakeup_timer =
+		TIMER_INITIALIZER(dummy_callback, 0, 0);
+
+	/* Wait for the main thread to give the start signal. */
+	wait_for_completion(&suspend_threads_started);
+
+	/* Set maximum priority to preempt all other threads on this CPU. */
+	if (sched_setscheduler_nocheck(current, SCHED_FIFO, &sched_priority))
+		pr_warn("Failed to set suspend thread scheduler on CPU %d\n",
+			cpu);
+
+	dev = this_cpu_read(cpuidle_devices);
+	drv = cpuidle_get_cpu_driver(dev);
+
+	pr_info("CPU %d entering suspend cycles, states 1 through %d\n",
+		cpu, drv->state_count - 1);
+
+	for (i = 0; i < NUM_SUSPEND_CYCLE; ++i) {
+		int index;
+		/*
+		 * Test all possible states, except 0 (which is usually WFI and
+		 * doesn't use PSCI).
+		 */
+		for (index = 1; index < drv->state_count; ++index) {
+			struct cpuidle_state *state = &drv->states[index];
+			bool broadcast = state->flags & CPUIDLE_FLAG_TIMER_STOP;
+			int ret;
+
+			/*
+			 * Set the timer to wake this CPU up in some time (which
+			 * should be largely sufficient for entering suspend).
+			 * If the local tick is disabled when entering suspend,
+			 * suspend_cpu() takes care of switching to a broadcast
+			 * tick, so the timer will still wake us up.
+			 */
+			mod_timer(&wakeup_timer, jiffies +
+				  usecs_to_jiffies(state->target_residency));
+
+			/* IRQs must be disabled during suspend operations. */
+			local_irq_disable();
+
+			ret = suspend_cpu(index, broadcast);
+
+			/*
+			 * We have woken up. Re-enable IRQs to handle any
+			 * pending interrupt, do not wait until the end of the
+			 * loop.
+			 */
+			local_irq_enable();
+
+			if (ret == index) {
+				++nb_suspend;
+			} else if (ret >= 0) {
+				/* We did not enter the expected state. */
+				++nb_shallow_sleep;
+			} else {
+				pr_err("Failed to suspend CPU %d: error %d "
+				       "(requested state %d, cycle %d)\n",
+				       cpu, ret, index, i);
+				++nb_err;
+			}
+		}
+	}
+
+	/*
+	 * Disable the timer to make sure that the timer will not trigger
+	 * later.
+	 */
+	del_timer(&wakeup_timer);
+
+	if (atomic_dec_return_relaxed(&nb_active_threads) == 0)
+		complete(&suspend_threads_done);
+
+	/* Give up on RT scheduling and wait for termination. */
+	sched_priority.sched_priority = 0;
+	if (sched_setscheduler_nocheck(current, SCHED_NORMAL, &sched_priority))
+		pr_warn("Failed to set suspend thread scheduler on CPU %d\n",
+			cpu);
+	for (;;) {
+		/* Needs to be set first to avoid missing a wakeup. */
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (kthread_should_stop()) {
+			__set_current_state(TASK_RUNNING);
+			break;
+		}
+		schedule();
+	}
+
+	pr_info("CPU %d suspend test results: success %d, shallow states %d, errors %d\n",
+		cpu, nb_suspend, nb_shallow_sleep, nb_err);
+
+	return nb_err;
+}
+
+static int suspend_tests(void)
+{
+	int i, cpu, err = 0;
+	struct task_struct **threads;
+	int nb_threads = 0;
+
+	threads = kmalloc_array(nb_available_cpus, sizeof(*threads),
+				GFP_KERNEL);
+	if (!threads)
+		return -ENOMEM;
+
+	for_each_online_cpu(cpu) {
+		struct task_struct *thread;
+		/* Check that cpuidle is available on that CPU. */
+		struct cpuidle_device *dev = per_cpu(cpuidle_devices, cpu);
+		struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev);
+
+		if (cpuidle_not_available(drv, dev)) {
+			pr_warn("cpuidle not available on CPU %d, ignoring\n",
+				cpu);
+			continue;
+		}
+
+		thread = kthread_create_on_cpu(suspend_stress_thread,
+					       (void *)(long)cpu, cpu,
+					       "psci_suspend_stress");
+		if (IS_ERR(thread))
+			pr_err("Failed to create kthread on CPU %d\n", cpu);
+		else
+			threads[nb_threads++] = thread;
+	}
+	if (nb_threads < 1) {
+		kfree(threads);
+		return -ENODEV;
+	}
+
+	atomic_set(&nb_active_threads, nb_threads);
+
+	/*
+	 * Stop cpuidle to prevent the idle tasks from entering a deep sleep
+	 * mode, as it might interfere with the suspend threads on other CPUs.
+	 * This does not prevent the suspend threads from using cpuidle (only
+	 * the idle tasks check this status).
+	 */
+	cpuidle_pause();
+
+	/*
+	 * Unpark the suspend threads. To avoid the main thread being preempted
+	 * before all the threads have been unparked, the suspend threads will
+	 * wait for the completion of suspend_threads_started.
+	 */
+	for (i = 0; i < nb_threads; ++i)
+		kthread_unpark(threads[i]);
+	complete_all(&suspend_threads_started);
+
+	wait_for_completion(&suspend_threads_done);
+
+	cpuidle_resume();
+
+	/* Stop and destroy all threads, get return status. */
+	for (i = 0; i < nb_threads; ++i)
+		err += kthread_stop(threads[i]);
+
+	kfree(threads);
+	return err;
+}
+
+static int __init psci_checker(void)
+{
+	int ret;
+
+	/*
+	 * Since we're in an initcall, we assume that all the CPUs that all
+	 * CPUs that can be onlined have been onlined.
+	 *
+	 * The tests assume that hotplug is enabled but nobody else is using it,
+	 * otherwise the results will be unpredictable. However, since there
+	 * is no userspace yet in initcalls, that should be fine.
+	 */
+	nb_available_cpus = num_online_cpus();
+
+	/* Check PSCI operations are set up and working. */
+	ret = psci_ops_check();
+	if (ret)
+		return ret;
+
+	pr_info("PSCI checker started using %u CPUs\n", nb_available_cpus);
+
+	pr_info("Starting hotplug tests\n");
+	ret = hotplug_tests();
+	if (ret == 0)
+		pr_info("Hotplug tests passed OK\n");
+	else if (ret > 0)
+		pr_err("%d error(s) encountered in hotplug tests\n", ret);
+	else {
+		pr_err("Out of memory\n");
+		return ret;
+	}
+
+	pr_info("Starting suspend tests (%d cycles per state)\n",
+		NUM_SUSPEND_CYCLE);
+	ret = suspend_tests();
+	if (ret == 0)
+		pr_info("Suspend tests passed OK\n");
+	else if (ret > 0)
+		pr_err("%d error(s) encountered in suspend tests\n", ret);
+	else {
+		switch (ret) {
+		case -ENOMEM:
+			pr_err("Out of memory\n");
+			break;
+		case -ENODEV:
+			pr_warn("Could not start suspend tests on any CPU\n");
+			break;
+		}
+	}
+
+	pr_info("PSCI checker completed\n");
+	return ret < 0 ? ret : 0;
+}
+late_initcall(psci_checker);
-- 
2.9.3

^ permalink raw reply related

* [PATCH V6 3/5] PCI: thunder-pem: Allow to probe PEM-specific register range for ACPI case
From: Gabriele Paoloni @ 2016-09-21 14:10 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160920191744.GA4941@localhost>

Hi Bjorn

[...]


> 
> If future hardware is completely ECAM-compliant and we don't need any
> more MCFG quirks, that would be great.
> 
> But we'll still need to describe that memory-mapped config space
> somewhere.  If that's done with PNP0C02 or similar devices (as is done
> on my x86 laptop), we'd be all set.
> 
> If we need to work around firmware in the field that doesn't do that,
> one possibility is a PNP quirk along the lines of
> quirk_amd_mmconfig_area().

So, if my understanding is correct, for platforms that have not been
shipped yet you propose to use PNP0C02 in the ACPI table in order to
declare a motherboard reserved resource whereas for shipped platforms
you propose to have a quirk along pnp_fixups in order to track the
resource usage even if values are hardcoded...correct?

Before Tomasz came up with this patchset we had a call between the vendors
involved in this PCI quirks saga and other guys from Linaro and ARM.

Lorenzo summarized the outcome as in the following link
http://lkml.iu.edu/hypermail/linux/kernel/1606.2/03344.html

Since this quirks mechanism has been discussed for quite a long time now
IMHO it would be good to have a last call including also you (Bjorn) so
that we can all agree on what to do and we avoid changing our drivers again
and again...

What do you think?

Thanks

Gab

> 
> Bjorn

^ permalink raw reply

* [PATCH V6 0/5] ECAM quirks handling for ARM64 platforms
From: Sinan Kaya @ 2016-09-21 14:07 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160921131107.GB10804@localhost>

On 9/21/2016 9:11 AM, Bjorn Helgaas wrote:
> On Tue, Sep 20, 2016 at 09:15:14PM -0400, cov at codeaurora.org wrote:
>> Hi Bjorn, Thomasz,
>>

>>
>> Did you delete this because there were no current users, because you'd
>> prefer users just use "-1", or for some other reason?
> 
> I removed it because there were no users of it and, more importantly,
> the code doesn't implement support for it.

Is it possible to queue up Cov's patch as part of this effort once he 
rebases and sends an updated version? Cov will have to implement something
else now.



-- 
Sinan Kaya
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.

^ permalink raw reply

* [PATCH V6 3/5] PCI: thunder-pem: Allow to probe PEM-specific register range for ACPI case
From: Lorenzo Pieralisi @ 2016-09-21 14:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160920191744.GA4941@localhost>

On Tue, Sep 20, 2016 at 02:17:44PM -0500, Bjorn Helgaas wrote:
> On Tue, Sep 20, 2016 at 04:09:25PM +0100, Ard Biesheuvel wrote:

[...]

> > None of these platforms can be fixed entirely in software, and given
> > that we will not be adding quirks for new broken hardware, we should
> > ask ourselves whether having two versions of a quirk, i.e., one for
> > broken hardware + currently shipping firmware, and one for the same
> > broken hardware with fixed firmware is really an improvement over what
> > has been proposed here.
> 
> We're talking about two completely different types of quirks:
> 
>   1) MCFG quirks to use memory-mapped config space that doesn't quite
>      conform to the ECAM model in the PCIe spec, and
> 
>   2) Some yet-to-be-determined method to describe address space
>      consumed by a bridge.
> 
> The first two patches of this series are a nice implementation for 1).
> The third patch (ThunderX-specific) is one possibility for 2), but I
> don't like it because there's no way for generic software like the
> ACPI core to discover these resources.

Ok, so basically this means that to implement (2) we need to assign
some sort of _HID to these quirky PCI bridges (so that we know what
device they represent and we can retrieve their _CRS). I take from
this discussion that the goal is to make sure that all non-config
resources have to be declared through _CRS device objects, which is
fine but that requires a FW update (unless we can fabricate ACPI
devices and corresponding _CRS in the kernel whenever we match a
given MCFG table signature).

We discussed this already and I think we should make a decision:

http://lists.infradead.org/pipermail/linux-arm-kernel/2016-March/414722.html

> > > I'd like to step back and come up with some understanding of how
> > > non-broken firmware *should* deal with this issue.  Then, if we *do*
> > > work around this particular broken firmware in the kernel, it would be
> > > nice to do it in a way that fits in with that understanding.
> > >
> > > For example, if a companion ACPI device is the preferred solution, an
> > > ACPI quirk could fabricate a device with the required resources.  That
> > > would address the problem closer to the source and make it more likely
> > > that the rest of the system will work correctly: /proc/iomem could
> > > make sense, things that look at _CRS generically would work (e.g,
> > > /sys/, an admittedly hypothetical "lsacpi", etc.)
> > >
> > > Hard-coding stuff in drivers is a point solution that doesn't provide
> > > any guidance for future platforms and makes it likely that the hack
> > > will get copied into even more drivers.
> > >
> > 
> > OK, I see. But the guidance for future platforms should be 'do not
> > rely on quirks', and what I am arguing here is that the more we polish
> > up this code and make it clean and reusable, the more likely it is
> > that will end up getting abused by new broken hardware that we set out
> > to reject entirely in the first place.
> > 
> > So of course, if the quirk involves claiming resources, let's make
> > sure that this occurs in the cleanest and most compliant way possible.
> > But any factoring/reuse concerns other than for the current crop of
> > broken hardware should be avoided imo.
> 
> If future hardware is completely ECAM-compliant and we don't need any
> more MCFG quirks, that would be great.

Yes.

> But we'll still need to describe that memory-mapped config space
> somewhere.  If that's done with PNP0C02 or similar devices (as is done
> on my x86 laptop), we'd be all set.

I am not sure I understand what you mean here. Are you referring
to MCFG regions reported as PNP0c02 resources through its _CRS ?

IIUC PNP0C02 is a reservation mechanism, but it does not help us
associate its _CRS to a specific PCI host bridge instance, right ?

> If we need to work around firmware in the field that doesn't do that,
> one possibility is a PNP quirk along the lines of
> quirk_amd_mmconfig_area().

You mean matching PNP0C01/PNP0c02 and create a resource (that has to
hardcoded in a static array in the kernel anyway, there is no way to
retrieve it otherwise) in the corresponding PNP quirk handler ?

And it is not a given we can match against PNP0c01/PNP0c02.

So it looks like the only solution is allocating an _HID for each
host bridge that is not ECAM compliant to add resources to its _CRS
(unless the MCFG quirk does not need any additional data/resource,
eg "use different set of PCI accessorsi 32-bit vs byte-access").

For FW that is immutable I really do not see what we can do apart
from hardcoding the non-config resources (consumed by a bridge),
somehow.

Thanks,
Lorenzo

^ permalink raw reply

* [PATCH 2/3] i2c: bcm2835: Add support for combined write-read transfer
From: Noralf Trønnes @ 2016-09-21 13:45 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <DF457EB8-D1B8-4BA4-B79C-AF01AF7CAA3B@martin.sperl.org>


Den 20.09.2016 13:29, skrev kernel at martin.sperl.org:
>> On 20.09.2016, at 12:56, Noralf Tr?nnes <noralf@tronnes.org> wrote:
>>
>>
>> Den 20.09.2016 12:15, skrev Martin Sperl:
>>>
>>> On 20.09.2016 10:41, Noralf Tr?nnes wrote:
>>>> Den 20.09.2016 09:19, skrev Martin Sperl:
>>>>> Hi Noralf!
>>>>>
>>>>> On 19.09.2016 17:26, Noralf Tr?nnes wrote:
>>>>>> Some SMBus protocols use Repeated Start Condition to switch from write
>>>>>> mode to read mode. Devices like MMA8451 won't work without it.
>>>>>>
>>>>>> When downstream implemented support for this in i2c-bcm2708, it broke
>>>>>> support for some devices, so a module parameter was added and combined
>>>>>> transfer was disabled by default.
>>>>>> See https://github.com/raspberrypi/linux/issues/599
>>>>>> It doesn't seem to have been any investigation into what the problem
>>>>>> really was. Later there was added a timeout on the polling loop.
>>>>>>
>>>>>> One of the devices mentioned to partially stop working was DS1307.
>>>>>>
>>>>>> I have run thousands of transfers to a DS1307 (rtc), MMA8451 (accel)
>>>>>> and AT24C32 (eeprom) in parallel without problems.
>>>>>>
>>>>>> Signed-off-by: Noralf Tr?nnes <noralf@tronnes.org>
>>>>>> ---
>>>>>>   drivers/i2c/busses/i2c-bcm2835.c | 107
>>>>>> +++++++++++++++++++++++++++++++++++----
>>>>>>   1 file changed, 98 insertions(+), 9 deletions(-)
>>>>> ...
>>>>>> @@ -209,8 +289,17 @@ static int bcm2835_i2c_xfer(struct i2c_adapter
>>>>>> *adap, struct i2c_msg msgs[],
>>>>>>       int i;
>>>>>>       int ret = 0;
>>>>>>   +    /* Combined write-read to the same address (smbus) */
>>>>>> +    if (num == 2 && (msgs[0].addr == msgs[1].addr) &&
>>>>>> +        !(msgs[0].flags & I2C_M_RD) && (msgs[1].flags & I2C_M_RD) &&
>>>>>> +        (msgs[0].len <= 16)) {
>>>>>> +        ret = bcm2835_i2c_xfer_msg(i2c_dev, &msgs[0], &msgs[1]);
>>>>>> +
>>>>>> +    return ret ? ret : 2;
>>>>>> +    }
>>>>>> +
>>>>>>       for (i = 0; i < num; i++) {
>>>>>> -        ret = bcm2835_i2c_xfer_msg(i2c_dev, &msgs[i]);
>>>>>> +        ret = bcm2835_i2c_xfer_msg(i2c_dev, &msgs[i], NULL);
>>>>>>           if (ret)
>>>>>>               break;
>>>>>>       }
>>>>> This does not seem to implement the i2c_msg api correctly.
>>>>>
>>>>> As per comments in include/uapi/linux/i2c.h on line 58 only the last
>>>>> message
>>>>> in a group should - by default - send a STOP.
>>>>>
>>>> Apparently it's a known problem that the i2c controller doesn't support
>>>> Repeated Start. It will always issue a Stop when it has transferred DLEN
>>>> bytes.
>>>> Refs:
>>>> http://www.circuitwizard.de/raspi-i2c-fix/raspi-i2c-fix.html
>>>> http://raspberrypi.stackexchange.com/questions/31728/has-anyone-successfully-used-i2c-repeated-starts-on-the-pi2-my-scope-says-they
>>>>
>>>>
>>>> UNLESS: a Start Transfer (ST) is issued after Transfer Active (TA) is set
>>>> and before DONE is set (or the last byte is shifted, I don't know excatly).
>>>> Refs:
>>>> https://github.com/raspberrypi/linux/issues/254#issuecomment-15254134
>>>> https://www.raspberrypi.org/forums/viewtopic.php?p=807834&sid=2b612c7209f2175bf1a266359c72ae6c#p807834
>>>>
>>>>
>>>> I found this answer/report by joan that the downstream combined support
>>>> isn't reliable:
>>>> http://raspberrypi.stackexchange.com/questions/31728/has-anyone-successfully-used-i2c-repeated-starts-on-the-pi2-my-scope-says-they
>>>>
>>>>
>>>> My implementation differs from downstream in that I use local_irq_save()
>>>> to protect the polling loop. But that only protects from missing the TA
>>>> (downstream can miss the TA and issue a Stop).
>>>>
>>>> So currently in mainline we have a driver that says it support the standard
>>>> (I2C_FUNC_I2C), but it really only supports one message transfers since it
>>>> can't do ReStart.
>>>>
>>>> What I have done in this patch is to support ReStart for transfers with
>>>> 2 messages: first write, then read. But maybe a better solution is to just
>>>> leave this alone if it is flaky and use bitbanging instead. I don't know.
>>> I have not said that the approach you have taken is wrong or bad.
>>>
>> I didn't take it as such, I'm just not sure what's the best approach here,
>> so I added and looked up some more information
>>
>>> I was only telling you that the portion inside the bcm2835_i2c_xfer:
>>> +    /* Combined write-read to the same address (smbus) */
>>> +    if (num == 2 && (msgs[0].addr == msgs[1].addr) &&
>>> +        !(msgs[0].flags & I2C_M_RD) && (msgs[1].flags & I2C_M_RD) &&
>>> +        (msgs[0].len <= 16)) {
>>> +        ret = bcm2835_i2c_xfer_msg(i2c_dev, &msgs[0], &msgs[1]);
>>> +
>>> +        return ret ? ret : 2;
>>> +    }
>>> is very specific and maybe could be done in a "generic" manner
>>> supporting more cases.
>>>
>> It has to be specific when it comes to number of messages. We can only
>> support ReStart after the first message unless we use polling for the
>> whole transfer. And in that case we can't disable interrupts for such
>> a long period and we will end up sometimes loosing Transfer Active,
>> resulting in Stop Condition between the messages.
>> So we can only do transfers with 2 messages if we want Restart.
>>
>> It is possible to support more than 16 bytes for the first message,
>> filling the FIFO after polling TA, but I'm not sure that is common.
>> Mostly it's 1 or 2 bytes to set a register.
>> The write-read restriction isn't absolutely necessary either, but it's the
>> most common case I think. So it was about reusing bcm2835_i2c_xfer_msg().
>> A less restrictive approach would require a dedicated function I think.
>>
>>> At least add a dev_warn_once for all num > 1 cases not handled by the
>>> code above.
>>>
>>> This gives people an opportunity to detect such a situation if they
>>> find something is not working as expected.
>>>
>> I agree.
>>
>> After reading joan's report I wonder if it would be best to add a module
>> parameter like downstream has, so it can be disabled. What do you think?
>>
> I guess let us start simple:
> * get warning in place about always issuing a stop for num > 1
>    - instead we may just want to set max_num_msgs = 1 in quirks.
> * apply your patch for the write (<=16) then read case.
>    - maybe by setting quirks I2C_AQ_COMB_WRITE_THEN_READ
>      plus max_comb_1st_msg_len = 16 and max_num_msgs = 2
>
> If this becomes too restrictive for some specific HW, then someone
> may want to add the missing features.
>
> As for the module parameters: no idea if this is acceptable
> or sensible.
>
> But that?s just my 2c...

It suddenly struct me that I had seen the TA bit set when I debugged the
interrupt function. And it turns out that if I don't prefill the FIFO,
then I can use the TXW interrupt to know when the transfer is active,
no need for polling. It looks promising so far, need to run my testcases
to be sure.

Thanks,
Noralf.

^ permalink raw reply

* ftrace function_graph causes system crash
From: Steven Rostedt @ 2016-09-21 13:45 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <0e38f79fdc1f4671943da74764c15050@SIWEX5A.sing.micron.com>

On Wed, 21 Sep 2016 07:50:58 +0000
"Bean Huo (beanhuo)" <beanhuo@micron.com> wrote:
 
> 
> Hi, Steve
> Thanks very much! This is a very useful trace tool, I now know the problem function,
> It is gt_counter_read, if not trace this function, ftrace function_graph work well.
> Do you know now how to deeply debug and trace which line is wrong through Ftrace?

Hmm, maybe I should add this into the scripts directory. Yeah, I should
do that.

-- Steve

^ permalink raw reply

* [PATCH v2] clkdev: Detect errors in clk_hw_register_clkdev() for mass registration
From: Geert Uytterhoeven @ 2016-09-21 13:12 UTC (permalink / raw)
  To: linux-arm-kernel

Unlike clk_register_clkdev(), clk_hw_register_clkdev() doesn't check for
passed error objects from a previous registration call. Hence the caller
of clk_hw_register_*() had to check for errors before calling
clk_hw_register_clkdev*().

Make clk_hw_register_clkdev() more similar to clk_register_clkdev() by
adding this error check, removing the burden from callers that do mass
registration.

Fixes: e4f1b49bda6d6aa2 ("clkdev: Add clk_hw based registration APIs")
Fixes: 944b9a41e004534f ("clk: ls1x: Migrate to clk_hw based OF and registration APIs")
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
---
v2:
  - Correct wrong function references
    s/clkdev_{,hw_}create/clk_{,hw_}register_clkdev/

Note that in the mean time more clk_hw conversions not checking for
errors have been posted, leading to crashes in error paths, so it would
be good to have this check in the core.
---
 drivers/clk/clkdev.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c
index 97ae60fa15849954..bb8a77a5985f8627 100644
--- a/drivers/clk/clkdev.c
+++ b/drivers/clk/clkdev.c
@@ -448,12 +448,20 @@ EXPORT_SYMBOL(clk_register_clkdev);
  *
  * con_id or dev_id may be NULL as a wildcard, just as in the rest of
  * clkdev.
+ *
+ * To make things easier for mass registration, we detect error clk_hws
+ * from a previous clk_hw_register_*() call, and return the error code for
+ * those.  This is to permit this function to be called immediately
+ * after clk_hw_register_*().
  */
 int clk_hw_register_clkdev(struct clk_hw *hw, const char *con_id,
 	const char *dev_id)
 {
 	struct clk_lookup *cl;
 
+	if (IS_ERR(hw))
+		return PTR_ERR(hw);
+
 	/*
 	 * Since dev_id can be NULL, and NULL is handled specially, we must
 	 * pass it as either a NULL format string, or with "%s".
-- 
1.9.1

^ permalink raw reply related

* [PATCH V6 0/5] ECAM quirks handling for ARM64 platforms
From: Bjorn Helgaas @ 2016-09-21 13:11 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <0429663d51ed8e93fb0c96261dd2ecc7@codeaurora.org>

On Tue, Sep 20, 2016 at 09:15:14PM -0400, cov at codeaurora.org wrote:
> Hi Bjorn, Thomasz,
> 
> On 2016-09-20 15:26, Bjorn Helgaas wrote:
> >On Fri, Sep 09, 2016 at 09:24:02PM +0200, Tomasz Nowicki wrote:
> >>Quirk handling relies on an idea of simple static array which contains
> >>quirk enties. Each entry consists of identification information
> >>(IDs from
> >>standard header of MCFG table) along with custom pci_ecam_ops
> >>structure and
> >>configuration space resource structure. This way it is possible find
> >>corresponding quirk entries and override pci_ecam_ops and PCI
> >>configuration
> >>space regions.
> >>
> >>As an example, the last 3 patches present quirk handling
> >>mechanism usage for
> >>ThunderX.
> >>
> >>v5 -> v6
> >>- rebase against v4.8-rc5
> >>- drop patch 1 form previous series
> >>- keep pci_acpi_setup_ecam_mapping() in ARM64 arch directory
> >>- move quirk code to pci_mcfg.c
> >>- restrict quirk to override pci_ecam_ops and CFG resource structure
> >>  only, no init call any more
> >>- split ThunderX quirks into the smaller chunks
> >>- add ThunderX pass1.x silicon revision support
> >>
> >>v4 -> v5
> >>- rebase against v4.8-rc1
> >>- rework to exact MCFG OEM ID, TABLE ID, rev match
> >>  - use memcmp instead of strncmp
> >>  - no substring match
> >>- fix typos and dmesg message
> >>
> >>Tomasz Nowicki (5):
> >>  PCI/ACPI: Extend pci_mcfg_lookup() responsibilities
> >>  PCI/ACPI: Check platform specific ECAM quirks
> >>  PCI: thunder-pem: Allow to probe PEM-specific register range
> >>for ACPI
> >>    case
> >>  PCI: thunder: Enable ACPI PCI controller for ThunderX pass2.x
> >>silicon
> >>    version
> >>  PCI: thunder: Enable ACPI PCI controller for ThunderX pass1.x
> >>silicon
> >>    version
> >>
> >> arch/arm64/kernel/pci.c             |  17 ++--
> >> drivers/acpi/pci_mcfg.c             | 168
> >>+++++++++++++++++++++++++++++++++++-
> >> drivers/pci/host/pci-thunder-ecam.c |   2 +-
> >> drivers/pci/host/pci-thunder-pem.c  |  63 +++++++++++---
> >> include/linux/pci-acpi.h            |   4 +-
> >> include/linux/pci-ecam.h            |   7 ++
> >> 6 files changed, 230 insertions(+), 31 deletions(-)
> >
> >I'm not quite ready to merge these because we haven't resolved the
> >question of how to expose the resources used by the memory-mapped
> >config space.  I'm fine with the first two patches (I did make a
> >couple trivial changes, see below), but there's no point in merging
> >them until we merge a user for them.
> >
> >I pushed the series to pci/ecam-v6 for build testing and discussion.
> >The diff (the changes I made locally) from v6 as posted by Tomasz is
> >below.
> 
> Rebasing the following simple quirks framework user onto this branch,
> I have some questions.
> 
> https://source.codeaurora.org/quic/server/kernel/commit/?h=cov/4.8-rc2-testing&id=83b766cafef11c107b10177d0626db311f382299
> 
> >diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c
> >index eb14f74..bb3b8ad 100644
> >--- a/drivers/acpi/pci_mcfg.c
> >+++ b/drivers/acpi/pci_mcfg.c
> >@@ -42,86 +42,59 @@ struct mcfg_fixup {
> > 	struct resource cfgres;
> > };
> >
> >-#define MCFG_DOM_ANY			(-1)
> 
> Did you delete this because there were no current users, because you'd
> prefer users just use "-1", or for some other reason?

I removed it because there were no users of it and, more importantly,
the code doesn't implement support for it.

> > #define MCFG_BUS_RANGE(start, end)	DEFINE_RES_NAMED((start),	\
> > 						((end) - (start) + 1),	\
> > 						NULL, IORESOURCE_BUS)
> >-#define MCFG_BUS_ANY		MCFG_BUS_RANGE(0x0, 0xff)
> >-#define MCFG_RES_EMPTY		DEFINE_RES_NAMED(0, 0, NULL, 0)
> >+#define MCFG_BUS_ANY			MCFG_BUS_RANGE(0x0, 0xff)
> >
> > static struct mcfg_fixup mcfg_quirks[] = {
> >-/*	{ OEM_ID, OEM_TABLE_ID, REV, DOMAIN, BUS_RANGE, cfgres, ops }, */
> >+/*	{ OEM_ID, OEM_TABLE_ID, REV, SEGMENT, BUS_RANGE, cfgres, ops }, */
> 
> This comment appears to have the order of cfgres and ops reversed.

Fixed, thanks!

> Am I correct in reading that if a user of the framework does not wish to
> override cfgres they must place a struct resource with .start = 0 at the
> end of their mcfg_quirks entry? If so, I guess I have the same questions
> about removing MCFG_RES_EMPTY as I do about removing MCFG_DOM_ANY.

You're right that we only override cfgres if the quirk supplies a
struct resource with non-zero .start.  I removed MCFG_RES_EMPTY
because mcfg_quirks[] is a static array and is initialized with all
members being zero anyway.  If a quirk doesn't need to override
cfgres, I think it's more readable if the quirk just doesn't mention
the resource at all.

Bjorn

^ permalink raw reply

* [PATCH] drm/sun4i: rgb: Enable panel after controller
From: Jonathan Liu @ 2016-09-21 13:03 UTC (permalink / raw)
  To: linux-arm-kernel

The panel should be enabled after the controller so that the panel
prepare/enable delays are properly taken into account. Similarly, the
panel should be disabled before the controller so that the panel
unprepare/disable delays are properly taken into account.

This is useful for avoiding visual glitches.

Signed-off-by: Jonathan Liu <net147@gmail.com>
---
 drivers/gpu/drm/sun4i/sun4i_rgb.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c
index c3ff10f..4e4bea6 100644
--- a/drivers/gpu/drm/sun4i/sun4i_rgb.c
+++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c
@@ -152,15 +152,16 @@ static void sun4i_rgb_encoder_enable(struct drm_encoder *encoder)
 
 	DRM_DEBUG_DRIVER("Enabling RGB output\n");
 
-	if (!IS_ERR(tcon->panel)) {
+	if (!IS_ERR(tcon->panel))
 		drm_panel_prepare(tcon->panel);
-		drm_panel_enable(tcon->panel);
-	}
 
 	/* encoder->bridge can be NULL; drm_bridge_enable checks for it */
 	drm_bridge_enable(encoder->bridge);
 
 	sun4i_tcon_channel_enable(tcon, 0);
+
+	if (!IS_ERR(tcon->panel))
+		drm_panel_enable(tcon->panel);
 }
 
 static void sun4i_rgb_encoder_disable(struct drm_encoder *encoder)
@@ -171,15 +172,16 @@ static void sun4i_rgb_encoder_disable(struct drm_encoder *encoder)
 
 	DRM_DEBUG_DRIVER("Disabling RGB output\n");
 
+	if (!IS_ERR(tcon->panel))
+		drm_panel_disable(tcon->panel);
+
 	sun4i_tcon_channel_disable(tcon, 0);
 
 	/* encoder->bridge can be NULL; drm_bridge_disable checks for it */
 	drm_bridge_disable(encoder->bridge);
 
-	if (!IS_ERR(tcon->panel)) {
-		drm_panel_disable(tcon->panel);
+	if (!IS_ERR(tcon->panel))
 		drm_panel_unprepare(tcon->panel);
-	}
 }
 
 static void sun4i_rgb_encoder_mode_set(struct drm_encoder *encoder,
-- 
2.10.0

^ permalink raw reply related

* [PATCH] usb: dwc3: host: inherit dma configuration from parent dev
From: Arnd Bergmann @ 2016-09-21 12:48 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <DB5PR0401MB1925CDF6CBD45ACBEE95D152F5F60@DB5PR0401MB1925.eurprd04.prod.outlook.com>

On Wednesday, September 21, 2016 11:43:59 AM CEST Sriram Dash wrote:
> >From: Arnd Bergmann [mailto:arnd at arndb.de]
> >On Wednesday, September 21, 2016 11:06:47 AM CEST Sriram Dash wrote:
> 
> ==============================================================
> From 8b0dea1513e9e73a11dcfa802ddc71cca11d66f8 Mon Sep 17 00:00:00 2001
> From: Sriram Dash <sriram.dash@nxp.com>
> Date: Wed, 21 Sep 2016 11:39:30 +0530
> Subject: [PATCH] usb: xhci: Fix the patch inherit dma configuration from
>  parent dev
> 
> Fixes the patch https://patchwork.kernel.org/patch/9319527/
> ("usb: dwc3: host: inherit dma configuration from parent dev").
> 
> Signed-off-by: Sriram Dash <sriram.dash@nxp.com>
> ---
>  drivers/usb/host/xhci-mem.c | 12 ++++++------
>  drivers/usb/host/xhci.c     | 20 ++++++++++----------
>  2 files changed, 16 insertions(+), 16 deletions(-)
> 
> diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
> index 6afe323..79608df 100644
> --- a/drivers/usb/host/xhci-mem.c
> +++ b/drivers/usb/host/xhci-mem.c

All the changes you did to this file seem fine, I completely missed that part.

> diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
> index 01d96c9..9a1ff09 100644
> --- a/drivers/usb/host/xhci.c
> +++ b/drivers/usb/host/xhci.c
> @@ -231,7 +231,7 @@ static int xhci_free_msi(struct xhci_hcd *xhci)
>  static int xhci_setup_msi(struct xhci_hcd *xhci)
>  {
>  	int ret;
> -	struct pci_dev  *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
> +	struct pci_dev  *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.sysdev);
>  
>  	ret = pci_enable_msi(pdev);
>  	if (ret) {

This one is interesting as I stumbled over some code comment mentioning
that for dwc3-pci, we don't support MSI. I think with this change,
we /can/ actually support MSI, but this could be a separate patch
and we'd have to test it on dwc3-pci hardware. Same for most of
this file.

> @@ -745,7 +745,7 @@ void xhci_shutdown(struct usb_hcd *hcd)
>  	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
>  
>  	if (xhci->quirks & XHCI_SPURIOUS_REBOOT)
> -		usb_disable_xhci_ports(to_pci_dev(hcd->self.controller));
> +		usb_disable_xhci_ports(to_pci_dev(hcd->self.sysdev));
>  
>  	spin_lock_irq(&xhci->lock);
>  	xhci_halt(xhci);

This seems obviously correct, but I don't yet see why it currently
works. We probably don't call this function on dwc3.

>  #ifdef CONFIG_PM
> @@ -3605,7 +3605,7 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
>  	 * if no devices remain.
>  	 */
>  	if (xhci->quirks & XHCI_RESET_ON_RESUME)
> -		pm_runtime_put_noidle(hcd->self.controller);
> +		pm_runtime_put_noidle(hcd->self.sysdev);
>  #endif
>  
>  	ret = xhci_check_args(hcd, udev, NULL, 0, true, __func__);

I suspect this one is wrong, based on what Felipe explained earlier:
the power management should propagate down from the child to the
parent device.

Someone who understands this better than I do should look at it.

> @@ -3745,7 +3745,7 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
>  	 * suspend if there is a device attached.
>  	 */
>  	if (xhci->quirks & XHCI_RESET_ON_RESUME)
> -		pm_runtime_get_noresume(hcd->self.controller);
> +		pm_runtime_get_noresume(hcd->self.sysdev);
>  #endif
>  
>  

Same here.

> @@ -4834,7 +4834,7 @@ int xhci_get_frame(struct usb_hcd *hcd)
>  int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
>  {
>  	struct xhci_hcd		*xhci;
> -	struct device		*dev = hcd->self.controller;
> +	struct device		*dev = hcd->self.sysdev;
>  	int			retval;


This one calls

        get_quirks(dev, xhci);

not sure whether this should be called with self.controller or
self.sysdev, we should audit every one of the callers here to
be sure:

drivers/usb/host/xhci-mtk.c:    return xhci_gen_setup(hcd, xhci_mtk_quirks);
drivers/usb/host/xhci-pci.c:    retval = xhci_gen_setup(hcd, xhci_pci_quirks);
drivers/usb/host/xhci-plat.c:   return xhci_gen_setup(hcd, xhci_plat_quirks);
drivers/usb/host/xhci-rcar.c:    * xhci_gen_setup().
drivers/usb/host/xhci-tegra.c:  return xhci_gen_setup(hcd, tegra_xhci_quirks);

	Arnd

^ permalink raw reply

* [PATCH v3 9/9] dmaengine: ti-dma-crossbar: enable COMPILE_TEST
From: Peter Ujfalusi @ 2016-09-21 12:41 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160921124135.11849-1-peter.ujfalusi@ti.com>

To get more coverage, enable COMPILE_TEST for this driver.
When compile testing eDMA or omap-dma, select also the ti-dma-crossbar so
it is also covered by the compile testing.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
---
 drivers/dma/Kconfig | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 84647d6cb2f4..1c11628062ad 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -133,7 +133,7 @@ config DMA_OMAP
 	depends on ARCH_OMAP || COMPILE_TEST
 	select DMA_ENGINE
 	select DMA_VIRTUAL_CHANNELS
-	select TI_DMA_CROSSBAR if SOC_DRA7XX
+	select TI_DMA_CROSSBAR if (SOC_DRA7XX || COMPILE_TEST)
 
 config DMA_SA11X0
 	tristate "SA-11x0 DMA support"
@@ -518,7 +518,7 @@ config TI_EDMA
 	depends on ARCH_DAVINCI || ARCH_OMAP || ARCH_KEYSTONE || COMPILE_TEST
 	select DMA_ENGINE
 	select DMA_VIRTUAL_CHANNELS
-	select TI_DMA_CROSSBAR if ARCH_OMAP
+	select TI_DMA_CROSSBAR if (ARCH_OMAP || COMPILE_TEST)
 	default n
 	help
 	  Enable support for the TI EDMA controller. This DMA
-- 
2.10.0

^ permalink raw reply related

* [PATCH v3 8/9] dmaengine: omap-dma: enable COMPILE_TEST
From: Peter Ujfalusi @ 2016-09-21 12:41 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160921124135.11849-1-peter.ujfalusi@ti.com>

To get more coverage, enable COMPILE_TEST for this driver.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
---
 drivers/dma/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 4348249b943a..84647d6cb2f4 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -130,7 +130,7 @@ config DMA_JZ4780
 
 config DMA_OMAP
 	tristate "OMAP DMA support"
-	depends on ARCH_OMAP
+	depends on ARCH_OMAP || COMPILE_TEST
 	select DMA_ENGINE
 	select DMA_VIRTUAL_CHANNELS
 	select TI_DMA_CROSSBAR if SOC_DRA7XX
-- 
2.10.0

^ permalink raw reply related

* [PATCH v3 7/9] dmaengine: edma: enable COMPILE_TEST
From: Peter Ujfalusi @ 2016-09-21 12:41 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160921124135.11849-1-peter.ujfalusi@ti.com>

To get more coverage, enable COMPILE_TEST for this driver.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
---
 drivers/dma/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index fe2dbb811e19..4348249b943a 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -515,7 +515,7 @@ config TI_DMA_CROSSBAR
 
 config TI_EDMA
 	bool "TI EDMA support"
-	depends on ARCH_DAVINCI || ARCH_OMAP || ARCH_KEYSTONE
+	depends on ARCH_DAVINCI || ARCH_OMAP || ARCH_KEYSTONE || COMPILE_TEST
 	select DMA_ENGINE
 	select DMA_VIRTUAL_CHANNELS
 	select TI_DMA_CROSSBAR if ARCH_OMAP
-- 
2.10.0

^ permalink raw reply related

* [PATCH v3 6/9] dmaengine: ti-dma-crossbar: Fix of_device_id data parameter usage
From: Peter Ujfalusi @ 2016-09-21 12:41 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160921124135.11849-1-peter.ujfalusi@ti.com>

Use pointers to static constant variables for crossbar type and for DMA
offset configuration.

Fixes compiler warnings on 64bit architectures:

drivers/dma/ti-dma-crossbar.c: In function ?ti_dra7_xbar_probe?:
drivers/dma/ti-dma-crossbar.c:398:21: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
  xbar->dma_offset = (u32)match->data;
                     ^
drivers/dma/ti-dma-crossbar.c: In function ?ti_dma_xbar_probe?:
drivers/dma/ti-dma-crossbar.c:431:10: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
  switch ((u32)match->data) {
          ^

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
---
 drivers/dma/ti-dma-crossbar.c | 28 ++++++++++++++++++----------
 1 file changed, 18 insertions(+), 10 deletions(-)

diff --git a/drivers/dma/ti-dma-crossbar.c b/drivers/dma/ti-dma-crossbar.c
index e4f3bd1ae264..9103b1260797 100644
--- a/drivers/dma/ti-dma-crossbar.c
+++ b/drivers/dma/ti-dma-crossbar.c
@@ -18,15 +18,19 @@
 
 #define TI_XBAR_DRA7		0
 #define TI_XBAR_AM335X		1
+static const u32 ti_xbar_type[] = {
+	[TI_XBAR_DRA7] = TI_XBAR_DRA7,
+	[TI_XBAR_AM335X] = TI_XBAR_AM335X,
+};
 
 static const struct of_device_id ti_dma_xbar_match[] = {
 	{
 		.compatible = "ti,dra7-dma-crossbar",
-		.data = (void *)TI_XBAR_DRA7,
+		.data = &ti_xbar_type[TI_XBAR_DRA7],
 	},
 	{
 		.compatible = "ti,am335x-edma-crossbar",
-		.data = (void *)TI_XBAR_AM335X,
+		.data = &ti_xbar_type[TI_XBAR_AM335X],
 	},
 	{},
 };
@@ -190,9 +194,6 @@ static int ti_am335x_xbar_probe(struct platform_device *pdev)
 #define TI_DRA7_XBAR_OUTPUTS	127
 #define TI_DRA7_XBAR_INPUTS	256
 
-#define TI_XBAR_EDMA_OFFSET	0
-#define TI_XBAR_SDMA_OFFSET	1
-
 struct ti_dra7_xbar_data {
 	void __iomem *iomem;
 
@@ -280,18 +281,25 @@ static void *ti_dra7_xbar_route_allocate(struct of_phandle_args *dma_spec,
 	return map;
 }
 
+#define TI_XBAR_EDMA_OFFSET	0
+#define TI_XBAR_SDMA_OFFSET	1
+static const u32 ti_dma_offset[] = {
+	[TI_XBAR_EDMA_OFFSET] = 0,
+	[TI_XBAR_SDMA_OFFSET] = 1,
+};
+
 static const struct of_device_id ti_dra7_master_match[] = {
 	{
 		.compatible = "ti,omap4430-sdma",
-		.data = (void *)TI_XBAR_SDMA_OFFSET,
+		.data = &ti_dma_offset[TI_XBAR_SDMA_OFFSET],
 	},
 	{
 		.compatible = "ti,edma3",
-		.data = (void *)TI_XBAR_EDMA_OFFSET,
+		.data = &ti_dma_offset[TI_XBAR_EDMA_OFFSET],
 	},
 	{
 		.compatible = "ti,edma3-tpcc",
-		.data = (void *)TI_XBAR_EDMA_OFFSET,
+		.data = &ti_dma_offset[TI_XBAR_EDMA_OFFSET],
 	},
 	{},
 };
@@ -395,7 +403,7 @@ static int ti_dra7_xbar_probe(struct platform_device *pdev)
 
 	xbar->dmarouter.dev = &pdev->dev;
 	xbar->dmarouter.route_free = ti_dra7_xbar_free;
-	xbar->dma_offset = (u32)match->data;
+	xbar->dma_offset = *(u32*)match->data;
 
 	mutex_init(&xbar->mutex);
 	platform_set_drvdata(pdev, xbar);
@@ -428,7 +436,7 @@ static int ti_dma_xbar_probe(struct platform_device *pdev)
 	if (unlikely(!match))
 		return -EINVAL;
 
-	switch ((u32)match->data) {
+	switch (*(u32*)match->data) {
 	case TI_XBAR_DRA7:
 		ret = ti_dra7_xbar_probe(pdev);
 		break;
-- 
2.10.0

^ permalink raw reply related

* [PATCH v3 5/9] dmaengine: ti-dma-crossbar: Correct type for of_find_property() third parameter
From: Peter Ujfalusi @ 2016-09-21 12:41 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160921124135.11849-1-peter.ujfalusi@ti.com>

The correct type is int and not for the third parameter of
of_find_property().
Fixes compilation for 64bit architectures (x86_64, aarch64).

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
---
 drivers/dma/ti-dma-crossbar.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/dma/ti-dma-crossbar.c b/drivers/dma/ti-dma-crossbar.c
index 5ae294b256a7..e4f3bd1ae264 100644
--- a/drivers/dma/ti-dma-crossbar.c
+++ b/drivers/dma/ti-dma-crossbar.c
@@ -311,7 +311,7 @@ static int ti_dra7_xbar_probe(struct platform_device *pdev)
 	struct property *prop;
 	struct resource *res;
 	u32 safe_val;
-	size_t sz;
+	int sz;
 	void __iomem *iomem;
 	int i, ret;
 
-- 
2.10.0

^ permalink raw reply related

* [PATCH v3 4/9] dmaengine/ARM: omap-dma: Fix the DMAengine compile test on non OMAP configs
From: Peter Ujfalusi @ 2016-09-21 12:41 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160921124135.11849-1-peter.ujfalusi@ti.com>

The DMAengine driver for omap-dma use three function calls from the
plat-omap legacy driver. When the DMAengine driver is built when ARCH_OMAP
is not set, the compilation will fail due to missing symbols.
Add empty inline functions to allow the DMAengine driver to be compiled
with COMPILE_TEST.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
---
 include/linux/omap-dma.h | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/include/linux/omap-dma.h b/include/linux/omap-dma.h
index 1d99b61adc65..290081620b3e 100644
--- a/include/linux/omap-dma.h
+++ b/include/linux/omap-dma.h
@@ -297,6 +297,7 @@ struct omap_system_dma_plat_info {
 #define dma_omap15xx()	__dma_omap15xx(d)
 #define dma_omap16xx()	__dma_omap16xx(d)
 
+#if defined(CONFIG_ARCH_OMAP)
 extern struct omap_system_dma_plat_info *omap_get_plat_info(void);
 
 extern void omap_set_dma_priority(int lch, int dst_port, int priority);
@@ -355,4 +356,22 @@ static inline int omap_lcd_dma_running(void)
 }
 #endif
 
+#else /* CONFIG_ARCH_OMAP */
+
+static inline struct omap_system_dma_plat_info *omap_get_plat_info(void)
+{
+	return NULL;
+}
+
+static inline int omap_request_dma(int dev_id, const char *dev_name,
+			void (*callback)(int lch, u16 ch_status, void *data),
+			void *data, int *dma_ch)
+{
+	return -ENODEV;
+}
+
+static inline void omap_free_dma(int ch) { }
+
+#endif /* CONFIG_ARCH_OMAP */
+
 #endif /* __LINUX_OMAP_DMA_H */
-- 
2.10.0

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox