Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 3/3] spi: pca2xx-pci: Allow MSI
From: Jan Kiszka @ 2017-01-16 18:44 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cover.1484592296.git.jan.kiszka@siemens.com>

Now that the core is ready for edge-triggered interrupts, we can safely
allow the PCI versions that provide this to enable the feature and,
thus, have less shared interrupts.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
---
 drivers/spi/spi-pxa2xx-pci.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c
index 58d2d48..58dcadb 100644
--- a/drivers/spi/spi-pxa2xx-pci.c
+++ b/drivers/spi/spi-pxa2xx-pci.c
@@ -203,10 +203,16 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev,
 	ssp = &spi_pdata.ssp;
 	ssp->phys_base = pci_resource_start(dev, 0);
 	ssp->mmio_base = pcim_iomap_table(dev)[0];
-	ssp->irq = dev->irq;
 	ssp->port_id = (c->port_id >= 0) ? c->port_id : dev->devfn;
 	ssp->type = c->type;
 
+	pci_set_master(dev);
+
+	ret = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_ALL_TYPES);
+	if (ret < 0)
+		return ret;
+	ssp->irq = pci_irq_vector(dev, 0);
+
 	snprintf(buf, sizeof(buf), "pxa2xx-spi.%d", ssp->port_id);
 	ssp->clk = clk_register_fixed_rate(&dev->dev, buf , NULL, 0,
 					   c->max_clk_rate);
-- 
2.1.4

^ permalink raw reply related

* [PATCH 03/10] devicetree: bindings: add bindings for ahci-da850
From: David Lechner @ 2017-01-16 18:47 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAMpxmJW9ns-aG=4zYHcV5-2b_TdmJ7q+Ap1jdyoxstZjWYRL2A@mail.gmail.com>

On 01/16/2017 08:30 AM, Bartosz Golaszewski wrote:
> 2017-01-16 13:45 GMT+01:00 Sekhar Nori <nsekhar@ti.com>:
>> On Monday 16 January 2017 03:43 PM, Bartosz Golaszewski wrote:
>>> 2017-01-13 20:25 GMT+01:00 David Lechner <david@lechnology.com>:
>>>>
>>>> A clock multiplier property seems redundant if you are specifying a clock.
>>>> It should be possible to get the rate from the clock to determine which
>>>> multiplier is needed.
>>>>
>>>
>>> I probably should have named it differently. This is not a multiplier
>>> of a clock derived from PLL0 or PLL1. Instead it's a value set by
>>> writing to the Port PHY Control Register (MPY bits) of the SATA
>>> controller that configures the multiplier for the external low-jitter
>>> clock. On the lcdk the signals (REFCLKP, REFCLKN) are provided by
>>> CDCM61001 (SATA OSCILLATOR component on the schematics).
>>>
>>> I'll find a better name and comment the property accordingly.
>>>
>>> FYI: the da850 platform does not use the common clock framework, so I
>>> don't specify the clock property on the sata node in the device tree.
>>> Instead I add the clock lookup entry in patch [01/10]. This is
>>> transparent for AHCI which can get the clock as usual by calling
>>> clk_get() in ahci_platform_get_resources().
>>
>> I think David's point is that the SATA_REFCLK needs to be modeled as a
>> actual clock input to the IP. You should be able to get the rate using
>> clk_get_rate() and make the MPY bits calculation depending on the
>> incoming rate.
>>
>> You should be able to model the clock even when not using common clock
>> framework.
>>
>> DA850 AHCI does not use a con_id at the moment (it assumes a single
>> clock), and that needs to change.
>>
>
> It's true that once davinci gets ported (is this planned?) to using
> the common clock framework, we could just create a fixed-clock node in
> da850-lcdk for the SATA oscillator, so the new property is redundant.
>

I have some commits[1] where I started on converting da850 to use the 
common clock framework. But, I don't know anything about other davinci 
family devices, so I don't think I could really take that to completion 
without lots of help.

[1]: https://github.com/dlech/ev3dev-kernel/commits/wip-20160509

> What I don't get is how should I model a clock that is not
> configurable and is board-specific? Is hard-coding the relevant rate
> in da850.c with a huge FIXME the right way?

In arch/arm/mach-davinci/usb-da8xx.c, there is a "usb_refclkin" that is 
very similar to the situation with the sata refclk. You could do 
something like this to register the clock...

---

diff --git a/arch/arm/mach-davinci/devices-da8xx.c 
b/arch/arm/mach-davinci/devices-da8xx.c
index c2457b3..790efce9 100644
--- a/arch/arm/mach-davinci/devices-da8xx.c
+++ b/arch/arm/mach-davinci/devices-da8xx.c
@@ -1023,6 +1023,34 @@ int __init da8xx_register_spi_bus(int instance, 
unsigned num_chipselect)
  }

  #ifdef CONFIG_ARCH_DAVINCI_DA850
+
+static struct clk sata_refclkin = {
+       .name           = "sata_refclkin",
+       .set_rate       = davinci_simple_set_rate,
+};
+
+static struct clk_lookup sata_refclkin_lookup =
+       CLK(NULL, "sata_refclkin", &sata_refclkin);
+
+/**
+ * da8xx_register_sata_refclkin - register SATA_REFCLKIN clock
+ *
+ * @rate: The clock rate in Hz
+ */
+int __init da850_register_sata_refclkin(int rate)
+{
+       int ret;
+
+       sata_refclkin.rate = rate;
+       ret = clk_register(&sata_refclkin);
+       if (ret)
+               return ret;
+
+       clkdev_add(&sata_refclkin_lookup);
+
+       return 0;
+}
+
  static struct resource da850_sata_resources[] = {
         {
                 .start  = DA850_SATA_BASE,
@@ -1055,8 +1083,11 @@ static struct platform_device da850_sata_device = {

  int __init da850_register_sata(unsigned long refclkpn)
  {
-       /* please see comment in drivers/ata/ahci_da850.c */
-       BUG_ON(refclkpn != 100 * 1000 * 1000);
+       int err;
+
+       err = da850_register_sata_refclkin(refclkpn);
+       if (err)
+               return err;

         return platform_device_register(&da850_sata_device);
  }

---

Then to get things working from device tree, add this...

---

diff --git a/arch/arm/mach-davinci/da8xx-dt.c 
b/arch/arm/mach-davinci/da8xx-dt.c
index d2be194..b54bdd6 100644
--- a/arch/arm/mach-davinci/da8xx-dt.c
+++ b/arch/arm/mach-davinci/da8xx-dt.c
@@ -60,6 +60,14 @@ static void __init da850_init_machine(void)
                 pr_warn("%s: registering USB 1.1 PHY clock failed: %d",
                         __func__, ret);

+       if (of_machine_is_compatible("ti,da850-evm") ||
+           of_machine_is_compatible("ti,da850-lcdk")) {
+               ret = da850_register_sata_refclkin(100000000);
+               if (ret)
+                       pr_warn("%s: registering SATA_REFCLK clock 
failed: %d",
+                               __func__, ret);
+       }
+
         of_platform_default_populate(NULL, da850_auxdata_lookup, NULL);
         davinci_pm_init();
         pdata_quirks_init();

---

^ permalink raw reply related

* [PATCH 1/2] dt-bindings: gpu: Add Mali Utgard bindings
From: Krzysztof Kozlowski @ 2017-01-16 18:49 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170116132424.7038-1-maxime.ripard@free-electrons.com>

On Mon, Jan 16, 2017 at 02:24:23PM +0100, Maxime Ripard wrote:
> The ARM Mali Utgard GPU family is embedded into a number of SoCs from
> Allwinner, Amlogic, Mediatek or Rockchip.
> 
> Add a binding for the GPU of that family.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  .../devicetree/bindings/gpu/arm,mali-utgard.txt    | 76 ++++++++++++++++++++++
>  1 file changed, 76 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/gpu/arm,mali-utgard.txt
> 

Hi,

Do you have a driver in kernel which will implement these bindings?

Defining them for out-of-tree driver does not bring any benefits (3rd
party driver will not respect them anyway).

Best regards,
Krzysztof

> diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-utgard.txt b/Documentation/devicetree/bindings/gpu/arm,mali-utgard.txt
> new file mode 100644
> index 000000000000..df05ba0ec357
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/gpu/arm,mali-utgard.txt
> @@ -0,0 +1,76 @@
> +ARM Mali Utgard GPU
> +===================
> +
> +Required properties:
> +  - compatible:
> +    * "arm,mali-utgard" and one of the following:
> +      + "arm,mali-300"
> +      + "arm,mali-400"
> +      + "arm,mali-450"
> +
> +  - reg: Physical base address and length of the GPU registers
> +
> +  - interrupts: an entry for each entry in interrupt-names.
> +    See ../interrupt-controller/interrupts.txt for details.
> +
> +  - interrupt-names:
> +    * ppX: Pixel Processor X interrupt (X from 0 to 7)
> +    * ppmmuX: Pixel Processor X MMU interrupt (X from 0 to 7)
> +    * pp: Pixel Processor broadcast interrupt (mali-450 only)
> +    * gp: Geometry Processor interrupt
> +    * gpmmu: Geometry Processor MMU interrupt
> +
> +
> +Optional properties:
> +  - interrupt-names:
> +    * pmu: Power Management Unit interrupt, if implemented in hardware
> +
> +Vendor-specific bindings
> +------------------------
> +
> +The Mali GPU is integrated very differently from one SoC to
> +another. In order to accommodate those differences, you have the option
> +to specify one more vendor-specific compatible, among:
> +
> +  - allwinner,sun4i-a10-mali
> +    Required properties:
> +      * clocks: an entry for each entry in clock-names
> +      * clock-names:
> +        + bus: bus clock for the GPU
> +        + core: clock driving the GPU itself
> +      * resets: phandle to the reset line for the GPU
> +
> +  - allwinner,sun7i-a20-mali
> +    Required properties:
> +      * clocks: an entry for each entry in clock-names
> +      * clock-names:
> +        + bus: bus clock for the GPU
> +        + core: clock driving the GPU itself
> +      * resets: phandle to the reset line for the GPU
> +
> +Example:
> +
> +mali: gpu at 01c40000 {
> +	compatible = "allwinner,sun7i-a20-mali", "arm,mali-400",
> +		     "arm,mali-utgard";
> +	reg = <0x01c40000 0x10000>;
> +	interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>,
> +		     <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>,
> +		     <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>,
> +		     <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>,
> +		     <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>,
> +		     <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>,
> +		     <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>;
> +	interrupt-names = "gp",
> +			  "gpmmu",
> +			  "pp0",
> +			  "ppmmu0",
> +			  "pp1",
> +			  "ppmmu1",
> +			  "pmu";
> +	clocks = <&ccu CLK_BUS_GPU>, <&ccu CLK_GPU>;
> +	clock-names = "bus", "core";
> +	resets = <&ccu RST_BUS_GPU>;
> +};
> +
> +
> -- 
> 2.11.0
> 

^ permalink raw reply

* [PATCH v8] perf: add qcom l2 cache perf events driver
From: Neil Leeder @ 2017-01-16 18:52 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 on Qualcomm Technologies processors.

Signed-off-by: Neil Leeder <nleeder@codeaurora.org>
---
v8:
Various style changes for function names & code restructuring
Replace dev_warn with ratelimited debug prints
Move hotplug registration before PMU registration
Reload counters with a fixed value
Add column-exclusion check for events in same group
Rebase on 4.10-rc3

v7:
Move to drivers/perf
Rebased on 4.9-rc1
Ran perf fuzzer against driver for 4 hours with no crashes 

v6: restore accidentally dropped Kconfig dependencies

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/perf/Kconfig       |    9 +
 drivers/perf/Makefile      |    1 +
 drivers/perf/qcom_l2_pmu.c | 1001 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/cpuhotplug.h |    1 +
 4 files changed, 1012 insertions(+)
 create mode 100644 drivers/perf/qcom_l2_pmu.c

diff --git a/drivers/perf/Kconfig b/drivers/perf/Kconfig
index 4d5c5f9..9365190 100644
--- a/drivers/perf/Kconfig
+++ b/drivers/perf/Kconfig
@@ -12,6 +12,15 @@ config ARM_PMU
 	  Say y if you want to use CPU performance monitors on ARM-based
 	  systems.
 
+config QCOM_L2_PMU
+	bool "Qualcomm Technologies L2-cache PMU"
+	depends on ARCH_QCOM && ARM64 && PERF_EVENTS && ACPI
+	  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 XGENE_PMU
         depends on PERF_EVENTS && ARCH_XGENE
         bool "APM X-Gene SoC PMU"
diff --git a/drivers/perf/Makefile b/drivers/perf/Makefile
index b116e98..ef24833 100644
--- a/drivers/perf/Makefile
+++ b/drivers/perf/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_ARM_PMU) += arm_pmu.o
+obj-$(CONFIG_QCOM_L2_PMU)	+= qcom_l2_pmu.o
 obj-$(CONFIG_XGENE_PMU) += xgene_pmu.o
diff --git a/drivers/perf/qcom_l2_pmu.c b/drivers/perf/qcom_l2_pmu.c
new file mode 100644
index 0000000..407ca9a
--- /dev/null
+++ b/drivers/perf/qcom_l2_pmu.c
@@ -0,0 +1,1001 @@
+/* Copyright (c) 2015-2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/acpi.h>
+#include <linux/bitops.h>
+#include <linux/bug.h>
+#include <linux/cpuhotplug.h>
+#include <linux/cpumask.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/percpu.h>
+#include <linux/perf_event.h>
+#include <linux/platform_device.h>
+#include <linux/smp.h>
+#include <linux/spinlock.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+
+#include <asm/barrier.h>
+#include <asm/local64.h>
+#include <asm/sysreg.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 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_COUNTER_RELOAD       BIT_ULL(31)
+#define L2_CYCLE_COUNTER_RELOAD BIT_ULL(63)
+
+#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 cluster_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 cluster_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 cluster_pmu *get_cluster_pmu(int cpu)
+{
+	return per_cpu(pmu_cluster, cpu);
+}
+
+static void cluster_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 cluster_pmu_reset(struct cluster_pmu *cluster)
+{
+	cpumask_t *mask = &cluster->cluster_cpus;
+
+	if (smp_call_function_any(mask, cluster_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 cluster_pmu_enable(void)
+{
+	set_l2_indirect_reg(L2PMCR, L2PMCR_COUNTERS_ENABLE);
+}
+
+static inline void cluster_pmu_disable(void)
+{
+	set_l2_indirect_reg(L2PMCR, L2PMCR_COUNTERS_DISABLE);
+}
+
+static inline void cluster_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 cluster_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 cluster_pmu_counter_enable(u32 idx)
+{
+	set_l2_indirect_reg(L2PMCNTENSET, idx_to_reg_bit(idx));
+}
+
+static inline void cluster_pmu_counter_disable(u32 idx)
+{
+	set_l2_indirect_reg(L2PMCNTENCLR, idx_to_reg_bit(idx));
+}
+
+static inline void cluster_pmu_counter_enable_interrupt(u32 idx)
+{
+	set_l2_indirect_reg(L2PMINTENSET, idx_to_reg_bit(idx));
+}
+
+static inline void cluster_pmu_counter_disable_interrupt(u32 idx)
+{
+	set_l2_indirect_reg(L2PMINTENCLR, idx_to_reg_bit(idx));
+}
+
+static inline void cluster_pmu_set_evccntcr(u32 val)
+{
+	set_l2_indirect_reg(L2PMCCNTCR, val);
+}
+
+static inline void cluster_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 cluster_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 cluster_pmu_set_resr(struct cluster_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);
+
+	spin_lock_irqsave(&cluster->pmu_lock, flags);
+
+	resr_val = get_l2_indirect_reg(L2PMRESR);
+	resr_val &= ~(L2PMRESR_GROUP_MASK << shift);
+	resr_val |= field;
+	resr_val |= L2PMRESR_EN;
+	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 cluster_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 cluster_pmu_getreset_ovsr(void)
+{
+	u32 result = get_l2_indirect_reg(L2PMOVSSET);
+
+	set_l2_indirect_reg(L2PMOVSCLR, result);
+	return result;
+}
+
+static inline bool cluster_pmu_has_overflowed(u32 ovsr)
+{
+	return !!(ovsr & l2_counter_present_mask);
+}
+
+static inline bool cluster_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 cluster_pmu *cluster)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	u64 delta, prev, now;
+	u32 idx = hwc->idx;
+
+	do {
+		prev = local64_read(&hwc->prev_count);
+		now = cluster_pmu_counter_get_value(idx);
+	} while (local64_cmpxchg(&hwc->prev_count, prev, now) != prev);
+
+	/*
+	 * The cycle counter is 64-bit, but all other counters are
+	 * 32-bit, and we must handle 32-bit overflow explicitly.
+	 */
+	delta = now - prev;
+	if (idx != l2_cycle_ctr_idx)
+		delta &= 0xffffffff;
+
+	local64_add(delta, &event->count);
+}
+
+static void l2_cache_cluster_set_period(struct cluster_pmu *cluster,
+				       struct hw_perf_event *hwc)
+{
+	u32 idx = hwc->idx;
+	u64 new;
+
+	/*
+	 * We limit the max period to half the max counter value so
+	 * that even in the case of extreme interrupt latency the
+	 * counter will (hopefully) not wrap past its initial value.
+	 */
+	if (idx == l2_cycle_ctr_idx)
+		new = L2_CYCLE_COUNTER_RELOAD;
+	else
+		new = L2_COUNTER_RELOAD;
+
+	local64_set(&hwc->prev_count, new);
+	cluster_pmu_counter_set_value(idx, new);
+}
+
+static int l2_cache_get_event_idx(struct cluster_pmu *cluster,
+				   struct perf_event *event)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	int idx;
+	int num_ctrs = cluster->l2cache_pmu->num_counters - 1;
+
+	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;
+	}
+
+	idx = find_first_zero_bit(cluster->used_counters, num_ctrs);
+	if (idx == num_ctrs)
+		/* The counters are all in use. */
+		return -EAGAIN;
+
+	set_bit(idx, cluster->used_counters);
+	set_bit(L2_EVT_GROUP(hwc->config_base), cluster->used_groups);
+
+	return idx;
+}
+
+static void l2_cache_clear_event_idx(struct cluster_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 cluster_pmu *cluster = data;
+	int num_counters = cluster->l2cache_pmu->num_counters;
+	u32 ovsr;
+	int idx;
+
+	ovsr = cluster_pmu_getreset_ovsr();
+	if (!cluster_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 (!cluster_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.
+	 */
+
+	cluster_pmu_enable();
+}
+
+static void l2_cache_pmu_disable(struct pmu *pmu)
+{
+	cluster_pmu_disable();
+}
+
+static int l2_cache_event_init(struct perf_event *event)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	struct cluster_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_dbg_ratelimited(&l2cache_pmu->pdev->dev,
+				    "Sampling not supported\n");
+		return -EOPNOTSUPP;
+	}
+
+	if (event->cpu < 0) {
+		dev_dbg_ratelimited(&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_dbg_ratelimited(&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_dbg_ratelimited(&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_dbg_ratelimited(&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_dbg_ratelimited(&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_cluster_pmu(event->cpu);
+	if ((event->group_leader != event) &&
+	    (cluster->on_cpu != event->group_leader->cpu)) {
+		dev_dbg_ratelimited(&l2cache_pmu->pdev->dev,
+			 "Can't create group on CPUs %d and %d",
+			 event->cpu, event->group_leader->cpu);
+		return -EINVAL;
+	}
+
+	if ((event != event->group_leader) &&
+	    (L2_EVT_GROUP(event->group_leader->attr.config) ==
+	     L2_EVT_GROUP(event->attr.config))) {
+		dev_dbg_ratelimited(&l2cache_pmu->pdev->dev,
+			 "Column exclusion: conflicting events %llx %llx\n",
+		       event->group_leader->attr.config,
+		       event->attr.config);
+		return -EINVAL;
+	}
+
+	list_for_each_entry(sibling, &event->group_leader->sibling_list,
+			    group_entry) {
+		if ((sibling != event) &&
+		    (L2_EVT_GROUP(sibling->attr.config) ==
+		     L2_EVT_GROUP(event->attr.config))) {
+			dev_dbg_ratelimited(&l2cache_pmu->pdev->dev,
+			     "Column exclusion: conflicting events %llx %llx\n",
+					    sibling->attr.config,
+					    event->attr.config);
+			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 cluster_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_cluster_pmu(event->cpu);
+	l2_cache_cluster_set_period(cluster, hwc);
+
+	if (hwc->config_base == L2CYCLE_CTR_RAW_CODE) {
+		cluster_pmu_set_evccntcr(0);
+	} else {
+		config = hwc->config_base;
+		event_cc    = L2_EVT_CODE(config);
+		event_group = L2_EVT_GROUP(config);
+
+		cluster_pmu_set_evcntcr(idx, 0);
+		cluster_pmu_set_evtyper(idx, event_group);
+		cluster_pmu_set_resr(cluster, event_group, event_cc);
+		cluster_pmu_set_evfilter_sys_mode(idx);
+	}
+
+	cluster_pmu_counter_enable_interrupt(idx);
+	cluster_pmu_counter_enable(idx);
+}
+
+static void l2_cache_event_stop(struct perf_event *event, int flags)
+{
+	struct cluster_pmu *cluster;
+	struct hw_perf_event *hwc = &event->hw;
+	int idx = hwc->idx;
+
+	if (hwc->state & PERF_HES_STOPPED)
+		return;
+
+	cluster = get_cluster_pmu(event->cpu);
+	cluster_pmu_counter_disable_interrupt(idx);
+	cluster_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 cluster_pmu *cluster;
+
+	cluster = get_cluster_pmu(event->cpu);
+
+	idx = l2_cache_get_event_idx(cluster, event);
+	if (idx < 0)
+		return idx;
+
+	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, 0);
+
+	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 cluster_pmu *cluster;
+	int idx = hwc->idx;
+
+	cluster = get_cluster_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_cluster_pmu(event->cpu));
+}
+
+static int l2_cache_filter_match(struct perf_event *event)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	struct hw_perf_event *chwc;
+	struct cluster_pmu *cluster = get_cluster_pmu(event->cpu);
+	struct l2cache_pmu *l2cache_pmu;
+	struct perf_event *conflict_event;
+	unsigned int group = L2_EVT_GROUP(hwc->config_base);
+
+	/*
+	 * Check for column exclusion: event column already in use by another
+	 * event. This is for events which are not in the same group.
+	 * Conflicting events in the same group are detected in event_init.
+	 */
+
+	if (test_bit(group, cluster->used_groups)) {
+		conflict_event =
+			cluster->events[cluster->group_to_counter[group]];
+		if (conflict_event != event) {
+			l2cache_pmu = to_l2cache_pmu(event->pmu);
+			chwc = &conflict_event->hw;
+			dev_dbg_ratelimited(&l2cache_pmu->pdev->dev,
+				    "column exclusion between events %lx %lx\n",
+				    hwc->config_base, chwc->config_base);
+			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 cluster_pmu *cluster;
+	cpumask_t cluster_online_cpus;
+	struct l2cache_pmu *l2cache_pmu;
+
+	l2cache_pmu = hlist_entry_safe(node, struct l2cache_pmu, node);
+	cluster = get_cluster_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 cluster_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_cluster_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 cluster_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 | IRQF_NO_THREAD,
+			       "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));
+
+	spin_lock_init(&cluster->pmu_lock);
+	cpumask_set_cpu(cluster->on_cpu, &l2cache_pmu->cpumask);
+
+	cluster_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) |
+		BIT(L2CYCLE_CTR_BIT);
+
+	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 = cpuhp_state_add_instance_nocalls(CPUHP_AP_PERF_ARM_QCOM_L2_ONLINE,
+					       &l2cache_pmu->node);
+	if (err) {
+		dev_err(&pdev->dev, "Error %d registering hotplug", err);
+		return err;
+	}
+
+	err = perf_pmu_register(&l2cache_pmu->pmu, l2cache_pmu->pmu.name, -1);
+	if (err) {
+		dev_err(&pdev->dev, "Error %d registering L2 cache PMU\n", err);
+		goto out_unregister;
+	}
+
+	dev_info(&pdev->dev, "Registered L2 cache PMU using %d HW PMUs\n",
+		 l2cache_pmu->num_pmus);
+
+	return err;
+
+out_unregister:
+	cpuhp_state_remove_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));
+
+	perf_pmu_unregister(&l2cache_pmu->pmu);
+	cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_QCOM_L2_ONLINE,
+					    &l2cache_pmu->node);
+	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 20bfefb..1b7b207 100644
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -138,6 +138,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_ONLINE_DYN,
-- 
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 04/12] pinctrl: samsung: Document Exynos3250 SoC support
From: Krzysztof Kozlowski @ 2017-01-16 19:01 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1484549107-5957-5-git-send-email-m.szyprowski@samsung.com>

On Mon, Jan 16, 2017 at 07:44:59AM +0100, Marek Szyprowski wrote:
> Add missing compatible id for Exynos3250 SoC to device tree docs.
> Exynos pin control driver supports it since commit d97f5b9804bfcdc1
> ("pinctrl: exynos: Add driver data for Exynos3250").
> 
> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> ---
>  Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt | 1 +
>  1 file changed, 1 insertion(+)
> 

Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org>

Best regards,
Krzysztof

^ permalink raw reply

* [PATCH 05/12] pinctrl: samsung: Remove messages for failed memory allocation
From: Krzysztof Kozlowski @ 2017-01-16 19:04 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1484549107-5957-6-git-send-email-m.szyprowski@samsung.com>

On Mon, Jan 16, 2017 at 07:45:00AM +0100, Marek Szyprowski wrote:
> Memory subsystem already prints message about failed memory
> allocation, there is no need to do it in the drivers.
> 
> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> ---
>  drivers/pinctrl/samsung/pinctrl-exynos.c  |  8 ++------
>  drivers/pinctrl/samsung/pinctrl-s3c64xx.c | 12 +++---------
>  drivers/pinctrl/samsung/pinctrl-samsung.c | 24 ++++++------------------
>  3 files changed, 11 insertions(+), 33 deletions(-)
> 
> diff --git a/drivers/pinctrl/samsung/pinctrl-exynos.c b/drivers/pinctrl/samsung/pinctrl-exynos.c
> index 24814db251a7..bf753a596209 100644
> --- a/drivers/pinctrl/samsung/pinctrl-exynos.c
> +++ b/drivers/pinctrl/samsung/pinctrl-exynos.c
> @@ -528,10 +528,8 @@ static int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d)
>  
>  		weint_data = devm_kzalloc(dev, bank->nr_pins
>  					* sizeof(*weint_data), GFP_KERNEL);
> -		if (!weint_data) {
> -			dev_err(dev, "could not allocate memory for weint_data\n");
> +		if (!weint_data)
>  			return -ENOMEM;
> -		}
>  
>  		for (idx = 0; idx < bank->nr_pins; ++idx) {
>  			irq = irq_of_parse_and_map(bank->of_node, idx);
> @@ -559,10 +557,8 @@ static int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d)
>  
>  	muxed_data = devm_kzalloc(dev, sizeof(*muxed_data)
>  		+ muxed_banks*sizeof(struct samsung_pin_bank *), GFP_KERNEL);
> -	if (!muxed_data) {
> -		dev_err(dev, "could not allocate memory for muxed_data\n");
> +	if (!muxed_data)
>  		return -ENOMEM;
> -	}
>  
>  	irq_set_chained_handler_and_data(irq, exynos_irq_demux_eint16_31,
>  					 muxed_data);
> diff --git a/drivers/pinctrl/samsung/pinctrl-s3c64xx.c b/drivers/pinctrl/samsung/pinctrl-s3c64xx.c
> index 4c632812ccff..f17890aa6e25 100644
> --- a/drivers/pinctrl/samsung/pinctrl-s3c64xx.c
> +++ b/drivers/pinctrl/samsung/pinctrl-s3c64xx.c
> @@ -489,10 +489,8 @@ static int s3c64xx_eint_gpio_init(struct samsung_pinctrl_drv_data *d)
>  
>  	data = devm_kzalloc(dev, sizeof(*data)
>  			+ nr_domains * sizeof(*data->domains), GFP_KERNEL);
> -	if (!data) {
> -		dev_err(dev, "failed to allocate handler data\n");
> +	if (!data)
>  		return -ENOMEM;
> -	}
>  	data->drvdata = d;
>  
>  	bank = d->pin_banks;
> @@ -715,10 +713,8 @@ static int s3c64xx_eint_eint0_init(struct samsung_pinctrl_drv_data *d)
>  		return -ENODEV;
>  
>  	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
> -	if (!data) {
> -		dev_err(dev, "could not allocate memory for wkup eint data\n");
> +	if (!data)
>  		return -ENOMEM;
> -	}
>  	data->drvdata = d;
>  
>  	for (i = 0; i < NUM_EINT0_IRQ; ++i) {
> @@ -751,10 +747,8 @@ static int s3c64xx_eint_eint0_init(struct samsung_pinctrl_drv_data *d)
>  
>  		ddata = devm_kzalloc(dev,
>  				sizeof(*ddata) + nr_eints, GFP_KERNEL);
> -		if (!ddata) {
> -			dev_err(dev, "failed to allocate domain data\n");
> +		if (!ddata)
>  			return -ENOMEM;
> -		}
>  		ddata->bank = bank;
>  
>  		bank->irq_domain = irq_domain_add_linear(bank->of_node,
> diff --git a/drivers/pinctrl/samsung/pinctrl-samsung.c b/drivers/pinctrl/samsung/pinctrl-samsung.c
> index 3bc925f61b71..b11e67e85460 100644
> --- a/drivers/pinctrl/samsung/pinctrl-samsung.c
> +++ b/drivers/pinctrl/samsung/pinctrl-samsung.c
> @@ -93,10 +93,8 @@ static int reserve_map(struct device *dev, struct pinctrl_map **map,
>  		return 0;
>  
>  	new_map = krealloc(*map, sizeof(*new_map) * new_num, GFP_KERNEL);
> -	if (!new_map) {
> -		dev_err(dev, "krealloc(map) failed\n");
> +	if (!new_map)
>  		return -ENOMEM;
> -	}
>  
>  	memset(new_map + old_num, 0, (new_num - old_num) * sizeof(*new_map));
>  
> @@ -133,10 +131,8 @@ static int add_map_configs(struct device *dev, struct pinctrl_map **map,
>  
>  	dup_configs = kmemdup(configs, num_configs * sizeof(*dup_configs),
>  			      GFP_KERNEL);
> -	if (!dup_configs) {
> -		dev_err(dev, "kmemdup(configs) failed\n");
> +	if (!dup_configs)
>  		return -ENOMEM;
> -	}
>  
>  	(*map)[*num_maps].type = PIN_MAP_TYPE_CONFIGS_GROUP;
>  	(*map)[*num_maps].data.configs.group_or_pin = group;
> @@ -156,10 +152,8 @@ static int add_config(struct device *dev, unsigned long **configs,
>  
>  	new_configs = krealloc(*configs, sizeof(*new_configs) * new_num,
>  			       GFP_KERNEL);
> -	if (!new_configs) {
> -		dev_err(dev, "krealloc(configs) failed\n");
> +	if (!new_configs)
>  		return -ENOMEM;
> -	}
>  
>  	new_configs[old_num] = config;
>  
> @@ -756,10 +750,8 @@ static struct samsung_pmx_func *samsung_pinctrl_create_functions(
>  
>  	functions = devm_kzalloc(dev, func_cnt * sizeof(*functions),
>  					GFP_KERNEL);
> -	if (!functions) {
> -		dev_err(dev, "failed to allocate memory for function list\n");
> +	if (!functions)
>  		return ERR_PTR(-EINVAL);

Out of scope of this patch - why are we returning EINVAL instead of
ENOMEM?

Regardless:
Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org>

Best regards,
Krzysztof

^ permalink raw reply

* [PATCH v2 2/3] spi: pxa2xx: Prepare for edge-triggered interrupts
From: Andy Shevchenko @ 2017-01-16 19:07 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <7b15a0910a3ad861fd32161c72559bafa7b71e29.1484592296.git.jan.kiszka@siemens.com>

On Mon, 2017-01-16 at 19:44 +0100, Jan Kiszka wrote:
> When using the a device with edge-triggered interrupts, such as MSIs,
> the interrupt handler has to ensure that there is a point in time
> during
> its execution where all interrupts sources are silent so that a new
> event can trigger a new interrupt again.
> 
> This is achieved here by looping over SSSR evaluation. We need to take
> into account that SSCR1 may be changed by the transfer handler, thus
> we
> need to redo the mask calculation, at least regarding the volatile
> interrupt enable bit (TIE).
> 

So, more comments/questions below.

> ?
> ?	sccr1_reg = pxa2xx_spi_read(drv_data, SSCR1);
> ?
> -	/* Ignore possible writes if we don't need to write */
> -	if (!(sccr1_reg & SSCR1_TIE))
> -		mask &= ~SSSR_TFS;
> -
> ?	/* Ignore RX timeout interrupt if it is disabled */
> ?	if (!(sccr1_reg & SSCR1_TINTE))
> ?		mask &= ~SSSR_TINT;
> ?
> -	if (!(status & mask))
> -		return IRQ_NONE;
> +	while (1) {

Can we switch to do-while and move previous block here? Btw, can TINTE
bit be set again during a loop?

> +		/* Ignore possible writes if we don't need to write
> */
> +		if (!(sccr1_reg & SSCR1_TIE))
> +			mask &= ~SSSR_TFS;
> ?
> -	if (!drv_data->master->cur_msg) {
> -		handle_bad_msg(drv_data);
> -		/* Never fail */
> -		return IRQ_HANDLED;
> -	}
> +		if (!(status & mask))
> +			return ret;
> +
> +		if (!drv_data->master->cur_msg) {
> +			handle_bad_msg(drv_data);
> +			/* Never fail */
> +			return IRQ_HANDLED;
> +		}
> +

> +		ret |= drv_data->transfer_handler(drv_data);

So, we might call handler several times. This needs to be commented in
the code why you do so.

> ?
> -	return drv_data->transfer_handler(drv_data);
> +		status = pxa2xx_spi_read(drv_data, SSSR);

Would it be possible to get all 1:s from the register
(something/autosuspend just powered off it by timeout?) ?

> +		sccr1_reg = pxa2xx_spi_read(drv_data, SSCR1);
> +	}
> ?}
> ?
> ?/*

-- 
Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Intel Finland Oy

^ permalink raw reply

* [PATCH v2 3/3] spi: pca2xx-pci: Allow MSI
From: Andy Shevchenko @ 2017-01-16 19:08 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <9f5bc54a5842b383dc81fa8586d6774c03d2c452.1484592296.git.jan.kiszka@siemens.com>

On Mon, 2017-01-16 at 19:44 +0100, Jan Kiszka wrote:
> Now that the core is ready for edge-triggered interrupts, we can
> safely
> allow the PCI versions that provide this to enable the feature and,
> thus, have less shared interrupts.
> 

Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>



>?drivers/spi/spi-pxa2xx-pci.c | 8 +++++++-
> ?1 file changed, 7 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-
> pci.c
> index 58d2d48..58dcadb 100644
> --- a/drivers/spi/spi-pxa2xx-pci.c
> +++ b/drivers/spi/spi-pxa2xx-pci.c
> @@ -203,10 +203,16 @@ static int pxa2xx_spi_pci_probe(struct pci_dev
> *dev,
> ?	ssp = &spi_pdata.ssp;
> ?	ssp->phys_base = pci_resource_start(dev, 0);
> ?	ssp->mmio_base = pcim_iomap_table(dev)[0];
> -	ssp->irq = dev->irq;
> ?	ssp->port_id = (c->port_id >= 0) ? c->port_id : dev->devfn;
> ?	ssp->type = c->type;
> ?
> +	pci_set_master(dev);
> +
> +	ret = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_ALL_TYPES);
> +	if (ret < 0)
> +		return ret;

+ perhaps an empty line?

> +	ssp->irq = pci_irq_vector(dev, 0);
> +
> ?	snprintf(buf, sizeof(buf), "pxa2xx-spi.%d", ssp->port_id);
> ?	ssp->clk = clk_register_fixed_rate(&dev->dev, buf , NULL, 0,
?					???c->max_clk_rate);

-- 
Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Intel Finland Oy

^ permalink raw reply

* [PATCH 06/12] pinctrl: samsung: Add missing initconst annotation
From: Krzysztof Kozlowski @ 2017-01-16 19:14 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1484549107-5957-7-git-send-email-m.szyprowski@samsung.com>

On Mon, Jan 16, 2017 at 07:45:01AM +0100, Marek Szyprowski wrote:
> Exynos5433 support has been added in parallel to adding initconst
> annotation to most of the init data structures, so add those
> annotations also to Exynos5433 structures.
> 
> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> ---
>  drivers/pinctrl/samsung/pinctrl-exynos.c | 22 +++++++++++-----------
>  1 file changed, 11 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/pinctrl/samsung/pinctrl-exynos.c b/drivers/pinctrl/samsung/pinctrl-exynos.c
> index bf753a596209..70b94ad10cc1 100644
> --- a/drivers/pinctrl/samsung/pinctrl-exynos.c
> +++ b/drivers/pinctrl/samsung/pinctrl-exynos.c
> @@ -1266,7 +1266,7 @@ static void exynos_pinctrl_resume(struct samsung_pinctrl_drv_data *drvdata)
>  };
>  
>  /* pin banks of exynos5433 pin-controller - ALIVE */
> -static const struct samsung_pin_bank_data exynos5433_pin_banks0[] = {
> +static const struct samsung_pin_bank_data exynos5433_pin_banks0[] __initconst = {
>  	EXYNOS5433_PIN_BANK_EINTW(8, 0x000, "gpa0", 0x00),
>  	EXYNOS5433_PIN_BANK_EINTW(8, 0x020, "gpa1", 0x04),
>  	EXYNOS5433_PIN_BANK_EINTW(8, 0x040, "gpa2", 0x08),
> @@ -1279,28 +1279,28 @@ static void exynos_pinctrl_resume(struct samsung_pinctrl_drv_data *drvdata)
>  };

Your change is aligned with existing code... but after I started to look
into it I think this should not be initconst.

The pin_banks (initconst) are referenced in pin_ctrl (initconst) which
is referenced in samsung_pinctrl_dt_match (NOT initconst). The dt_match
then is used in samsung_pinctrl_driver (for obvious reasons not
initconst).

We suppress the bind so this looks safe - this data should not be ever used
after init - but it is not correct strictly speaking.

Let's imagine some weird future platform which will use DT overlays with
pinctrl. I think the overlays could affect the tree after the init
stage.

The other question is why the DEBUG_SECTION_MISMATCH is not complaining
about this...

Best regards,
Krzysztof

>  
>  /* pin banks of exynos5433 pin-controller - AUD */
> -static const struct samsung_pin_bank_data exynos5433_pin_banks1[] = {
> +static const struct samsung_pin_bank_data exynos5433_pin_banks1[] __initconst = {
>  	EXYNOS5433_PIN_BANK_EINTG(7, 0x000, "gpz0", 0x00),
>  	EXYNOS5433_PIN_BANK_EINTG(4, 0x020, "gpz1", 0x04),
>  };
>  
>  /* pin banks of exynos5433 pin-controller - CPIF */
> -static const struct samsung_pin_bank_data exynos5433_pin_banks2[] = {
> +static const struct samsung_pin_bank_data exynos5433_pin_banks2[] __initconst = {
>  	EXYNOS5433_PIN_BANK_EINTG(2, 0x000, "gpv6", 0x00),
>  };
>  
>  /* pin banks of exynos5433 pin-controller - eSE */
> -static const struct samsung_pin_bank_data exynos5433_pin_banks3[] = {
> +static const struct samsung_pin_bank_data exynos5433_pin_banks3[] __initconst = {
>  	EXYNOS5433_PIN_BANK_EINTG(3, 0x000, "gpj2", 0x00),
>  };
>  
>  /* pin banks of exynos5433 pin-controller - FINGER */
> -static const struct samsung_pin_bank_data exynos5433_pin_banks4[] = {
> +static const struct samsung_pin_bank_data exynos5433_pin_banks4[] __initconst = {
>  	EXYNOS5433_PIN_BANK_EINTG(4, 0x000, "gpd5", 0x00),
>  };
>  
>  /* pin banks of exynos5433 pin-controller - FSYS */
> -static const struct samsung_pin_bank_data exynos5433_pin_banks5[] = {
> +static const struct samsung_pin_bank_data exynos5433_pin_banks5[] __initconst = {
>  	EXYNOS5433_PIN_BANK_EINTG(6, 0x000, "gph1", 0x00),
>  	EXYNOS5433_PIN_BANK_EINTG(7, 0x020, "gpr4", 0x04),
>  	EXYNOS5433_PIN_BANK_EINTG(5, 0x040, "gpr0", 0x08),
> @@ -1310,17 +1310,17 @@ static void exynos_pinctrl_resume(struct samsung_pinctrl_drv_data *drvdata)
>  };
>  
>  /* pin banks of exynos5433 pin-controller - IMEM */
> -static const struct samsung_pin_bank_data exynos5433_pin_banks6[] = {
> +static const struct samsung_pin_bank_data exynos5433_pin_banks6[] __initconst = {
>  	EXYNOS5433_PIN_BANK_EINTG(8, 0x000, "gpf0", 0x00),
>  };
>  
>  /* pin banks of exynos5433 pin-controller - NFC */
> -static const struct samsung_pin_bank_data exynos5433_pin_banks7[] = {
> +static const struct samsung_pin_bank_data exynos5433_pin_banks7[] __initconst = {
>  	EXYNOS5433_PIN_BANK_EINTG(3, 0x000, "gpj0", 0x00),
>  };
>  
>  /* pin banks of exynos5433 pin-controller - PERIC */
> -static const struct samsung_pin_bank_data exynos5433_pin_banks8[] = {
> +static const struct samsung_pin_bank_data exynos5433_pin_banks8[] __initconst = {
>  	EXYNOS5433_PIN_BANK_EINTG(6, 0x000, "gpv7", 0x00),
>  	EXYNOS5433_PIN_BANK_EINTG(5, 0x020, "gpb0", 0x04),
>  	EXYNOS5433_PIN_BANK_EINTG(8, 0x040, "gpc0", 0x08),
> @@ -1341,7 +1341,7 @@ static void exynos_pinctrl_resume(struct samsung_pinctrl_drv_data *drvdata)
>  };
>  
>  /* pin banks of exynos5433 pin-controller - TOUCH */
> -static const struct samsung_pin_bank_data exynos5433_pin_banks9[] = {
> +static const struct samsung_pin_bank_data exynos5433_pin_banks9[] __initconst = {
>  	EXYNOS5433_PIN_BANK_EINTG(3, 0x000, "gpj1", 0x00),
>  };
>  
> @@ -1349,7 +1349,7 @@ static void exynos_pinctrl_resume(struct samsung_pinctrl_drv_data *drvdata)
>   * Samsung pinctrl driver data for Exynos5433 SoC. Exynos5433 SoC includes
>   * ten gpio/pin-mux/pinconfig controllers.
>   */
> -const struct samsung_pin_ctrl exynos5433_pin_ctrl[] = {
> +const struct samsung_pin_ctrl exynos5433_pin_ctrl[] __initconst = {
>  	{
>  		/* pin-controller instance 0 data */
>  		.pin_banks	= exynos5433_pin_banks0,
> -- 
> 1.9.1
> 

^ permalink raw reply

* [PATCH 0/4] Enable USB OTG on Allwinner H3 and two boards
From: Icenowy Zheng @ 2017-01-16 19:14 UTC (permalink / raw)
  To: linux-arm-kernel

This patchset depends on the following patch (only the patch, as it adds the
MUSB controller used in H3, the patchset itself is for V3s):
http://lists.infradead.org/pipermail/linux-arm-kernel/2017-January/476387.html

This patchset enables USB OTG ports on Orange Pi Zero and One board, using the
SoC's MUSB controller.

The mode of PHY0 is currently set to MUSB mode, as supporting EHCI/OHCI will
cost more time and code; but MUSB can supply both host and peripheral mode.

OTG function is only enabled for these two boards, as they're the only H3/H2+
boards that I have.

I think other boards' owners can easily enable their boards' OTG function with
these patches.

Icenowy Zheng (4):
  phy: sun4i-usb: support PHY0 on H3 in MUSB mode
  ARM: dts: sun8i: add MUSB node to H3 SoC
  ARM: dts: sun8i: enable USB OTG for Orange Pi Zero board
  ARM: dts: sun8i: enable USB OTG on Orange Pi One board

 arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts |  6 ++++++
 arch/arm/boot/dts/sun8i-h3-orangepi-one.dts       | 12 +++++++++++
 arch/arm/boot/dts/sun8i-h3.dtsi                   | 13 ++++++++++++
 drivers/phy/phy-sun4i-usb.c                       | 25 ++++++++---------------
 4 files changed, 40 insertions(+), 16 deletions(-)

-- 
2.11.0

^ permalink raw reply

* [PATCH 1/4] phy: sun4i-usb: support PHY0 on H3 in MUSB mode
From: Icenowy Zheng @ 2017-01-16 19:14 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170116191449.50397-1-icenowy@aosc.xyz>

The PHY0 on H3 can be wired either to MUSB controller or OHCI/EHCI
controller.

The original driver wired it to OHCI/EHCI controller; however, as the
code to use PHY0 as OHCI/EHCI is missing, it makes the PHY fully
unusable.

Rename the register (according to its function and the name in BSP
driver), and remove the code which wires the PHY0 to OHCI/EHCI, as MUSB
can support both peripheral and host mode (although the host mode of
MUSB is buggy).

The register that is renamed is now unused, as its initial value is just
MUSB mode. However, when OHCI/EHCI mode support is added, the register
can be used again.

Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
---
 drivers/phy/phy-sun4i-usb.c | 25 +++++++++----------------
 1 file changed, 9 insertions(+), 16 deletions(-)

diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
index bf28a0fdd569..6b193a635c6b 100644
--- a/drivers/phy/phy-sun4i-usb.c
+++ b/drivers/phy/phy-sun4i-usb.c
@@ -49,7 +49,7 @@
 #define REG_PHYBIST			0x08
 #define REG_PHYTUNE			0x0c
 #define REG_PHYCTL_A33			0x10
-#define REG_PHY_UNK_H3			0x20
+#define REG_PHY_OTGCTL			0x20
 
 #define REG_PMU_UNK1			0x10
 
@@ -269,23 +269,16 @@ static int sun4i_usb_phy_init(struct phy *_phy)
 		writel(val & ~2, phy->pmu + REG_PMU_UNK1);
 	}
 
-	if (data->cfg->type == sun8i_h3_phy) {
-		if (phy->index == 0) {
-			val = readl(data->base + REG_PHY_UNK_H3);
-			writel(val & ~1, data->base + REG_PHY_UNK_H3);
-		}
-	} else {
-		/* Enable USB 45 Ohm resistor calibration */
-		if (phy->index == 0)
-			sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1);
+	/* Enable USB 45 Ohm resistor calibration */
+	if (phy->index == 0)
+		sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1);
 
-		/* Adjust PHY's magnitude and rate */
-		sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5);
+	/* Adjust PHY's magnitude and rate */
+	sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5);
 
-		/* Disconnect threshold adjustment */
-		sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL,
-				    data->cfg->disc_thresh, 2);
-	}
+	/* Disconnect threshold adjustment */
+	sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL,
+			    data->cfg->disc_thresh, 2);
 
 	sun4i_usb_phy_passby(phy, 1);
 
-- 
2.11.0

^ permalink raw reply related

* [PATCH 2/4] ARM: dts: sun8i: add MUSB node to H3 SoC
From: Icenowy Zheng @ 2017-01-16 19:14 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170116191449.50397-1-icenowy@aosc.xyz>

As we have already H3 PHY support code to MUSB mode fixed, and the H3
MUSB controller supported, we can now add the MUSB node to the DTSI file
of H3.

Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
---
 arch/arm/boot/dts/sun8i-h3.dtsi | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi
index 08fd0860bb6b..60dd88c7fb9a 100644
--- a/arch/arm/boot/dts/sun8i-h3.dtsi
+++ b/arch/arm/boot/dts/sun8i-h3.dtsi
@@ -206,6 +206,19 @@
 			#size-cells = <0>;
 		};
 
+		usb_otg: usb at 01c19000 {
+			compatible = "allwinner,sun8i-h3-musb";
+			reg = <0x01c19000 0x0400>;
+			clocks = <&ccu CLK_BUS_OTG>;
+			resets = <&ccu RST_BUS_OTG>;
+			interrupts = <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-names = "mc";
+			phys = <&usbphy 0>;
+			phy-names = "usb";
+			extcon = <&usbphy 0>;
+			status = "disabled";
+		};
+
 		usbphy: phy at 01c19400 {
 			compatible = "allwinner,sun8i-h3-usb-phy";
 			reg = <0x01c19400 0x2c>,
-- 
2.11.0

^ permalink raw reply related

* [PATCH 3/4] ARM: dts: sun8i: enable USB OTG for Orange Pi Zero board
From: Icenowy Zheng @ 2017-01-16 19:14 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170116191449.50397-1-icenowy@aosc.xyz>

Orange Pi Zero board features a USB OTG port, which has a ID pin, and
can be used to power up the borad. However, even if the board is powered
via +5V pin in GPIO/expansion headers, the VBUS in the OTG port cannot
be powered up, thus it's impossible to use it in host mode with simple
OTG cables.

Add support for it in peripheral mode.

If someone really want to use it in host mode, the dr_mode property of
&usb_otg can be changed into "otg", then a powered USB OTG cable or USB
HUB is needed for powering out the external USB devices.

Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
---
 arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts b/arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts
index b7ca916d871d..437536e2b594 100644
--- a/arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts
+++ b/arch/arm/boot/dts/sun8i-h2-plus-orangepi-zero.dts
@@ -154,7 +154,13 @@
 	status = "disabled";
 };
 
+&usb_otg {
+	dr_mode = "peripheral";
+	status = "okay";
+};
+
 &usbphy {
 	/* USB VBUS is always on */
 	status = "okay";
+	usb0_id_det-gpios = <&pio 6 12 GPIO_ACTIVE_HIGH>; /* PG12 */
 };
-- 
2.11.0

^ permalink raw reply related

* [PATCH 4/4] ARM: dts: sun8i: enable USB OTG on Orange Pi One board
From: Icenowy Zheng @ 2017-01-16 19:14 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170116191449.50397-1-icenowy@aosc.xyz>

Orange Pi One board features a USB OTG port, which cannot be used to
power up the board, but have a gateable VBUS and an ID pin, which makes
it possible to be used in both host and peripheral mode.

Enable its OTG port in dual-role mode.

Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
---
 arch/arm/boot/dts/sun8i-h3-orangepi-one.dts | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts
index 34da853ee037..8f69c8c9995c 100644
--- a/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts
+++ b/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts
@@ -115,6 +115,11 @@
 	};
 };
 
+&reg_usb0_vbus {
+	gpio = <&r_pio 0 2 GPIO_ACTIVE_HIGH>; /* PL2 */
+	status = "okay";
+};
+
 &r_pio {
 	leds_r_opc: led_pins at 0 {
 		pins = "PL10";
@@ -151,7 +156,14 @@
 	status = "disabled";
 };
 
+&usb_otg {
+	dr_mode = "otg";
+	status = "okay";
+};
+
 &usbphy {
 	/* USB VBUS is always on */
 	status = "okay";
+	usb0_id_det-gpios = <&pio 6 12 GPIO_ACTIVE_HIGH>; /* PG12 */
+	usb0_vbus-supply = <&reg_usb0_vbus>;
 };
-- 
2.11.0

^ permalink raw reply related

* [PATCH v3 8/13] arm64: allwinner: a64: Add MMC nodes
From: Corentin Labbe @ 2017-01-16 19:16 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <ac77b9e25e7633886822cd1f1ebafd5985751ae4.1484585798.git-series.maxime.ripard@free-electrons.com>

On Mon, Jan 16, 2017 at 05:56:58PM +0100, Maxime Ripard wrote:
> From: Andre Przywara <andre.przywara@arm.com>
> 
> The A64 has 3 MMC controllers, one of them being especially targeted to
> eMMC. Among other things, it has a data strobe signal and a 8 bits data
> width.
> 
> The two other are more usual controllers that will have a 4 bits width at
> most and no data strobe signal, which limits it to more usual SD or MMC
> peripherals.
> 
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 39 ++++++++++++++++++++-
>  1 file changed, 39 insertions(+), 0 deletions(-)
> 
> diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
> index 99b6bb1e141c..143e9706438f 100644
> --- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
> +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
> @@ -299,6 +299,45 @@
>  			#size-cells = <0>;
>  		};
>  
> +		mmc0: mmc at 1c0f000 {
> +			compatible = "allwinner,sun50i-a64-mmc";
> +			reg = <0x01c0f000 0x1000>;
> +			clocks = <&ccu CLK_BUS_MMC0>, <&ccu CLK_MMC0>;
> +			clock-names = "ahb", "mmc";
> +			resets = <&ccu RST_BUS_MMC0>;
> +			reset-names = "ahb";
> +			interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
> +			status = "disabled";
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		mmc1: mmc at 1c10000 {
> +			compatible = "allwinner,sun50i-a64-mmc";
> +			reg = <0x01c10000 0x1000>;
> +			clocks = <&ccu CLK_BUS_MMC1>, <&ccu CLK_MMC1>;
> +			clock-names = "ahb", "mmc";
> +			resets = <&ccu RST_BUS_MMC1>;
> +			reset-names = "ahb";
> +			interrupts = <GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>;
> +			status = "disabled";
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
> +		mmc2: mmc at 1c11000 {
> +			compatible = "allwinner,sun50i-a64-emmc";
> +			reg = <0x01c11000 0x1000>;
> +			clocks = <&ccu CLK_BUS_MMC2>, <&ccu CLK_MMC2>;
> +			clock-names = "ahb", "mmc";
> +			resets = <&ccu RST_BUS_MMC2>;
> +			reset-names = "ahb";
> +			interrupts = <GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>;
> +			status = "disabled";
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};
> +
>  		gic: interrupt-controller at 1c81000 {
>  			compatible = "arm,gic-400";
>  			reg = <0x01c81000 0x1000>,

Hello

It seems that mmc node is after i2c at 1c2b400 so not in address order.

Regards
Corentin Labbe

^ permalink raw reply

* [PATCH 08/12] pinctrl: samsung: Use generic of_device_get_match_data helper
From: Krzysztof Kozlowski @ 2017-01-16 19:19 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1484549107-5957-9-git-send-email-m.szyprowski@samsung.com>

On Mon, Jan 16, 2017 at 07:45:03AM +0100, Marek Szyprowski wrote:
> Replace custom code with generic helper.
> 
> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> Acked-by: Tomasz Figa <tomasz.figa@gmail.com>
> ---
>  drivers/pinctrl/samsung/pinctrl-samsung.c | 9 ++++-----
>  1 file changed, 4 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/pinctrl/samsung/pinctrl-samsung.c b/drivers/pinctrl/samsung/pinctrl-samsung.c
> index 7648a280c0f4..86f23842f681 100644
> --- a/drivers/pinctrl/samsung/pinctrl-samsung.c
> +++ b/drivers/pinctrl/samsung/pinctrl-samsung.c
> @@ -27,6 +27,7 @@
>  #include <linux/err.h>
>  #include <linux/gpio.h>
>  #include <linux/irqdomain.h>
> +#include <linux/of_device.h>
>  #include <linux/spinlock.h>
>  #include <linux/syscore_ops.h>
>  
> @@ -955,15 +956,13 @@ static int samsung_gpiolib_unregister(struct platform_device *pdev,
>  	return 0;
>  }
>  
> -static const struct of_device_id samsung_pinctrl_dt_match[];
> -
>  /* retrieve the soc specific data */
>  static const struct samsung_pin_ctrl *
>  samsung_pinctrl_get_soc_data(struct samsung_pinctrl_drv_data *d,
>  			     struct platform_device *pdev)
>  {
>  	int id;
> -	const struct of_device_id *match;
> +	const struct samsung_pin_ctrl *match_data;
>  	struct device_node *node = pdev->dev.of_node;
>  	struct device_node *np;
>  	const struct samsung_pin_bank_data *bdata;
> @@ -978,8 +977,8 @@ static int samsung_gpiolib_unregister(struct platform_device *pdev,
>  		dev_err(&pdev->dev, "failed to get alias id\n");
>  		return ERR_PTR(-ENOENT);
>  	}
> -	match = of_match_node(samsung_pinctrl_dt_match, node);
> -	ctrl = (struct samsung_pin_ctrl *)match->data + id;
> +	match_data = of_device_get_match_data(&pdev->dev);
> +	ctrl = match_data + id;

I did not receive answer for my previous question here - why you need
two samsung_pin_ctrl* variables? Why you need additional match_data?

Just make it:
	ctrl = of_device_get_match_data(&pdev->dev);
	ctrl += id;
To me it looks simpler and obvious. Having two variables for the same
brings questions.

If you do not agree, say it. I don't mind. But I would appreciate if
comments were not ignored.

Best regards,
Krzysztof

^ permalink raw reply

* [PATCH 00/12] Move pad retention control to Exynos pin controller driver
From: Krzysztof Kozlowski @ 2017-01-16 19:23 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1484549107-5957-1-git-send-email-m.szyprowski@samsung.com>

On Mon, Jan 16, 2017 at 07:44:55AM +0100, Marek Szyprowski wrote:
> Hello,
> 
> This patchset is a follow-up of my work on adding runtime PM support
> to Exynos pin controller driver:
> http://www.spinics.net/lists/arm-kernel/msg550161.html
> 
> Runtime PM support itself needs a bit more discussion, so lets first focus on
> the prerequisites.
> 
> In case of Exynos pin controller driver it is a pad retention control. In
> current code it was handled by machine and PMU code and had no relation to
> what pin controller driver does. This patch series moves pad retention
> control to pin controller driver. While implmenting it, I also did a little
> cleanup of both Exynos PMU and pin controller drivers.
> 
> Patches are based on linux-next from 2017.01.16 with Exynos4415 support
> removal patch applied: https://lkml.org/lkml/2017/1/14/137

I didn't find explicit statement for dependency between the patches in
the patchset itself and also I could not find the usage of
EXYNOS_PMU_DEV_NAME by pinctrl driver.

Do the pinctrl changes depend on soc/samsung patches? If not, then
probably your future work will depend on this?

Best regards,
Krzysztof

^ permalink raw reply

* [PATCH v7 06/15] irqchip: gicv3-its: platform-msi: refactor its_pmsi_init() to prepare for ACPI
From: Matthias Brugger @ 2017-01-16 19:27 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1484147199-4267-7-git-send-email-hanjun.guo@linaro.org>



On 11/01/17 16:06, Hanjun Guo wrote:
> Introduce its_pmsi_init_one() to refactor the code to isolate
> ACPI&DT common code to prepare for ACPI later.
>
> Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
> Cc: Marc Zyngier <marc.zyngier@arm.com>
> Cc: Tomasz Nowicki <tn@semihalf.com>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> ---
>  drivers/irqchip/irq-gic-v3-its-platform-msi.c | 45 ++++++++++++++++-----------
>  1 file changed, 27 insertions(+), 18 deletions(-)
>

Reviewed-by: Matthias Brugger <mbrugger@suse.com>

> diff --git a/drivers/irqchip/irq-gic-v3-its-platform-msi.c b/drivers/irqchip/irq-gic-v3-its-platform-msi.c
> index 3c94278..3d9efd1 100644
> --- a/drivers/irqchip/irq-gic-v3-its-platform-msi.c
> +++ b/drivers/irqchip/irq-gic-v3-its-platform-msi.c
> @@ -82,34 +82,43 @@ static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
>  	{},
>  };
>
> -static int __init its_pmsi_init(void)
> +static int __init its_pmsi_init_one(struct fwnode_handle *fwnode,
> +				const char *name)
>  {
> -	struct device_node *np;
>  	struct irq_domain *parent;
>
> +	parent = irq_find_matching_fwnode(fwnode, DOMAIN_BUS_NEXUS);
> +	if (!parent || !msi_get_domain_info(parent)) {
> +		pr_err("%s: unable to locate ITS domain\n", name);
> +		return -ENXIO;
> +	}
> +
> +	if (!platform_msi_create_irq_domain(fwnode, &its_pmsi_domain_info,
> +					    parent)) {
> +		pr_err("%s: unable to create platform domain\n", name);
> +		return -ENXIO;
> +	}
> +
> +	pr_info("Platform MSI: %s domain created\n", name);
> +	return 0;
> +}
> +
> +static void __init its_pmsi_of_init(void)
> +{
> +	struct device_node *np;
> +
>  	for (np = of_find_matching_node(NULL, its_device_id); np;
>  	     np = of_find_matching_node(np, its_device_id)) {
>  		if (!of_property_read_bool(np, "msi-controller"))
>  			continue;
>
> -		parent = irq_find_matching_host(np, DOMAIN_BUS_NEXUS);
> -		if (!parent || !msi_get_domain_info(parent)) {
> -			pr_err("%s: unable to locate ITS domain\n",
> -			       np->full_name);
> -			continue;
> -		}
> -
> -		if (!platform_msi_create_irq_domain(of_node_to_fwnode(np),
> -						    &its_pmsi_domain_info,
> -						    parent)) {
> -			pr_err("%s: unable to create platform domain\n",
> -			       np->full_name);
> -			continue;
> -		}
> -
> -		pr_info("Platform MSI: %s domain created\n", np->full_name);
> +		its_pmsi_init_one(of_node_to_fwnode(np), np->full_name);
>  	}
> +}
>
> +static int __init its_pmsi_init(void)
> +{
> +	its_pmsi_of_init();
>  	return 0;
>  }
>  early_initcall(its_pmsi_init);
>

^ permalink raw reply

* [PATCH 0/4] ARM: dts: mt7623: Add initial Geek Force support
From: Andreas Färber @ 2017-01-16 19:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <3fecb422-8185-7ee0-c203-2bfdc4fd1393@phrozen.org>

Am 10.01.2017 um 11:18 schrieb John Crispin:
> On 10/01/2017 10:48, Andreas F?rber wrote:
>> Am 10.01.2017 um 08:00 schrieb John Crispin:
>>> On 08/01/2017 14:30, Andreas F?rber wrote:
>>>>
>>>> Andreas F?rber (4):
>>>>   Documentation: devicetree: Add vendor prefix for AsiaRF
>>>>   Documentation: devicetree: arm: mediatek: Add Geek Force board
>>>>   ARM: dts: mt7623: Add Geek Force config
>>>>   MAINTAINERS: Extend ARM/Mediatek SoC support section
>>>>
>>>
>>> Hi,
>>>
>>> i need to NAK this series. the asiarf board is nothing more than the
>>> official MTK EVB with AsiaRF written on it. this board is already
>>> supported by linux (arch/arm/boot/dts/mt7623-evb.dts) please extend the
>>> EVB dts file nstead of adding a duplicate and letting the original
> bitrot.
>>
>> Well, I disagree.
> 
> reading the rest of the email you seem to be quite agro about this.

Please re-read your reply above and my comments and reconsider your
attitude when replying in the future. It's not about whether these
patches go in or not, it's about your wording. And that continues with
aggressively throwing around the term "agro" in two mails already. I
don't see anything aggressive or angry in my original cover letter that
would've prompted your reply and can only interpret that as your own
frustration with your mt7623 progress. Instead you should've taken the
time to explain a bit better what you really meant, then we could've
spared or shortened this lengthy discussion and have a v2 already.

>> First of all I'm not letting "the original" bitrot, because I have
>> nothing to do with that .dts! If anyone is to blame for letting it
>> bitrot since February 2016, pick your own nose:
>>
>>
> http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/log/arch/arm/boot/dts/mt7623-evb.dts
> 
> what should i pick my nose about ?

Don't blame _me_ for "bitrot" of a file _you_ added almost a year ago
that did not see any updates since.

Nothing wrong with you not working on something, just don't blame me for
it then, especially when I don't (knowingly) have that hardware and have
never come in touch with it before. I am new to linux-mediatek and had
checked LAKML in vein for any mt7623 patches before sending mine.

[snip]
>> Third, by your argumentation we shouldn't be adding, e.g., Odroid .dts
>> files either because they were based on a Samsung SMDK, or .dts files
>> for Amlogic TV boxes because they're almost identical to reference
>> designs, etc.
>> Users need to know which .dts file to choose, so having a sane .dts
>> filename is warranted. Depending on how similar they are, one could
>> either #include the -evb.dts or factor out a shared .dtsi, but that
>> takes us back to the previous point of hardly anyone having access to
>> EVB information to identify such a subset. Therefore duplicating trivial
>> nodes is the method of choice for all practical purposes - mt7623.dtsi
>> is getting reused just fine.
>>
> 
> in that case add a dtsi file for the EVB and include it in your geek
> board.dts and only update the compat string.

The question that I am not in a position to answer is: Are those two
boards identical or just very similar? Paul in CC can hopefully clarify
this when he is back. Matthias as maintainer has also remained silent.

>> Comparing our two .dts files, mine has two more UART nodes enabled, the
>> U-Boot bootloader's baudrate set to actually get serial output, a
>> different board compatible string for identification, and I chose the
>> new dual-licensing header that is being requested for new DT files.
> 
> 1) at the time we adde this the uart support was not ready

> 2) the bootloader i am using is a custom built one hence the random baudrate

Well, did the _original_ bootloader use 115200? In that case we could
update -evb.dts with it, and you could still override it via console=
for your custom build.

Or maybe having source access you could even contribute to mainline
U-Boot, so we can all work on the same codebase? I read a BPi-R2 is
coming up with mt7623, so booting without appended .dtb and uImage will
come in handy for more people than just EVB owners and Geek Force
backers. I'd happily contribute to making bootefi command work.

> 3) you can just updae the license if you want to, no problem

OK, will gladly look into the feasibility.

>> For lack of schematics I figured out UART1 by testing - continuity tests
>> for GND, console=ttySx,115200n8 and trial-and-error for RX/TX. Obviously
>> I can't do that for a board I don't have access to.
>> UART2 and UART0 pins were clear, but only UART2 was obvious from ttyMT2.
> 
> you do have the EVB directly in front of you
> 
>> Do you actually have access to a Geek Force board yourself, or what are
>> you basing your claims on? Mine looks different from the Indiegogo
>> picture and thus has different identification from that on
>> https://wikidevi.com/wiki/AsiaRF_WS2977 (WS3301, MT7623N RFB_V10).
> 
> i dont need the geek board as i have the EVB and they are identical
> according to MTK
> 
>> If you confirm the EVB's baudrate I can happily send that part your way.
>> I've seen 921600 on the Helio X20 96board for instance.
> 
> see above

So... 4) add my UART nodes to your -evb.dts? Any nitpicks on the actual
patch 3/4?

>> Also, none of what you've said justifies NAK'ing patch 4/4, which
>> applies to any mt7* and arm64 .dts, including yours.
> 
> agreed, i never even mentioned 4/4

You replied to the cover letter 0/4 "i need to NAK this series" - and
series includes all four patches. Therefore my frustration with your
hip-shot reply. If you haven't read the patch, don't NAK it!

In fact still no one replied to it at all, even now that you're aware of
its existence.

>> While we're at it, I noticed that mainline has a "mediatek,mt7623-eth"
>> network driver but no corresponding .dtsi node. Talk about bitrot...
> 
> the idea is that we work together to make thins optimal. this is not a
> you or is right. this is about the FOSS peer review process. please dont
> be so agro.

See above, please reconsider your tone. Peer review is no excuse for
rude and destructive behavior towards contributors you don't know.

Had you looked at the lists and codebase you would know that I am not
new to FOSS.

And please stop writing "agro", it sounds like some pothead making a
peace sign; not helpful among professional software developers.

Taking the time to start your sentences with a capital letter would also
be a respectful gesture.

> to me it seems suboptimal to support 2 dts files for the same board.

If it is the identical board, then we are in violent agreement. I just
find all your replies to me offensive so far, I don't know you and thus
have no reason to trust your unsubstantiated claims about my board.

Regards,

Andreas

-- 
SUSE Linux GmbH, Maxfeldstr. 5, 90409 N?rnberg, Germany
GF: Felix Imend?rffer, Jane Smithard, Graham Norton
HRB 21284 (AG N?rnberg)

^ permalink raw reply

* [PATCH 0/4] ARM: dts: mt7623: Add initial Geek Force support
From: Andreas Färber @ 2017-01-16 19:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <0bd6567e-1c5b-0ff6-d044-57d25e1a40d2@phrozen.org>

Hi John,

Am 12.01.2017 um 09:23 schrieb John Crispin:
> had a look last night why the ethernet dtsi was not added and it
> obviously was not added as we were waiting for the clk-mt2701 to be
> merged. the ethernet dtsi will have phandles pointing at the clk nodes
> which did not exist at the time. same is true for the PWM code.
> 
> i sat down last night and worked out what pending patches i still have
> for mt7623 and out of the ~80 required to get v4.4 working i only need
> around 10 for v4.10-rc1.
> 
> i started to rebase these patches last night and will have time to test
> them tomorrow or early next week. as the pwrap node alone is around 200
> lines of devicetree we need to figure out a way to add this to the dts
> files without duplicating it. i'll try to post a series early next week
> that we can then discuss and rebase your geekboard patches on.

Sounds like a plan. Many thanks!

Regards,
Andreas

-- 
SUSE Linux GmbH, Maxfeldstr. 5, 90409 N?rnberg, Germany
GF: Felix Imend?rffer, Jane Smithard, Graham Norton
HRB 21284 (AG N?rnberg)

^ permalink raw reply

* [PATCH 09/12] pinctrl: samsung: Add infrastructure for pin-bank retention control
From: Krzysztof Kozlowski @ 2017-01-16 19:37 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1484549107-5957-10-git-send-email-m.szyprowski@samsung.com>

On Mon, Jan 16, 2017 at 07:45:04AM +0100, Marek Szyprowski wrote:
> Pad retention control after suspend/resume cycle should be done from pin
> controller driver instead of PMU (power management unit) driver to avoid
> possible ordering and logical dependencies. Till now it worked fine only
> because PMU driver registered its sys_ops after pin controller.
> 
> This patch adds infrastructure to handle pad retention during pin control
> driver resume.
> 
> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> ---
>  drivers/pinctrl/samsung/pinctrl-samsung.c | 12 ++++++---
>  drivers/pinctrl/samsung/pinctrl-samsung.h | 42 +++++++++++++++++++++++++++++++
>  2 files changed, 51 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/pinctrl/samsung/pinctrl-samsung.c b/drivers/pinctrl/samsung/pinctrl-samsung.c
> index 86f23842f681..95a84086a2e9 100644
> --- a/drivers/pinctrl/samsung/pinctrl-samsung.c
> +++ b/drivers/pinctrl/samsung/pinctrl-samsung.c
> @@ -1075,6 +1075,9 @@ static int samsung_pinctrl_probe(struct platform_device *pdev)
>  		ctrl->eint_gpio_init(drvdata);
>  	if (ctrl->eint_wkup_init)
>  		ctrl->eint_wkup_init(drvdata);
> +	if (ctrl->retention_data && ctrl->retention_data->init)
> +		drvdata->retention_ctrl = ctrl->retention_data->init(drvdata,
> +							  ctrl->retention_data);
>  
>  	platform_set_drvdata(pdev, drvdata);
>  
> @@ -1127,15 +1130,15 @@ static void samsung_pinctrl_suspend_dev(
>  
>  	if (drvdata->suspend)
>  		drvdata->suspend(drvdata);
> +	if (drvdata->retention_ctrl && drvdata->retention_ctrl->on)
> +		drvdata->retention_ctrl->on(drvdata);
> +

This new line is not needed (checkpatch might complain... either in
normal mode or in strict). Beside that, thanks for splitting the
patch for interface from the implementation:

Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org>

Best regards,
Krzysztof

^ permalink raw reply

* [PATCH v2 2/3] spi: pxa2xx: Prepare for edge-triggered interrupts
From: Jan Kiszka @ 2017-01-16 19:46 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1484593637.2133.152.camel@linux.intel.com>

On 2017-01-16 20:07, Andy Shevchenko wrote:
> On Mon, 2017-01-16 at 19:44 +0100, Jan Kiszka wrote:
>> When using the a device with edge-triggered interrupts, such as MSIs,
>> the interrupt handler has to ensure that there is a point in time
>> during
>> its execution where all interrupts sources are silent so that a new
>> event can trigger a new interrupt again.
>>
>> This is achieved here by looping over SSSR evaluation. We need to take
>> into account that SSCR1 may be changed by the transfer handler, thus
>> we
>> need to redo the mask calculation, at least regarding the volatile
>> interrupt enable bit (TIE).
>>
> 
> So, more comments/questions below.
> 
>>  
>>  	sccr1_reg = pxa2xx_spi_read(drv_data, SSCR1);
>>  
>> -	/* Ignore possible writes if we don't need to write */
>> -	if (!(sccr1_reg & SSCR1_TIE))
>> -		mask &= ~SSSR_TFS;
>> -
>>  	/* Ignore RX timeout interrupt if it is disabled */
>>  	if (!(sccr1_reg & SSCR1_TINTE))
>>  		mask &= ~SSSR_TINT;
>>  
>> -	if (!(status & mask))
>> -		return IRQ_NONE;
>> +	while (1) {
> 
> Can we switch to do-while and move previous block here?

Don't see how this would help (without duplicating more code).

> Btw, can TINTE
> bit be set again during a loop?

Nope, it's statically set, at least so far.

What we could do is simply restarting ssp_int

> 
>> +		/* Ignore possible writes if we don't need to write
>> */
>> +		if (!(sccr1_reg & SSCR1_TIE))
>> +			mask &= ~SSSR_TFS;
>>  
>> -	if (!drv_data->master->cur_msg) {
>> -		handle_bad_msg(drv_data);
>> -		/* Never fail */
>> -		return IRQ_HANDLED;
>> -	}
>> +		if (!(status & mask))
>> +			return ret;
>> +
>> +		if (!drv_data->master->cur_msg) {
>> +			handle_bad_msg(drv_data);
>> +			/* Never fail */
>> +			return IRQ_HANDLED;
>> +		}
>> +
> 
>> +		ret |= drv_data->transfer_handler(drv_data);
> 
> So, we might call handler several times. This needs to be commented in
> the code why you do so.

I can move the commit log into the code.

> 
>>  
>> -	return drv_data->transfer_handler(drv_data);
>> +		status = pxa2xx_spi_read(drv_data, SSSR);
> 
> Would it be possible to get all 1:s from the register
> (something/autosuspend just powered off it by timeout?) ?
> 

Not sure if that can happen, but I guess it would be simpler and more
readable to simply do this instead:

	while (1) {
		/*
		 * If the device is not yet in RPM suspended state and we get an
		 * interrupt that is meant for another device, check if status
		 * bits are all set to one. That means that the device is
		 * already powered off.
		 */
		status = pxa2xx_spi_read(drv_data, SSSR);
		if (status == ~0)
			return ret;

		sccr1_reg = pxa2xx_spi_read(drv_data, SSCR1);

		/* Ignore RX timeout interrupt if it is disabled */
		if (!(sccr1_reg & SSCR1_TINTE))
			mask &= ~SSSR_TINT;

		/* Ignore possible writes if we don't need to write */
		if (!(sccr1_reg & SSCR1_TIE))
			mask &= ~SSSR_TFS;

		if (!(status & mask))
			return ret;

		if (!drv_data->master->cur_msg) {
			handle_bad_msg(drv_data);
			/* Never fail */
			return IRQ_HANDLED;
		}

		ret |= drv_data->transfer_handler(drv_data);
	}


i.e. preserve the current structure, just add the loop.

Jan

-- 
Siemens AG, Corporate Technology, CT RDA ITP SES-DE
Corporate Competence Center Embedded Linux

^ permalink raw reply

* [PATCH 01/12] soc: samsung: pmu: Use common device name to let others to find it easily
From: Krzysztof Kozlowski @ 2017-01-16 19:48 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1484549107-5957-2-git-send-email-m.szyprowski@samsung.com>

On Mon, Jan 16, 2017 at 07:44:56AM +0100, Marek Szyprowski wrote:
> This patch always set device name to "exynos-pmu" to let other drivers to
> find PMU device easily. This is done mainly to get regmap to access PMU
> registers from other drivers. This way it can be avoided to add phandle to
> the PMU node to almost all drivers in the SoC just to get a regmap access
> in the drivers. PMU is something like a SoC wide service, so there is no
> point modeling it as hardware dependency for all devices in device tree.
> 
> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> ---
>  drivers/soc/samsung/exynos-pmu.c       |  1 +
>  include/linux/soc/samsung/exynos-pmu.h | 19 +++++++++++++++++++
>  2 files changed, 20 insertions(+)
> 
> diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c
> index 0acdfd82e751..63bb471845cb 100644
> --- a/drivers/soc/samsung/exynos-pmu.c
> +++ b/drivers/soc/samsung/exynos-pmu.c
> @@ -120,6 +120,7 @@ static int exynos_pmu_probe(struct platform_device *pdev)
>  		pmu_context->pmu_data->pmu_init();
>  
>  	platform_set_drvdata(pdev, pmu_context);
> +	dev_set_name(dev, EXYNOS_PMU_DEV_NAME);
>  
>  	dev_dbg(dev, "Exynos PMU Driver probe done\n");
>  	return 0;
> diff --git a/include/linux/soc/samsung/exynos-pmu.h b/include/linux/soc/samsung/exynos-pmu.h
> index e2e9de1acc5b..90d9205805ea 100644
> --- a/include/linux/soc/samsung/exynos-pmu.h
> +++ b/include/linux/soc/samsung/exynos-pmu.h
> @@ -12,6 +12,10 @@
>  #ifndef __LINUX_SOC_EXYNOS_PMU_H
>  #define __LINUX_SOC_EXYNOS_PMU_H
>  
> +#include <linux/mfd/syscon.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
>  enum sys_powerdown {
>  	SYS_AFTR,
>  	SYS_LPA,
> @@ -21,4 +25,19 @@ enum sys_powerdown {
>  
>  extern void exynos_sys_powerdown_conf(enum sys_powerdown mode);
>  
> +#define EXYNOS_PMU_DEV_NAME "exynos-pmu"

The define is not used outside so it should not be defined in header.
You need it only for the code below (see next comment).

> +
> +static inline struct regmap *exynos_get_pmu_regs(void)
> +{
> +	struct device *dev = bus_find_device_by_name(&platform_bus_type, NULL,
> +						     EXYNOS_PMU_DEV_NAME);
> +	if (dev) {
> +		struct regmap *regs = syscon_node_to_regmap(dev->of_node);
> +		put_device(dev);
> +		if (!IS_ERR(regs))
> +			return regs;
> +	}
> +	return NULL;
> +}

Any particular reason why definion of this is in header? This rather
looks like candidate for EXPORTED_SYMBOL defined in .c (plus Kconfig
dependency).

Best regards,
Krzysztof

> +
>  #endif /* __LINUX_SOC_EXYNOS_PMU_H */
> -- 
> 1.9.1
> 

^ permalink raw reply

* [PATCH 00/12] Move pad retention control to Exynos pin controller driver
From: Krzysztof Kozlowski @ 2017-01-16 19:50 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170116192345.4cywkpzsswitgclp@kozik-lap>

On Mon, Jan 16, 2017 at 09:23:45PM +0200, Krzysztof Kozlowski wrote:
> On Mon, Jan 16, 2017 at 07:44:55AM +0100, Marek Szyprowski wrote:
> > Hello,
> > 
> > This patchset is a follow-up of my work on adding runtime PM support
> > to Exynos pin controller driver:
> > http://www.spinics.net/lists/arm-kernel/msg550161.html
> > 
> > Runtime PM support itself needs a bit more discussion, so lets first focus on
> > the prerequisites.
> > 
> > In case of Exynos pin controller driver it is a pad retention control. In
> > current code it was handled by machine and PMU code and had no relation to
> > what pin controller driver does. This patch series moves pad retention
> > control to pin controller driver. While implmenting it, I also did a little
> > cleanup of both Exynos PMU and pin controller drivers.
> > 
> > Patches are based on linux-next from 2017.01.16 with Exynos4415 support
> > removal patch applied: https://lkml.org/lkml/2017/1/14/137
> 
> I didn't find explicit statement for dependency between the patches in
> the patchset itself and also I could not find the usage of
> EXYNOS_PMU_DEV_NAME by pinctrl driver.

Okay, now I found the usage of the function from header so this answers
the question below about dependency in the patchset itself. Anyway it
would be nice to state that explicitly.

Best regards,
Krzysztof

> 
> Do the pinctrl changes depend on soc/samsung patches? If not, then
> probably your future work will depend on this?
> 
> Best regards,
> Krzysztof

^ permalink raw reply

* [PATCH net-next v3 06/10] net: dsa: Migrate to device_find_class()
From: Florian Fainelli @ 2017-01-16 20:01 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170115191632.GD5643@lunn.ch>

On 01/15/2017 11:16 AM, Andrew Lunn wrote:
>>> What exactly is the relationship between these devices (a ascii-art tree
>>> or sysfs tree output might be nice) so I can try to understand what is
>>> going on here.
> 
> Hi Greg, Florian
> 
> A few diagrams and trees which might help understand what is going on.
> 
> The first diagram comes from the 2008 patch which added all this code:
> 
>             +-----------+       +-----------+
>             |           | RGMII |           |
>             |           +-------+           +------ 1000baseT MDI ("WAN")
>             |           |       |  6-port   +------ 1000baseT MDI ("LAN1")
>             |    CPU    |       |  ethernet +------ 1000baseT MDI ("LAN2")
>             |           |MIImgmt|  switch   +------ 1000baseT MDI ("LAN3")
>             |           +-------+  w/5 PHYs +------ 1000baseT MDI ("LAN4")
>             |           |       |           |
>             +-----------+       +-----------+
> 
> We have an ethernet switch and a host CPU. The switch is connected to
> the CPU in two different ways. RGMII allows us to get Ethernet frames
> from the CPU into the switch. MIImgmt, is the management bus normally
> used for Ethernet PHYs, but Marvell switches also use it for Managing
> switches.
> 
> The diagram above is the simplest setup. You can have multiple
> Ethernet switches, connected together via switch ports. Each switch
> has its own MIImgmt connect to the CPU, but there is only one RGMII
> link.
> 
> When this code was designed back in 2008, it was decided to represent
> this is a platform device, and it has a platform_data, which i have
> slightly edited to keep it simple:
> 
> struct dsa_platform_data {
>         /*
>          * Reference to a Linux network interface that connects
>          * to the root switch chip of the tree.
>          */
>         struct device   *netdev;
> 
>         /*
>          * Info structs describing each of the switch chips
>          * connected via this network interface.
>          */
>         int             nr_chips;
>         struct dsa_chip_data    *chip;
> };
> 
> This netdev is the CPU side of the RGMII interface.
> 
> Each switch has a dsa_chip_data, again edited:
> 
> struct dsa_chip_data {
>         /*
>          * How to access the switch configuration registers.
>          */
>         struct device   *host_dev;
>         int             sw_addr;
> ...
> }
> 
> The host_dev is the CPU side of the MIImgmt, and we have the address
> the switch is using on the bus.
> 
> During probe of this platform device, we need to get from the
> struct device *netdev to a struct net_device *dev.
> 
> So the code looks in the device net class to find the device
> 
> |   |   |   |-- f1074000.ethernet
> |   |   |   |   |-- deferred_probe
> |   |   |   |   |-- driver -> ../../../../../bus/platform/drivers/mvneta
> |   |   |   |   |-- driver_override
> |   |   |   |   |-- modalias
> |   |   |   |   |-- net
> |   |   |   |   |   `-- eth1
> |   |   |   |   |       |-- addr_assign_type
> |   |   |   |   |       |-- address
> |   |   |   |   |       |-- addr_len
> |   |   |   |   |       |-- broadcast
> |   |   |   |   |       |-- carrier
> |   |   |   |   |       |-- carrier_changes
> |   |   |   |   |       |-- deferred_probe
> |   |   |   |   |       |-- device -> ../../../f1074000.ethernet
> 
> and then use container_of() to get the net_device.
> 
> Similarly, the code needs to get from struct device *host_dev to a struct mii_bus *.
> 
> |   |   |   |-- f1072004.mdio
> |   |   |   |   |-- deferred_probe
> |   |   |   |   |-- driver -> ../../../../../bus/platform/drivers/orion-mdio
> |   |   |   |   |-- driver_override
> |   |   |   |   |-- mdio_bus
> |   |   |   |   |   `-- f1072004.mdio-mi
> |   |   |   |   |       |-- deferred_probe
> |   |   |   |   |       |-- device -> ../../../f1072004.mdio
> 

Thanks Andrew! Greg, does that make it clearer how these devices
references are used, do you still think the way this is done is wrong,
too cautious, or valid?
-- 
Florian

^ permalink raw reply


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