Skip to contentSkip to conversation

The *Full* Cubox-i series

PATCH New Russell King - ARM Linux 6 participants 232 messages Apr 25, 2014 – May 8, 2014 Page 1 of 2

Subsystems (40)
lore
Share & tools
lore
#0 lore raw

cover The *Full* Cubox-i series

So that people can see what kind of a patch nightmare I'm in, and hopefully start being more co-operative towards getting patches into mainline, here is the *full* patch set for CuBox-i support that I'm presently sitting on.

Anything that can be done to make this easier would be very helpful, so I'm not spending lots of time tweaking some random patch and then having to remerge all these different sub-series.

 .../DocBook/media/v4l/pixfmt-packed-rgb.xml        |   39 +
 .../devicetree/bindings/leds/leds-pwm.txt          |    2 +
 Documentation/devicetree/bindings/mmc/mmc.txt      |   11 +
 .../bindings/staging/imx-drm/fsl-imx-drm.txt       |    3 +-
 arch/arm/Kconfig                                   |   51 -
 arch/arm/boot/dts/imx6dl-hummingboard.dts          |   28 +
 arch/arm/boot/dts/imx6q-cubox-i.dts                |    4 +
 arch/arm/boot/dts/imx6qdl-cubox-i.dtsi             |   79 +-
 arch/arm/boot/dts/imx6qdl-microsom-ar8035.dtsi     |   22 +-
 arch/arm/boot/dts/imx6qdl-microsom.dtsi            |   98 ++
 arch/arm/boot/dts/imx6qdl.dtsi                     |    2 +
 arch/arm/boot/dts/imx6sl.dtsi                      |    2 +
 arch/arm/boot/dts/marco.dtsi                       |    2 +-
 arch/arm/boot/dts/prima2.dtsi                      |    2 +-
 arch/arm/configs/imx_v6_v7_defconfig               |    1 +
 arch/arm/include/asm/hardware/cache-l2x0.h         |  104 +-
 arch/arm/include/asm/outercache.h                  |   67 +-
 arch/arm/mach-berlin/berlin.c                      |    2 +-
 arch/arm/mach-cns3xxx/core.c                       |   10 +-
 arch/arm/mach-exynos/common.h                      |    1 -
 arch/arm/mach-exynos/exynos.c                      |   15 +-
 arch/arm/mach-exynos/sleep.S                       |   30 +-
 arch/arm/mach-highbank/highbank.c                  |   18 +-
 arch/arm/mach-imx/clk-imx6q.c                      |   29 +-
 arch/arm/mach-imx/clk-pllv3.c                      |   27 +-
 arch/arm/mach-imx/mach-vf610.c                     |    2 +-
 arch/arm/mach-imx/suspend-imx6.S                   |   24 +-
 arch/arm/mach-imx/system.c                         |    8 +-
 arch/arm/mach-mvebu/board-v7.c                     |    2 +-
 arch/arm/mach-nomadik/cpu-8815.c                   |    2 +-
 arch/arm/mach-omap2/Kconfig                        |    1 +
 arch/arm/mach-omap2/common.h                       |    1 +
 arch/arm/mach-omap2/io.c                           |    2 +
 arch/arm/mach-omap2/omap-mpuss-lowpower.c          |   16 +-
 arch/arm/mach-omap2/omap4-common.c                 |   86 +-
 arch/arm/mach-prima2/l2x0.c                        |   34 +-
 arch/arm/mach-prima2/pm.c                          |    1 -
 arch/arm/mach-realview/realview_eb.c               |    9 +-
 arch/arm/mach-realview/realview_pb1176.c           |    8 +-
 arch/arm/mach-realview/realview_pb11mp.c           |    9 +-
 arch/arm/mach-realview/realview_pbx.c              |    4 +-
 arch/arm/mach-rockchip/rockchip.c                  |    2 +-
 .../board-armadillo800eva-reference.c              |    4 +-
 arch/arm/mach-shmobile/board-armadillo800eva.c     |    4 +-
 arch/arm/mach-shmobile/board-kzm9g-reference.c     |    4 +-
 arch/arm/mach-shmobile/board-kzm9g.c               |    4 +-
 arch/arm/mach-shmobile/setup-r8a7778.c             |    4 +-
 arch/arm/mach-shmobile/setup-r8a7779.c             |    4 +-
 arch/arm/mach-socfpga/socfpga.c                    |    2 +-
 arch/arm/mach-spear/platsmp.c                      |   19 +-
 arch/arm/mach-spear/spear13xx.c                    |    8 +-
 arch/arm/mach-sti/board-dt.c                       |   12 +-
 arch/arm/mach-tegra/pm.h                           |    2 -
 arch/arm/mach-tegra/reset-handler.S                |   11 +-
 arch/arm/mach-tegra/sleep.h                        |   31 -
 arch/arm/mach-tegra/tegra.c                        |   22 +-
 arch/arm/mach-ux500/cache-l2x0.c                   |   32 +-
 arch/arm/mach-vexpress/ct-ca9x4.c                  |   28 +-
 arch/arm/mach-zynq/common.c                        |    2 +-
 arch/arm/mm/Kconfig                                |   51 +
 arch/arm/mm/Makefile                               |    3 +-
 arch/arm/mm/cache-feroceon-l2.c                    |    1 -
 arch/arm/mm/cache-l2x0.c                           | 1564 +++++++++++++-------
 arch/arm/mm/l2c-common.c                           |   20 +
 arch/arm/mm/l2c-l2x0-resume.S                      |   58 +
 arch/arm/plat-samsung/s5p-sleep.S                  |    1 -
 drivers/Kconfig                                    |    2 +
 drivers/Makefile                                   |    1 +
 drivers/ata/ahci_imx.c                             |  184 ++-
 drivers/cec/Kconfig                                |   14 +
 drivers/cec/Makefile                               |    1 +
 drivers/cec/cec-dev.c                              |  384 +++++
 drivers/gpu/drm/drm_crtc_helper.c                  |    6 -
 drivers/leds/leds-pwm.c                            |  144 +-
 drivers/mmc/core/core.c                            |   42 +
 drivers/mmc/core/host.c                            |   68 +
 drivers/mmc/core/sdio_irq.c                        |   41 +-
 drivers/mmc/host/Kconfig                           |   63 +-
 drivers/mmc/host/dw_mmc.c                          |    2 +
 drivers/mmc/host/sdhci-acpi.c                      |    8 +
 drivers/mmc/host/sdhci-bcm-kona.c                  |    4 +
 drivers/mmc/host/sdhci-bcm2835.c                   |    4 +
 drivers/mmc/host/sdhci-cns3xxx.c                   |   13 +-
 drivers/mmc/host/sdhci-dove.c                      |    4 +
 drivers/mmc/host/sdhci-esdhc-imx.c                 |   82 +-
 drivers/mmc/host/sdhci-esdhc.h                     |    4 +-
 drivers/mmc/host/sdhci-of-arasan.c                 |    4 +
 drivers/mmc/host/sdhci-of-esdhc.c                  |   70 +-
 drivers/mmc/host/sdhci-of-hlwd.c                   |    4 +
 drivers/mmc/host/sdhci-pci.c                       |    9 +-
 drivers/mmc/host/sdhci-pltfm.c                     |    4 +
 drivers/mmc/host/sdhci-pxav2.c                     |   14 +-
 drivers/mmc/host/sdhci-pxav3.c                     |   13 +-
 drivers/mmc/host/sdhci-s3c.c                       |   36 +-
 drivers/mmc/host/sdhci-sirf.c                      |    4 +
 drivers/mmc/host/sdhci-spear.c                     |    5 +-
 drivers/mmc/host/sdhci-tegra.c                     |   27 +-
 drivers/mmc/host/sdhci.c                           |  728 +++++----
 drivers/mmc/host/sdhci.h                           |   20 +-
 drivers/net/ethernet/freescale/fec.h               |   62 +-
 drivers/net/ethernet/freescale/fec_main.c          | 1414 +++++++++++-------
 drivers/regulator/anatop-regulator.c               |    1 +
 drivers/regulator/core.c                           |    2 +-
 drivers/regulator/dummy.c                          |    1 +
 drivers/regulator/fixed.c                          |    4 +-
 drivers/staging/imx-drm/Kconfig                    |   18 +
 drivers/staging/imx-drm/Makefile                   |    3 +
 drivers/staging/imx-drm/drm-ddc-connector.c        |   92 ++
 drivers/staging/imx-drm/drm-ddc-connector.h        |   26 +
 drivers/staging/imx-drm/dw-hdmi-audio.c            |  654 ++++++++
 drivers/staging/imx-drm/dw-hdmi-audio.h            |   15 +
 drivers/staging/imx-drm/dw-hdmi-cec.c              |  205 +++
 drivers/staging/imx-drm/dw-hdmi-cec.h              |   16 +
 drivers/staging/imx-drm/imx-drm-core.c             |    7 +-
 drivers/staging/imx-drm/imx-hdmi.c                 |  178 ++-
 drivers/staging/imx-drm/imx-ldb.c                  |   21 +
 drivers/staging/imx-drm/imx-tve.c                  |   63 +-
 drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h        |    5 +
 drivers/staging/imx-drm/ipu-v3/ipu-common.c        |   41 +-
 drivers/staging/imx-drm/ipu-v3/ipu-dc.c            |   94 +-
 drivers/staging/imx-drm/ipu-v3/ipu-di.c            |    2 +-
 drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c          |   25 +-
 drivers/staging/imx-drm/ipu-v3/ipu-dp.c            |   71 +-
 drivers/staging/imx-drm/ipu-v3/ipu-prv.h           |    3 +
 drivers/staging/imx-drm/ipuv3-crtc.c               |   16 +-
 drivers/staging/imx-drm/ipuv3-plane.c              |    4 +
 drivers/staging/imx-drm/parallel-display.c         |    2 +
 include/linux/cec-dev.h                            |   69 +
 include/linux/mmc/host.h                           |    8 +
 include/linux/mmc/sdhci.h                          |   15 +-
 include/uapi/linux/cec-dev.h                       |   34 +
 include/uapi/linux/videodev2.h                     |    1 +
 sound/soc/fsl/imx-pcm-dma.c                        |    2 +-
 133 files changed, 5598 insertions(+), 2336 deletions(-)
Signature · 2 lines
FTTC broadband for 0.8mile line: now at 9.7Mbps down 460kbps up... slowly
improving, and getting towards what was expected from it.
#1 lore raw

PATCH 1/222 ARM: l2c: remove outer_inv_all() method

No one ever calls this function anywhere in the kernel, so let's completely remove it from the outer cache API and turn it into an internal-only thing.

---
 arch/arm/include/asm/outercache.h | 8 --------
 arch/arm/mm/cache-feroceon-l2.c   | 1 -
 arch/arm/mm/cache-l2x0.c          | 5 -----
 3 files changed, 14 deletions(-)
arch/arm/include/asm/outercache.h +0 -8
index f94784f0e3a6..0e4420858990 100644
@@ -28,7 +28,6 @@ struct outer_cache_fns {
 	void (*clean_range)(unsigned long, unsigned long);
 	void (*flush_range)(unsigned long, unsigned long);
 	void (*flush_all)(void);
-	void (*inv_all)(void);
 	void (*disable)(void);
 #ifdef CONFIG_OUTER_CACHE_SYNC
 	void (*sync)(void);
@@ -63,12 +62,6 @@ static inline void outer_flush_all(void)
 		outer_cache.flush_all();
 }
 
-static inline void outer_inv_all(void)
-{
-	if (outer_cache.inv_all)
-		outer_cache.inv_all();
-}
-
 static inline void outer_disable(void)
 {
 	if (outer_cache.disable)
@@ -90,7 +83,6 @@ static inline void outer_clean_range(phys_addr_t start, phys_addr_t end)
 static inline void outer_flush_range(phys_addr_t start, phys_addr_t end)
 { }
 static inline void outer_flush_all(void) { }
-static inline void outer_inv_all(void) { }
 static inline void outer_disable(void) { }
 static inline void outer_resume(void) { }
 
arch/arm/mm/cache-feroceon-l2.c +0 -1
index dc814a548056..e028a7f2ebcc 100644
@@ -350,7 +350,6 @@ void __init feroceon_l2_init(int __l2_wt_override)
 	outer_cache.inv_range = feroceon_l2_inv_range;
 	outer_cache.clean_range = feroceon_l2_clean_range;
 	outer_cache.flush_range = feroceon_l2_flush_range;
-	outer_cache.inv_all = l2_inv_all;
 
 	enable_l2();
 
arch/arm/mm/cache-l2x0.c +0 -5
index 7abde2ce8973..f9985e5a208c 100644
@@ -414,7 +414,6 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
 		outer_cache.flush_range = l2x0_flush_range;
 		outer_cache.sync = l2x0_cache_sync;
 		outer_cache.flush_all = l2x0_flush_all;
-		outer_cache.inv_all = l2x0_inv_all;
 		outer_cache.disable = l2x0_disable;
 	}
 
@@ -884,7 +883,6 @@ static const struct l2x0_of_data pl310_data = {
 		.flush_range = l2x0_flush_range,
 		.sync        = l2x0_cache_sync,
 		.flush_all   = l2x0_flush_all,
-		.inv_all     = l2x0_inv_all,
 		.disable     = l2x0_disable,
 	},
 };
@@ -899,7 +897,6 @@ static const struct l2x0_of_data l2x0_data = {
 		.flush_range = l2x0_flush_range,
 		.sync        = l2x0_cache_sync,
 		.flush_all   = l2x0_flush_all,
-		.inv_all     = l2x0_inv_all,
 		.disable     = l2x0_disable,
 	},
 };
@@ -914,7 +911,6 @@ static const struct l2x0_of_data aurora_with_outer_data = {
 		.flush_range = aurora_flush_range,
 		.sync        = l2x0_cache_sync,
 		.flush_all   = l2x0_flush_all,
-		.inv_all     = l2x0_inv_all,
 		.disable     = l2x0_disable,
 	},
 };
@@ -946,7 +942,6 @@ static const struct l2x0_of_data bcm_l2x0_data = {
 		.flush_range = bcm_flush_range,
 		.sync        = l2x0_cache_sync,
 		.flush_all   = l2x0_flush_all,
-		.inv_all     = l2x0_inv_all,
 		.disable     = l2x0_disable,
 	},
 };
Signature · 1 line
1.8.3.1
#2 lore raw

PATCH 2/222 ARM: l2c: remove unnecessary call to outer_flush_all()

In reply to Russell King - ARM Linux

outer_disable() is defined to safely turn the L2 cache off without data
loss: this means that outer_flush_all() should never be called unless you need to implement some special L2 cache disabling, and even then only from your replacement L2 cache disable function.

---
 arch/arm/mach-prima2/pm.c | 1 -
 1 file changed, 1 deletion(-)
arch/arm/mach-prima2/pm.c +0 -1
index c4525a88e5da..96e9bc102117 100644
@@ -71,7 +71,6 @@ static int sirfsoc_pm_enter(suspend_state_t state)
 	case PM_SUSPEND_MEM:
 		sirfsoc_pre_suspend_power_off();
 
-		outer_flush_all();
 		outer_disable();
 		/* go zzz */
 		cpu_suspend(0, sirfsoc_finish_suspend);
Signature · 1 line
1.8.3.1
#3 lore raw

PATCH 3/222 ARM: l2c: avoid calling outer_flush_all() unnecessarily (Spear)

In reply to Russell King - ARM Linux

Spear calls outer_flush_all() from it's SMP bringup function. This is potentially dangerous as the L2C set/way operations which implement this don't take kindly to concurrent operations. Besides, there's better solutions to this, as implemented on other platforms.

---
 arch/arm/mach-spear/platsmp.c | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)
arch/arm/mach-spear/platsmp.c +14 -5
index 5c4a19887b2b..eb9f2ef1e974 100644
@@ -20,6 +20,18 @@
 #include <mach/spear.h>
 #include "generic.h"
 
+/*
+ * Write pen_release in a way that is guaranteed to be visible to all
+ * observers, irrespective of whether they're taking part in coherency
+ * or not.  This is necessary for the hotplug code to work reliably.
+ */
+static void write_pen_release(int val)
+{
+	pen_release = val;
+	smp_wmb();
+	sync_cache_w(&pen_release);
+}
+
 static DEFINE_SPINLOCK(boot_lock);
 
 static void __iomem *scu_base = IOMEM(VA_SCU_BASE);
@@ -30,8 +42,7 @@ static void spear13xx_secondary_init(unsigned int cpu)
 	 * let the primary processor know we're out of the
 	 * pen, then head off into the C entry point
 	 */
-	pen_release = -1;
-	smp_wmb();
+	write_pen_release(-1);
 
 	/*
 	 * Synchronise with the boot thread.
@@ -58,9 +69,7 @@ static int spear13xx_boot_secondary(unsigned int cpu, struct task_struct *idle)
 	 * Note that "pen_release" is the hardware CPU ID, whereas
 	 * "cpu" is Linux's internal ID.
 	 */
-	pen_release = cpu;
-	flush_cache_all();
-	outer_flush_all();
+	write_pen_release(cpu);
 
 	timeout = jiffies + (1 * HZ);
 	while (time_before(jiffies, timeout)) {
Signature · 1 line
1.8.3.1
#4 lore raw

PATCH 4/222 ARM: l2c: omap2: remove ES1.0 support

In reply to Russell King - ARM Linux

Santosh says:

Quoted 4 lines

But we should kill all of that since we long back decided to remove ES1.0 related code. The mach-omap code alreasy has removed the ES1.0 compatibility so feel free to remove any specific ES1.0 related stuff. That silicon is long dead.

---
 arch/arm/mach-omap2/omap4-common.c | 25 ++++++++-----------------
 1 file changed, 8 insertions(+), 17 deletions(-)
arch/arm/mach-omap2/omap4-common.c +8 -17
index 95e171a055f3..48cf74d284ec 100644
@@ -182,7 +182,7 @@ static void omap4_l2x0_set_debug(unsigned long val)
 
 static int __init omap_l2_cache_init(void)
 {
-	u32 aux_ctrl = 0;
+	u32 aux_ctrl;
 
 	/*
 	 * To avoid code running on other OMAPs in
@@ -196,27 +196,18 @@ static int __init omap_l2_cache_init(void)
 	if (WARN_ON(!l2cache_base))
 		return -ENOMEM;
 
-	/*
-	 * 16-way associativity, parity disabled
-	 * Way size - 32KB (es1.0)
-	 * Way size - 64KB (es2.0 +)
-	 */
-	aux_ctrl = ((1 << L2X0_AUX_CTRL_ASSOCIATIVITY_SHIFT) |
+	/* 16-way associativity, parity disabled, way size - 64KB (es2.0 +) */
+	aux_ctrl = (1 << L2X0_AUX_CTRL_ASSOCIATIVITY_SHIFT) |
 			(0x1 << 25) |
 			(0x1 << L2X0_AUX_CTRL_NS_LOCKDOWN_SHIFT) |
-			(0x1 << L2X0_AUX_CTRL_NS_INT_CTRL_SHIFT));
-
-	if (omap_rev() == OMAP4430_REV_ES1_0) {
-		aux_ctrl |= 0x2 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT;
-	} else {
-		aux_ctrl |= ((0x3 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT) |
+			(0x1 << L2X0_AUX_CTRL_NS_INT_CTRL_SHIFT)) |
+			(0x3 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT) |
 			(1 << L2X0_AUX_CTRL_SHARE_OVERRIDE_SHIFT) |
 			(1 << L2X0_AUX_CTRL_DATA_PREFETCH_SHIFT) |
 			(1 << L2X0_AUX_CTRL_INSTR_PREFETCH_SHIFT) |
-			(1 << L2X0_AUX_CTRL_EARLY_BRESP_SHIFT));
-	}
-	if (omap_rev() != OMAP4430_REV_ES1_0)
-		omap_smc1(0x109, aux_ctrl);
+			(1 << L2X0_AUX_CTRL_EARLY_BRESP_SHIFT);
+
+	omap_smc1(0x109, aux_ctrl);
 
 	/* Enable PL310 L2 Cache controller */
 	omap_smc1(0x102, 0x1);
Signature · 1 line
1.8.3.1
#5 lore raw

PATCH 5/222 ARM: l2c: remove unnecessary UL-suffix to mask values

In reply to Russell King - ARM Linux

They're u32, they're not unsigned long. The UL suffix is not required here.

---
 arch/arm/mach-highbank/highbank.c | 2 +-
 arch/arm/mach-imx/mach-vf610.c    | 2 +-
 arch/arm/mach-imx/system.c        | 2 +-
 arch/arm/mach-mvebu/board-v7.c    | 2 +-
 arch/arm/mach-rockchip/rockchip.c | 2 +-
 arch/arm/mach-socfpga/socfpga.c   | 2 +-
 6 files changed, 6 insertions(+), 6 deletions(-)
arch/arm/mach-highbank/highbank.c +1 -1
index c7de89b263dd..38e1dc3b4c6e 100644
@@ -69,7 +69,7 @@ static void __init highbank_init_irq(void)
 	if (IS_ENABLED(CONFIG_CACHE_L2X0) &&
 	    of_find_compatible_node(NULL, NULL, "arm,pl310-cache")) {
 		highbank_smc1(0x102, 0x1);
-		l2x0_of_init(0, ~0UL);
+		l2x0_of_init(0, ~0);
 		outer_cache.disable = highbank_l2x0_disable;
 	}
 }
arch/arm/mach-imx/mach-vf610.c +1 -1
index 2d8aef5a6efa..6288a9690e78 100644
@@ -22,7 +22,7 @@ static void __init vf610_init_machine(void)
 
 static void __init vf610_init_irq(void)
 {
-	l2x0_of_init(0, ~0UL);
+	l2x0_of_init(0, ~0);
 	irqchip_init();
 }
 
arch/arm/mach-imx/system.c +1 -1
index 5e3027d3692f..c6571f1de9fd 100644
@@ -145,6 +145,6 @@ void __init imx_init_l2cache(void)
 	of_node_put(np);
 
 out:
-	l2x0_of_init(0, ~0UL);
+	l2x0_of_init(0, ~0);
 }
 #endif
arch/arm/mach-mvebu/board-v7.c +1 -1
index 333fca8fdc41..c6bd79f64744 100644
@@ -60,7 +60,7 @@ static void __init mvebu_timer_and_clk_init(void)
 	coherency_init();
 	BUG_ON(mvebu_mbus_dt_init());
 #ifdef CONFIG_CACHE_L2X0
-	l2x0_of_init(0, ~0UL);
+	l2x0_of_init(0, ~0);
 #endif
 
 	if (of_machine_is_compatible("marvell,armada375"))
arch/arm/mach-rockchip/rockchip.c +1 -1
index d211d6fa0d98..d252efe3747b 100644
@@ -26,7 +26,7 @@
 
 static void __init rockchip_dt_init(void)
 {
-	l2x0_of_init(0, ~0UL);
+	l2x0_of_init(0, ~0);
 	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
 }
 
arch/arm/mach-socfpga/socfpga.c +1 -1
index d86231e11b34..9bbb8177f247 100644
@@ -100,7 +100,7 @@ static void socfpga_cyclone5_restart(enum reboot_mode mode, const char *cmd)
 
 static void __init socfpga_cyclone5_init(void)
 {
-	l2x0_of_init(0, ~0UL);
+	l2x0_of_init(0, ~0);
 	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
 }
 
Signature · 1 line
1.8.3.1
#6 lore raw

PATCH 6/222 ARM: outer cache: add documentation of outer cache functions

In reply to Russell King - ARM Linux

Add some documentation to cover the outer cache functions so that their requirements can be better understood. Of particular note are the flush_all() and disable() methods which must not be called except in very specific circumstances.

---
 arch/arm/include/asm/outercache.h | 48 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 47 insertions(+), 1 deletion(-)
arch/arm/include/asm/outercache.h +47 -1
index 0e4420858990..2615b3d9e807 100644
@@ -39,35 +39,75 @@ struct outer_cache_fns {
 extern struct outer_cache_fns outer_cache;
 
 #ifdef CONFIG_OUTER_CACHE
-
+/**
+ * outer_inv_range - invalidate range of outer cache lines
+ * @start: starting physical address, inclusive
+ * @end: end physical address, exclusive
+ */
 static inline void outer_inv_range(phys_addr_t start, phys_addr_t end)
 {
 	if (outer_cache.inv_range)
 		outer_cache.inv_range(start, end);
 }
+
+/**
+ * outer_clean_range - clean dirty outer cache lines
+ * @start: starting physical address, inclusive
+ * @end: end physical address, exclusive
+ */
 static inline void outer_clean_range(phys_addr_t start, phys_addr_t end)
 {
 	if (outer_cache.clean_range)
 		outer_cache.clean_range(start, end);
 }
+
+/**
+ * outer_flush_range - clean and invalidate outer cache lines
+ * @start: starting physical address, inclusive
+ * @end: end physical address, exclusive
+ */
 static inline void outer_flush_range(phys_addr_t start, phys_addr_t end)
 {
 	if (outer_cache.flush_range)
 		outer_cache.flush_range(start, end);
 }
 
+/**
+ * outer_flush_all - clean and invalidate all cache lines in the outer cache
+ *
+ * Note: depending on implementation, this may not be atomic - it must
+ * only be called with interrupts disabled and no other active outer
+ * cache masters.
+ *
+ * It is intended that this function is only used by implementations
+ * needing to override the outer_cache.disable() method due to security.
+ * (Some implementations perform this as a clean followed by an invalidate.)
+ */
 static inline void outer_flush_all(void)
 {
 	if (outer_cache.flush_all)
 		outer_cache.flush_all();
 }
 
+/**
+ * outer_disable - clean, invalidate and disable the outer cache
+ *
+ * Disable the outer cache, ensuring that any data contained in the outer
+ * cache is pushed out to lower levels of system memory.  The note and
+ * conditions above concerning outer_flush_all() applies here.
+ */
 static inline void outer_disable(void)
 {
 	if (outer_cache.disable)
 		outer_cache.disable();
 }
 
+/**
+ * outer_resume - restore the cache configuration and re-enable outer cache
+ *
+ * Restore any configuration that the cache had when previously enabled,
+ * and re-enable the outer cache.
+ */
 static inline void outer_resume(void)
 {
 	if (outer_cache.resume)
@@ -89,6 +129,12 @@ static inline void outer_resume(void) { }
 #endif
 
 #ifdef CONFIG_OUTER_CACHE_SYNC
+/**
+ * outer_sync - perform a sync point for outer cache
+ *
+ * Ensure that all outer cache operations are complete and any store
+ * buffers are drained.
+ */
 static inline void outer_sync(void)
 {
 	if (outer_cache.sync)
Signature · 1 line
1.8.3.1
#7 lore raw

PATCH 7/222 ARM: outer cache: add WARN_ON() to outer_disable()

In reply to Russell King - ARM Linux

Add WARN_ON() conditions to outer_disable() to ensure that its requirements aren't violated.

---
 arch/arm/include/asm/outercache.h |  7 ++-----
 arch/arm/mm/Makefile              |  1 +
 arch/arm/mm/l2c-common.c          | 20 ++++++++++++++++++++
 3 files changed, 23 insertions(+), 5 deletions(-)
 create mode 100644 arch/arm/mm/l2c-common.c
arch/arm/include/asm/outercache.h +2 -5
index 2615b3d9e807..e9a0797fe188 100644
@@ -21,6 +21,7 @@
 #ifndef __ASM_OUTERCACHE_H
 #define __ASM_OUTERCACHE_H
 
+#include <linux/bug.h>
 #include <linux/types.h>
 
 struct outer_cache_fns {
@@ -96,11 +97,7 @@ static inline void outer_flush_all(void)
  * cache is pushed out to lower levels of system memory.  The note and
  * conditions above concerning outer_flush_all() applies here.
  */
-static inline void outer_disable(void)
-{
-	if (outer_cache.disable)
-		outer_cache.disable();
-}
+extern void outer_disable(void);
 
 /**
  * outer_resume - restore the cache configuration and re-enable outer cache
arch/arm/mm/Makefile +1 -0
index 7f39ce2f841f..de5a6a27081b 100644
@@ -95,6 +95,7 @@ obj-$(CONFIG_CPU_V7M)		+= proc-v7m.o
 AFLAGS_proc-v6.o	:=-Wa,-march=armv6
 AFLAGS_proc-v7.o	:=-Wa,-march=armv7-a
 
+obj-$(CONFIG_OUTER_CACHE)	+= l2c-common.o
 obj-$(CONFIG_CACHE_FEROCEON_L2)	+= cache-feroceon-l2.o
 obj-$(CONFIG_CACHE_L2X0)	+= cache-l2x0.o
 obj-$(CONFIG_CACHE_XSC3L2)	+= cache-xsc3l2.o
arch/arm/mm/l2c-common.c new file +20 -0
new file mode 100644
index 000000000000..10a3cf28c362
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2010 ARM Ltd.
+ * Written by Catalin Marinas <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/bug.h>
+#include <linux/smp.h>
+#include <asm/outercache.h>
+
+void outer_disable(void)
+{
+	WARN_ON(!irqs_disabled());
+	WARN_ON(num_online_cpus() > 1);
+
+	if (outer_cache.disable)
+		outer_cache.disable();
+}
Signature · 1 line
1.8.3.1
#8 lore raw

PATCH 8/222 ARM: l2c: add helper for L2 cache controller DT IDs

In reply to Russell King - ARM Linux

Make it easier to declare L2 cache controller DT IDs by using a macro.

---
 arch/arm/mm/cache-l2x0.c | 23 ++++++++++-------------
 1 file changed, 10 insertions(+), 13 deletions(-)
arch/arm/mm/cache-l2x0.c +10 -13
index f9985e5a208c..ac410b21edfb 100644
@@ -946,20 +946,17 @@ static const struct l2x0_of_data bcm_l2x0_data = {
 	},
 };
 
+#define L2C_ID(name, fns) { .compatible = name, .data = (void *)&fns }
 static const struct of_device_id l2x0_ids[] __initconst = {
-	{ .compatible = "arm,l210-cache", .data = (void *)&l2x0_data },
-	{ .compatible = "arm,l220-cache", .data = (void *)&l2x0_data },
-	{ .compatible = "arm,pl310-cache", .data = (void *)&pl310_data },
-	{ .compatible = "bcm,bcm11351-a2-pl310-cache", /* deprecated name */
-	  .data = (void *)&bcm_l2x0_data},
-	{ .compatible = "brcm,bcm11351-a2-pl310-cache",
-	  .data = (void *)&bcm_l2x0_data},
-	{ .compatible = "marvell,aurora-outer-cache",
-	  .data = (void *)&aurora_with_outer_data},
-	{ .compatible = "marvell,aurora-system-cache",
-	  .data = (void *)&aurora_no_outer_data},
-	{ .compatible = "marvell,tauros3-cache",
-	  .data = (void *)&tauros3_data },
+	L2C_ID("arm,l210-cache", l2x0_data),
+	L2C_ID("arm,l220-cache", l2x0_data),
+	L2C_ID("arm,pl310-cache", pl310_data),
+	L2C_ID("brcm,bcm11351-a2-pl310-cache", bcm_l2x0_data),
+	L2C_ID("marvell,aurora-outer-cache", aurora_with_outer_data),
+	L2C_ID("marvell,aurora-system-cache", aurora_no_outer_data),
+	L2C_ID("marvell,tauros3-cache", tauros3_data),
+	/* Deprecated IDs */
+	L2C_ID("bcm,bcm11351-a2-pl310-cache", bcm_l2x0_data),
 	{}
 };
 
Signature · 1 line
1.8.3.1
#9 lore raw

PATCH 9/222 ARM: l2c: tidy up l2x0_of_data declarations

In reply to Russell King - ARM Linux

Remove NULL initialisers, make these all __initconst structures, and order their members in the same order as the structure declaration.

---
 arch/arm/mm/cache-l2x0.c | 30 ++++++++++++++----------------
 1 file changed, 14 insertions(+), 16 deletions(-)
arch/arm/mm/cache-l2x0.c +14 -16
index ac410b21edfb..063e1787e8c3 100644
@@ -873,49 +873,48 @@ static void __init aurora_of_setup(const struct device_node *np,
 	*aux_mask &= ~mask;
 }
 
-static const struct l2x0_of_data pl310_data = {
+static const struct l2x0_of_data pl310_data __initconst = {
 	.setup = pl310_of_setup,
 	.save  = pl310_save,
 	.outer_cache = {
-		.resume      = pl310_resume,
 		.inv_range   = l2x0_inv_range,
 		.clean_range = l2x0_clean_range,
 		.flush_range = l2x0_flush_range,
-		.sync        = l2x0_cache_sync,
 		.flush_all   = l2x0_flush_all,
 		.disable     = l2x0_disable,
+		.sync        = l2x0_cache_sync,
+		.resume      = pl310_resume,
 	},
 };
 
-static const struct l2x0_of_data l2x0_data = {
+static const struct l2x0_of_data l2x0_data __initconst = {
 	.setup = l2x0_of_setup,
-	.save  = NULL,
 	.outer_cache = {
-		.resume      = l2x0_resume,
 		.inv_range   = l2x0_inv_range,
 		.clean_range = l2x0_clean_range,
 		.flush_range = l2x0_flush_range,
-		.sync        = l2x0_cache_sync,
 		.flush_all   = l2x0_flush_all,
 		.disable     = l2x0_disable,
+		.sync        = l2x0_cache_sync,
+		.resume      = l2x0_resume,
 	},
 };
 
-static const struct l2x0_of_data aurora_with_outer_data = {
+static const struct l2x0_of_data aurora_with_outer_data __initconst = {
 	.setup = aurora_of_setup,
 	.save  = aurora_save,
 	.outer_cache = {
-		.resume      = aurora_resume,
 		.inv_range   = aurora_inv_range,
 		.clean_range = aurora_clean_range,
 		.flush_range = aurora_flush_range,
-		.sync        = l2x0_cache_sync,
 		.flush_all   = l2x0_flush_all,
 		.disable     = l2x0_disable,
+		.sync        = l2x0_cache_sync,
+		.resume      = aurora_resume,
 	},
 };
 
-static const struct l2x0_of_data aurora_no_outer_data = {
+static const struct l2x0_of_data aurora_no_outer_data __initconst = {
 	.setup = aurora_of_setup,
 	.save  = aurora_save,
 	.outer_cache = {
@@ -923,8 +922,7 @@ static const struct l2x0_of_data aurora_no_outer_data = {
 	},
 };
 
-static const struct l2x0_of_data tauros3_data = {
-	.setup = NULL,
+static const struct l2x0_of_data tauros3_data __initconst = {
 	.save  = tauros3_save,
 	/* Tauros3 broadcasts L1 cache operations to L2 */
 	.outer_cache = {
@@ -932,17 +930,17 @@ static const struct l2x0_of_data tauros3_data = {
 	},
 };
 
-static const struct l2x0_of_data bcm_l2x0_data = {
+static const struct l2x0_of_data bcm_l2x0_data __initconst = {
 	.setup = pl310_of_setup,
 	.save  = pl310_save,
 	.outer_cache = {
-		.resume      = pl310_resume,
 		.inv_range   = bcm_inv_range,
 		.clean_range = bcm_clean_range,
 		.flush_range = bcm_flush_range,
-		.sync        = l2x0_cache_sync,
 		.flush_all   = l2x0_flush_all,
 		.disable     = l2x0_disable,
+		.sync        = l2x0_cache_sync,
+		.resume      = pl310_resume,
 	},
 };
 
Signature · 1 line
1.8.3.1
#10 lore raw

PATCH 10/222 ARM: l2c: rename OF specific things, making l2x0_of_data available to all

In reply to Russell King - ARM Linux

Rename a few things to help distinguish their function(s):
l2x0_of_data -> l2c_init_data
setup -> of_parse
add of_ prefix to OF specific data

---
 arch/arm/mm/cache-l2x0.c | 64 ++++++++++++++++++++++++------------------------
 1 file changed, 32 insertions(+), 32 deletions(-)
arch/arm/mm/cache-l2x0.c +32 -32
index 063e1787e8c3..d659c4ca46bb 100644
@@ -28,6 +28,12 @@
 #include "cache-tauros3.h"
 #include "cache-aurora-l2.h"
 
+struct l2c_init_data {
+	void (*of_parse)(const struct device_node *, u32 *, u32 *);
+	void (*save)(void);
+	struct outer_cache_fns outer_cache;
+};
+
 #define CACHE_LINE_SIZE		32
 
 static void __iomem *l2x0_base;
@@ -42,12 +48,6 @@ static u32  cache_id_part_number_from_dt;
 
 struct l2x0_regs l2x0_saved_regs;
 
-struct l2x0_of_data {
-	void (*setup)(const struct device_node *, u32 *, u32 *);
-	void (*save)(void);
-	struct outer_cache_fns outer_cache;
-};
-
 static bool of_init = false;
 
 static inline void cache_wait_way(void __iomem *reg, unsigned long mask)
@@ -664,7 +664,7 @@ static void bcm_flush_range(unsigned long start, unsigned long end)
 		new_end);
 }
 
-static void __init l2x0_of_setup(const struct device_node *np,
+static void __init l2x0_of_parse(const struct device_node *np,
 				 u32 *aux_val, u32 *aux_mask)
 {
 	u32 data[2] = { 0, 0 };
@@ -698,7 +698,7 @@ static void __init l2x0_of_setup(const struct device_node *np,
 	*aux_mask &= ~mask;
 }
 
-static void __init pl310_of_setup(const struct device_node *np,
+static void __init pl310_of_parse(const struct device_node *np,
 				  u32 *aux_val, u32 *aux_mask)
 {
 	u32 data[3] = { 0, 0, 0 };
@@ -851,7 +851,7 @@ static void __init aurora_broadcast_l2_commands(void)
 	isb();
 }
 
-static void __init aurora_of_setup(const struct device_node *np,
+static void __init aurora_of_parse(const struct device_node *np,
 				u32 *aux_val, u32 *aux_mask)
 {
 	u32 val = AURORA_ACR_REPLACEMENT_TYPE_SEMIPLRU;
@@ -873,8 +873,8 @@ static void __init aurora_of_setup(const struct device_node *np,
 	*aux_mask &= ~mask;
 }
 
-static const struct l2x0_of_data pl310_data __initconst = {
-	.setup = pl310_of_setup,
+static const struct l2c_init_data of_pl310_data __initconst = {
+	.of_parse = pl310_of_parse,
 	.save  = pl310_save,
 	.outer_cache = {
 		.inv_range   = l2x0_inv_range,
@@ -887,8 +887,8 @@ static const struct l2x0_of_data pl310_data __initconst = {
 	},
 };
 
-static const struct l2x0_of_data l2x0_data __initconst = {
-	.setup = l2x0_of_setup,
+static const struct l2c_init_data of_l2x0_data __initconst = {
+	.of_parse = l2x0_of_parse,
 	.outer_cache = {
 		.inv_range   = l2x0_inv_range,
 		.clean_range = l2x0_clean_range,
@@ -900,8 +900,8 @@ static const struct l2x0_of_data l2x0_data __initconst = {
 	},
 };
 
-static const struct l2x0_of_data aurora_with_outer_data __initconst = {
-	.setup = aurora_of_setup,
+static const struct l2c_init_data of_aurora_with_outer_data __initconst = {
+	.of_parse = aurora_of_parse,
 	.save  = aurora_save,
 	.outer_cache = {
 		.inv_range   = aurora_inv_range,
@@ -914,15 +914,15 @@ static const struct l2x0_of_data aurora_with_outer_data __initconst = {
 	},
 };
 
-static const struct l2x0_of_data aurora_no_outer_data __initconst = {
-	.setup = aurora_of_setup,
+static const struct l2c_init_data of_aurora_no_outer_data __initconst = {
+	.of_parse = aurora_of_parse,
 	.save  = aurora_save,
 	.outer_cache = {
 		.resume      = aurora_resume,
 	},
 };
 
-static const struct l2x0_of_data tauros3_data __initconst = {
+static const struct l2c_init_data of_tauros3_data __initconst = {
 	.save  = tauros3_save,
 	/* Tauros3 broadcasts L1 cache operations to L2 */
 	.outer_cache = {
@@ -930,8 +930,8 @@ static const struct l2x0_of_data tauros3_data __initconst = {
 	},
 };
 
-static const struct l2x0_of_data bcm_l2x0_data __initconst = {
-	.setup = pl310_of_setup,
+static const struct l2c_init_data of_bcm_l2x0_data __initconst = {
+	.of_parse = pl310_of_parse,
 	.save  = pl310_save,
 	.outer_cache = {
 		.inv_range   = bcm_inv_range,
@@ -946,22 +946,22 @@ static const struct l2x0_of_data bcm_l2x0_data __initconst = {
 
 #define L2C_ID(name, fns) { .compatible = name, .data = (void *)&fns }
 static const struct of_device_id l2x0_ids[] __initconst = {
-	L2C_ID("arm,l210-cache", l2x0_data),
-	L2C_ID("arm,l220-cache", l2x0_data),
-	L2C_ID("arm,pl310-cache", pl310_data),
-	L2C_ID("brcm,bcm11351-a2-pl310-cache", bcm_l2x0_data),
-	L2C_ID("marvell,aurora-outer-cache", aurora_with_outer_data),
-	L2C_ID("marvell,aurora-system-cache", aurora_no_outer_data),
-	L2C_ID("marvell,tauros3-cache", tauros3_data),
+	L2C_ID("arm,l210-cache", of_l2x0_data),
+	L2C_ID("arm,l220-cache", of_l2x0_data),
+	L2C_ID("arm,pl310-cache", of_pl310_data),
+	L2C_ID("brcm,bcm11351-a2-pl310-cache", of_bcm_l2x0_data),
+	L2C_ID("marvell,aurora-outer-cache", of_aurora_with_outer_data),
+	L2C_ID("marvell,aurora-system-cache", of_aurora_no_outer_data),
+	L2C_ID("marvell,tauros3-cache", of_tauros3_data),
 	/* Deprecated IDs */
-	L2C_ID("bcm,bcm11351-a2-pl310-cache", bcm_l2x0_data),
+	L2C_ID("bcm,bcm11351-a2-pl310-cache", of_bcm_l2x0_data),
 	{}
 };
 
 int __init l2x0_of_init(u32 aux_val, u32 aux_mask)
 {
+	const struct l2c_init_data *data;
 	struct device_node *np;
-	const struct l2x0_of_data *data;
 	struct resource res;
 
 	np = of_find_matching_node(NULL, l2x0_ids);
@@ -981,12 +981,12 @@ int __init l2x0_of_init(u32 aux_val, u32 aux_mask)
 
 	/* L2 configuration can only be changed if the cache is disabled */
 	if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
-		if (data->setup)
-			data->setup(np, &aux_val, &aux_mask);
+		if (data->of_parse)
+			data->of_parse(np, &aux_val, &aux_mask);
 
 		/* For aurora cache in no outer mode select the
 		 * correct mode using the coprocessor*/
-		if (data == &aurora_no_outer_data)
+		if (data == &of_aurora_no_outer_data)
 			aurora_broadcast_l2_commands();
 	}
 
Signature · 1 line
1.8.3.1
#11 lore raw

PATCH 11/222 ARM: l2c: provide generic function for calling set_debug method

In reply to Russell King - ARM Linux

Provide a generic function which always calls the set_debug method. This will be used later in the series as some work-arounds require that the debug register be written.

---
 arch/arm/mm/cache-l2x0.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)
arch/arm/mm/cache-l2x0.c +11 -1
index d659c4ca46bb..595c50519e41 100644
@@ -57,6 +57,16 @@ static inline void cache_wait_way(void __iomem *reg, unsigned long mask)
 		cpu_relax();
 }
 
+/*
+ * This should only be called when we have a requirement that the
+ * register be written due to a work-around, as platforms running
+ * in non-secure mode may not be able to access this register.
+ */
+static inline void l2c_set_debug(void __iomem *base, unsigned long val)
+{
+	outer_cache.set_debug(val);
+}
+
 #ifdef CONFIG_CACHE_PL310
 static inline void cache_wait(void __iomem *reg, unsigned long mask)
 {
@@ -92,7 +102,7 @@ static inline void l2x0_inv_line(unsigned long addr)
 static inline void debug_writel(unsigned long val)
 {
 	if (outer_cache.set_debug)
-		outer_cache.set_debug(val);
+		l2c_set_debug(l2x0_base, val);
 }
 
 static void pl310_set_debug(unsigned long val)
Signature · 1 line
1.8.3.1
#12 lore raw

PATCH 12/222 ARM: l2c: split out cache unlock code

In reply to Russell King - ARM Linux

Split the cache unlock code out of l2x0_unlock(). We want to be able to re-use this functionality later.

---
 arch/arm/mm/cache-l2x0.c | 23 ++++++++++++++++-------
 1 file changed, 16 insertions(+), 7 deletions(-)
arch/arm/mm/cache-l2x0.c +16 -7
index 595c50519e41..a1313d20f205 100644
@@ -50,6 +50,9 @@ struct l2x0_regs l2x0_saved_regs;
 
 static bool of_init = false;
 
+/*
+ * Common code for all cache controllers.
+ */
 static inline void cache_wait_way(void __iomem *reg, unsigned long mask)
 {
 	/* wait for cache operation by line or way to complete */
@@ -67,6 +70,18 @@ static inline void l2c_set_debug(void __iomem *base, unsigned long val)
 	outer_cache.set_debug(val);
 }
 
+static inline void l2c_unlock(void __iomem *base, unsigned num)
+{
+	unsigned i;
+
+	for (i = 0; i < num; i++) {
+		writel_relaxed(0, base + L2X0_LOCKDOWN_WAY_D_BASE +
+			       i * L2X0_LOCKDOWN_STRIDE);
+		writel_relaxed(0, base + L2X0_LOCKDOWN_WAY_I_BASE +
+			       i * L2X0_LOCKDOWN_STRIDE);
+	}
+}
+
 #ifdef CONFIG_CACHE_PL310
 static inline void cache_wait(void __iomem *reg, unsigned long mask)
 {
@@ -308,7 +323,6 @@ static void l2x0_disable(void)
 static void l2x0_unlock(u32 cache_id)
 {
 	int lockregs;
-	int i;
 
 	switch (cache_id & L2X0_CACHE_ID_PART_MASK) {
 	case L2X0_CACHE_ID_PART_L310:
@@ -323,12 +337,7 @@ static void l2x0_unlock(u32 cache_id)
 		break;
 	}
 
-	for (i = 0; i < lockregs; i++) {
-		writel_relaxed(0x0, l2x0_base + L2X0_LOCKDOWN_WAY_D_BASE +
-			       i * L2X0_LOCKDOWN_STRIDE);
-		writel_relaxed(0x0, l2x0_base + L2X0_LOCKDOWN_WAY_I_BASE +
-			       i * L2X0_LOCKDOWN_STRIDE);
-	}
+	l2c_unlock(l2x0_base, lockregs);
 }
 
 void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
Signature · 1 line
1.8.3.1
#13 lore raw

PATCH 13/222 ARM: l2c: provide generic helper for way-based operations

In reply to Russell King - ARM Linux

Provide a generic helper function for way based operations. These are always background operations, and thus have to be waited for before a new operation is commenced. This helper extracts that requirement from several locations in the code.

---
 arch/arm/mm/cache-l2x0.c | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)
arch/arm/mm/cache-l2x0.c +9 -6
index a1313d20f205..1c3a23318f53 100644
@@ -70,6 +70,12 @@ static inline void l2c_set_debug(void __iomem *base, unsigned long val)
 	outer_cache.set_debug(val);
 }
 
+static void __l2c_op_way(void __iomem *reg)
+{
+	writel_relaxed(l2x0_way_mask, reg);
+	cache_wait_way(reg, l2x0_way_mask);
+}
+
 static inline void l2c_unlock(void __iomem *base, unsigned num)
 {
 	unsigned i;
@@ -166,8 +172,7 @@ static void l2x0_cache_sync(void)
 static void __l2x0_flush_all(void)
 {
 	debug_writel(0x03);
-	writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_CLEAN_INV_WAY);
-	cache_wait_way(l2x0_base + L2X0_CLEAN_INV_WAY, l2x0_way_mask);
+	__l2c_op_way(l2x0_base + L2X0_CLEAN_INV_WAY);
 	cache_sync();
 	debug_writel(0x00);
 }
@@ -188,8 +193,7 @@ static void l2x0_clean_all(void)
 
 	/* clean all ways */
 	raw_spin_lock_irqsave(&l2x0_lock, flags);
-	writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_CLEAN_WAY);
-	cache_wait_way(l2x0_base + L2X0_CLEAN_WAY, l2x0_way_mask);
+	__l2c_op_way(l2x0_base + L2X0_CLEAN_WAY);
 	cache_sync();
 	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
 }
@@ -202,8 +206,7 @@ static void l2x0_inv_all(void)
 	raw_spin_lock_irqsave(&l2x0_lock, flags);
 	/* Invalidating when L2 is enabled is a nono */
 	BUG_ON(readl(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN);
-	writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_INV_WAY);
-	cache_wait_way(l2x0_base + L2X0_INV_WAY, l2x0_way_mask);
+	__l2c_op_way(l2x0_base + L2X0_INV_WAY);
 	cache_sync();
 	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
 }
Signature · 1 line
1.8.3.1
#14 lore raw

PATCH 14/222 ARM: l2c: rename cache_wait_way()

In reply to Russell King - ARM Linux

cache_wait_way() is actually used to wait for a particular mask to report clear; it's not really got much to do with cache ways at all. Indeed, it gets used to wait for the C bit to clear on older caches. Rename this with a more generic function name which better reflects its purpose: l2c_wait_mask().

---
 arch/arm/mm/cache-l2x0.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
arch/arm/mm/cache-l2x0.c +3 -3
index 1c3a23318f53..29ee7f692801 100644
@@ -53,7 +53,7 @@ static bool of_init = false;
 /*
  * Common code for all cache controllers.
  */
-static inline void cache_wait_way(void __iomem *reg, unsigned long mask)
+static inline void l2c_wait_mask(void __iomem *reg, unsigned long mask)
 {
 	/* wait for cache operation by line or way to complete */
 	while (readl_relaxed(reg) & mask)
@@ -73,7 +73,7 @@ static inline void l2c_set_debug(void __iomem *base, unsigned long val)
 static void __l2c_op_way(void __iomem *reg)
 {
 	writel_relaxed(l2x0_way_mask, reg);
-	cache_wait_way(reg, l2x0_way_mask);
+	l2c_wait_mask(reg, l2x0_way_mask);
 }
 
 static inline void l2c_unlock(void __iomem *base, unsigned num)
@@ -94,7 +94,7 @@ static inline void cache_wait(void __iomem *reg, unsigned long mask)
 	/* cache operations by line are atomic on PL310 */
 }
 #else
-#define cache_wait	cache_wait_way
+#define cache_wait	l2c_wait_mask
 #endif
 
 static inline void cache_sync(void)
Signature · 1 line
1.8.3.1
#15 lore raw

PATCH 15/222 ARM: l2c: add and use L2C revision constants

In reply to Russell King - ARM Linux

The revision namespace is specific to the L2 cache part, so don't name these with generic identifiers, use a part specific identifier.

---
 arch/arm/include/asm/hardware/cache-l2x0.h | 22 ++++++++++++++++------
 arch/arm/mm/cache-l2x0.c                   | 10 +++++-----
 2 files changed, 21 insertions(+), 11 deletions(-)
arch/arm/include/asm/hardware/cache-l2x0.h +16 -6
index 6795ff743b3d..3af45734b514 100644
@@ -68,14 +68,24 @@
 /* Registers shifts and masks */
 #define L2X0_CACHE_ID_PART_MASK		(0xf << 6)
 #define L2X0_CACHE_ID_PART_L210		(1 << 6)
+#define L2X0_CACHE_ID_PART_L220		(2 << 6)
 #define L2X0_CACHE_ID_PART_L310		(3 << 6)
 #define L2X0_CACHE_ID_RTL_MASK          0x3f
-#define L2X0_CACHE_ID_RTL_R0P0          0x0
-#define L2X0_CACHE_ID_RTL_R1P0          0x2
-#define L2X0_CACHE_ID_RTL_R2P0          0x4
-#define L2X0_CACHE_ID_RTL_R3P0          0x5
-#define L2X0_CACHE_ID_RTL_R3P1          0x6
-#define L2X0_CACHE_ID_RTL_R3P2          0x8
+#define L210_CACHE_ID_RTL_R0P2_02	0x00
+#define L210_CACHE_ID_RTL_R0P1		0x01
+#define L210_CACHE_ID_RTL_R0P2_01	0x02
+#define L210_CACHE_ID_RTL_R0P3		0x03
+#define L210_CACHE_ID_RTL_R0P4		0x0b
+#define L210_CACHE_ID_RTL_R0P5		0x0f
+#define L220_CACHE_ID_RTL_R1P7_01REL0	0x06
+#define L310_CACHE_ID_RTL_R0P0		0x00
+#define L310_CACHE_ID_RTL_R1P0		0x02
+#define L310_CACHE_ID_RTL_R2P0		0x04
+#define L310_CACHE_ID_RTL_R3P0		0x05
+#define L310_CACHE_ID_RTL_R3P1		0x06
+#define L310_CACHE_ID_RTL_R3P1_50REL0	0x07
+#define L310_CACHE_ID_RTL_R3P2		0x08
+#define L310_CACHE_ID_RTL_R3P3		0x09
 
 #define L2X0_AUX_CTRL_MASK			0xc0000fff
 #define L2X0_AUX_CTRL_DATA_RD_LATENCY_SHIFT	0
arch/arm/mm/cache-l2x0.c +5 -5
index 29ee7f692801..c39602ef2cdd 100644
@@ -374,7 +374,7 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
 		/* Unmapped register. */
 		sync_reg_offset = L2X0_DUMMY_REG;
 #endif
-		if ((cache_id & L2X0_CACHE_ID_RTL_MASK) <= L2X0_CACHE_ID_RTL_R3P0)
+		if ((cache_id & L2X0_CACHE_ID_RTL_MASK) <= L310_CACHE_ID_RTL_R3P0)
 			outer_cache.set_debug = pl310_set_debug;
 		break;
 	case L2X0_CACHE_ID_PART_L210:
@@ -768,7 +768,7 @@ static void __init pl310_save(void)
 	l2x0_saved_regs.filter_start = readl_relaxed(l2x0_base +
 		L2X0_ADDR_FILTER_START);
 
-	if (l2x0_revision >= L2X0_CACHE_ID_RTL_R2P0) {
+	if (l2x0_revision >= L310_CACHE_ID_RTL_R2P0) {
 		/*
 		 * From r2p0, there is Prefetch offset/control register
 		 */
@@ -777,7 +777,7 @@ static void __init pl310_save(void)
 		/*
 		 * From r3p0, there is Power control register
 		 */
-		if (l2x0_revision >= L2X0_CACHE_ID_RTL_R3P0)
+		if (l2x0_revision >= L310_CACHE_ID_RTL_R3P0)
 			l2x0_saved_regs.pwr_ctrl = readl_relaxed(l2x0_base +
 				L2X0_POWER_CTRL);
 	}
@@ -830,10 +830,10 @@ static void pl310_resume(void)
 		l2x0_revision = readl_relaxed(l2x0_base + L2X0_CACHE_ID) &
 			L2X0_CACHE_ID_RTL_MASK;
 
-		if (l2x0_revision >= L2X0_CACHE_ID_RTL_R2P0) {
+		if (l2x0_revision >= L310_CACHE_ID_RTL_R2P0) {
 			writel_relaxed(l2x0_saved_regs.prefetch_ctrl,
 				l2x0_base + L2X0_PREFETCH_CTRL);
-			if (l2x0_revision >= L2X0_CACHE_ID_RTL_R3P0)
+			if (l2x0_revision >= L310_CACHE_ID_RTL_R3P0)
 				writel_relaxed(l2x0_saved_regs.pwr_ctrl,
 					l2x0_base + L2X0_POWER_CTRL);
 		}
Signature · 1 line
1.8.3.1
#16 lore raw

PATCH 16/222 ARM: l2c: clean up OF initialisation a bit

In reply to Russell King - ARM Linux

Rather than having a boolean and other tricks to disable some bits of l2x0_init(), split this function into two parts: a common part shared between OF and non-OF, and the non-OF part.

The common part can take a block of function pointers, and the cache ID (to cope with Aurora's DT specified ID.) Eliminate the redundant setting of l2x0_base in the OF case, moving it to the non-OF init function.

This allows us to localise the OF-specific initialisation handling from the non-OF handling.

---
 arch/arm/mm/cache-l2x0.c | 66 +++++++++++++++++++++++++++++-------------------
 1 file changed, 40 insertions(+), 26 deletions(-)
arch/arm/mm/cache-l2x0.c +40 -26
index c39602ef2cdd..0d83b24b7971 100644
@@ -42,14 +42,8 @@ static u32 l2x0_way_mask;	/* Bitmask of active ways */
 static u32 l2x0_size;
 static unsigned long sync_reg_offset = L2X0_CACHE_SYNC;
 
-/* Aurora don't have the cache ID register available, so we have to
- * pass it though the device tree */
-static u32  cache_id_part_number_from_dt;
-
 struct l2x0_regs l2x0_saved_regs;
 
-static bool of_init = false;
-
 /*
  * Common code for all cache controllers.
  */
@@ -343,20 +337,26 @@ static void l2x0_unlock(u32 cache_id)
 	l2c_unlock(l2x0_base, lockregs);
 }
 
-void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
+static const struct l2c_init_data l2x0_init_fns __initconst = {
+	.outer_cache = {
+		.inv_range = l2x0_inv_range,
+		.clean_range = l2x0_clean_range,
+		.flush_range = l2x0_flush_range,
+		.flush_all = l2x0_flush_all,
+		.disable = l2x0_disable,
+		.sync = l2x0_cache_sync,
+	},
+};
+
+static void __init __l2c_init(const struct l2c_init_data *data,
+	u32 aux_val, u32 aux_mask, u32 cache_id)
 {
 	u32 aux;
-	u32 cache_id;
 	u32 way_size = 0;
 	int ways;
 	int way_size_shift = L2X0_WAY_SIZE_SHIFT;
 	const char *type;
 
-	l2x0_base = base;
-	if (cache_id_part_number_from_dt)
-		cache_id = cache_id_part_number_from_dt;
-	else
-		cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID);
 	aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
 
 	aux &= aux_mask;
@@ -374,8 +374,6 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
 		/* Unmapped register. */
 		sync_reg_offset = L2X0_DUMMY_REG;
 #endif
-		if ((cache_id & L2X0_CACHE_ID_RTL_MASK) <= L310_CACHE_ID_RTL_R3P0)
-			outer_cache.set_debug = pl310_set_debug;
 		break;
 	case L2X0_CACHE_ID_PART_L210:
 		ways = (aux >> 13) & 0xf;
@@ -430,23 +428,35 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
 	/* Save the value for resuming. */
 	l2x0_saved_regs.aux_ctrl = aux;
 
-	if (!of_init) {
-		outer_cache.inv_range = l2x0_inv_range;
-		outer_cache.clean_range = l2x0_clean_range;
-		outer_cache.flush_range = l2x0_flush_range;
-		outer_cache.sync = l2x0_cache_sync;
-		outer_cache.flush_all = l2x0_flush_all;
-		outer_cache.disable = l2x0_disable;
-	}
+	outer_cache = data->outer_cache;
+
+	if ((cache_id & L2X0_CACHE_ID_PART_MASK) == L2X0_CACHE_ID_PART_L310 &&
+	    (cache_id & L2X0_CACHE_ID_RTL_MASK) <= L310_CACHE_ID_RTL_R3P0)
+		outer_cache.set_debug = pl310_set_debug;
 
 	pr_info("%s cache controller enabled\n", type);
 	pr_info("l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x, Cache size: %d kB\n",
 		ways, cache_id, aux, l2x0_size >> 10);
 }
 
+void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
+{
+	u32 cache_id;
+
+	l2x0_base = base;
+
+	cache_id = readl_relaxed(base + L2X0_CACHE_ID);
+
+	__l2c_init(&l2x0_init_fns, aux_val, aux_mask, cache_id);
+}
+
 #ifdef CONFIG_OF
 static int l2_wt_override;
 
+/* Aurora don't have the cache ID register available, so we have to
+ * pass it though the device tree */
+static u32 cache_id_part_number_from_dt;
+
 /*
  * Note that the end addresses passed to Linux primitives are
  * noninclusive, while the hardware cache range operations use
@@ -985,6 +995,7 @@ int __init l2x0_of_init(u32 aux_val, u32 aux_mask)
 	const struct l2c_init_data *data;
 	struct device_node *np;
 	struct resource res;
+	u32 cache_id;
 
 	np = of_find_matching_node(NULL, l2x0_ids);
 	if (!np)
@@ -1015,9 +1026,12 @@ int __init l2x0_of_init(u32 aux_val, u32 aux_mask)
 	if (data->save)
 		data->save();
 
-	of_init = true;
-	memcpy(&outer_cache, &data->outer_cache, sizeof(outer_cache));
-	l2x0_init(l2x0_base, aux_val, aux_mask);
+	if (cache_id_part_number_from_dt)
+		cache_id = cache_id_part_number_from_dt;
+	else
+		cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID);
+
+	__l2c_init(data, aux_val, aux_mask, cache_id);
 
 	return 0;
 }
Signature · 1 line
1.8.3.1
#17 lore raw

PATCH 17/222 ARM: l2c: pass iomem address into data->save function

In reply to Russell King - ARM Linux

Pass the iomem address into this function so we don't have to keep accessing it from a global.

---
 arch/arm/mm/cache-l2x0.c | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)
arch/arm/mm/cache-l2x0.c +16 -16
index 0d83b24b7971..08f9cade028a 100644
@@ -30,7 +30,7 @@
 
 struct l2c_init_data {
 	void (*of_parse)(const struct device_node *, u32 *, u32 *);
-	void (*save)(void);
+	void (*save)(void __iomem *);
 	struct outer_cache_fns outer_cache;
 };
 
@@ -764,47 +764,47 @@ static void __init pl310_of_parse(const struct device_node *np,
 	}
 }
 
-static void __init pl310_save(void)
+static void __init pl310_save(void __iomem *base)
 {
-	u32 l2x0_revision = readl_relaxed(l2x0_base + L2X0_CACHE_ID) &
+	u32 l2x0_revision = readl_relaxed(base + L2X0_CACHE_ID) &
 		L2X0_CACHE_ID_RTL_MASK;
 
-	l2x0_saved_regs.tag_latency = readl_relaxed(l2x0_base +
+	l2x0_saved_regs.tag_latency = readl_relaxed(base +
 		L2X0_TAG_LATENCY_CTRL);
-	l2x0_saved_regs.data_latency = readl_relaxed(l2x0_base +
+	l2x0_saved_regs.data_latency = readl_relaxed(base +
 		L2X0_DATA_LATENCY_CTRL);
-	l2x0_saved_regs.filter_end = readl_relaxed(l2x0_base +
+	l2x0_saved_regs.filter_end = readl_relaxed(base +
 		L2X0_ADDR_FILTER_END);
-	l2x0_saved_regs.filter_start = readl_relaxed(l2x0_base +
+	l2x0_saved_regs.filter_start = readl_relaxed(base +
 		L2X0_ADDR_FILTER_START);
 
 	if (l2x0_revision >= L310_CACHE_ID_RTL_R2P0) {
 		/*
 		 * From r2p0, there is Prefetch offset/control register
 		 */
-		l2x0_saved_regs.prefetch_ctrl = readl_relaxed(l2x0_base +
+		l2x0_saved_regs.prefetch_ctrl = readl_relaxed(base +
 			L2X0_PREFETCH_CTRL);
 		/*
 		 * From r3p0, there is Power control register
 		 */
 		if (l2x0_revision >= L310_CACHE_ID_RTL_R3P0)
-			l2x0_saved_regs.pwr_ctrl = readl_relaxed(l2x0_base +
+			l2x0_saved_regs.pwr_ctrl = readl_relaxed(base +
 				L2X0_POWER_CTRL);
 	}
 }
 
-static void aurora_save(void)
+static void aurora_save(void __iomem *base)
 {
-	l2x0_saved_regs.ctrl = readl_relaxed(l2x0_base + L2X0_CTRL);
-	l2x0_saved_regs.aux_ctrl = readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
+	l2x0_saved_regs.ctrl = readl_relaxed(base + L2X0_CTRL);
+	l2x0_saved_regs.aux_ctrl = readl_relaxed(base + L2X0_AUX_CTRL);
 }
 
-static void __init tauros3_save(void)
+static void __init tauros3_save(void __iomem *base)
 {
 	l2x0_saved_regs.aux2_ctrl =
-		readl_relaxed(l2x0_base + TAUROS3_AUX2_CTRL);
+		readl_relaxed(base + TAUROS3_AUX2_CTRL);
 	l2x0_saved_regs.prefetch_ctrl =
-		readl_relaxed(l2x0_base + L2X0_PREFETCH_CTRL);
+		readl_relaxed(base + L2X0_PREFETCH_CTRL);
 }
 
 static void l2x0_resume(void)
@@ -1024,7 +1024,7 @@ int __init l2x0_of_init(u32 aux_val, u32 aux_mask)
 	}
 
 	if (data->save)
-		data->save();
+		data->save(l2x0_base);
 
 	if (cache_id_part_number_from_dt)
 		cache_id = cache_id_part_number_from_dt;
Signature · 1 line
1.8.3.1
#18 lore raw

PATCH 18/222 ARM: l2c: move l2c save function to __l2c_init()

In reply to Russell King - ARM Linux

There's no reason this functionality should be specific to DT, so move it into the common initialisation function.

---
 arch/arm/mm/cache-l2x0.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)
arch/arm/mm/cache-l2x0.c +7 -3
index 08f9cade028a..3b6213838054 100644
@@ -357,6 +357,13 @@ static void __init __l2c_init(const struct l2c_init_data *data,
 	int way_size_shift = L2X0_WAY_SIZE_SHIFT;
 	const char *type;
 
+	/*
+	 * It is strange to save the register state before initialisation,
+	 * but hey, this is what the DT implementations decided to do.
+	 */
+	if (data->save)
+		data->save(l2x0_base);
+
 	aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
 
 	aux &= aux_mask;
@@ -1023,9 +1030,6 @@ int __init l2x0_of_init(u32 aux_val, u32 aux_mask)
 			aurora_broadcast_l2_commands();
 	}
 
-	if (data->save)
-		data->save(l2x0_base);
-
 	if (cache_id_part_number_from_dt)
 		cache_id = cache_id_part_number_from_dt;
 	else
Signature · 1 line
1.8.3.1
#19 lore raw

PATCH 19/222 ARM: l2c: group implementation specific code together

In reply to Russell King - ARM Linux

Back in the mists of time, someone decided that it would be a good idea to group like functions together - so all the save functions in one place, all the resume functions in another, all the OF parsing functions some place else.

This makes it difficult to get an overview on what a particular implementation is doing - grouping an implementations specific functions together makes more sense, because you can see what it's doing without the clutter of other implementations.

Organise it according to implementation.

---
 arch/arm/mm/cache-l2x0.c | 502 +++++++++++++++++++++++------------------------
 1 file changed, 251 insertions(+), 251 deletions(-)
arch/arm/mm/cache-l2x0.c +251 -251
index 3b6213838054..09fe0f5eada5 100644
@@ -464,6 +464,175 @@ static int l2_wt_override;
  * pass it though the device tree */
 static u32 cache_id_part_number_from_dt;
 
+static void __init l2x0_of_parse(const struct device_node *np,
+				 u32 *aux_val, u32 *aux_mask)
+{
+	u32 data[2] = { 0, 0 };
+	u32 tag = 0;
+	u32 dirty = 0;
+	u32 val = 0, mask = 0;
+
+	of_property_read_u32(np, "arm,tag-latency", &tag);
+	if (tag) {
+		mask |= L2X0_AUX_CTRL_TAG_LATENCY_MASK;
+		val |= (tag - 1) << L2X0_AUX_CTRL_TAG_LATENCY_SHIFT;
+	}
+
+	of_property_read_u32_array(np, "arm,data-latency",
+				   data, ARRAY_SIZE(data));
+	if (data[0] && data[1]) {
+		mask |= L2X0_AUX_CTRL_DATA_RD_LATENCY_MASK |
+			L2X0_AUX_CTRL_DATA_WR_LATENCY_MASK;
+		val |= ((data[0] - 1) << L2X0_AUX_CTRL_DATA_RD_LATENCY_SHIFT) |
+		       ((data[1] - 1) << L2X0_AUX_CTRL_DATA_WR_LATENCY_SHIFT);
+	}
+
+	of_property_read_u32(np, "arm,dirty-latency", &dirty);
+	if (dirty) {
+		mask |= L2X0_AUX_CTRL_DIRTY_LATENCY_MASK;
+		val |= (dirty - 1) << L2X0_AUX_CTRL_DIRTY_LATENCY_SHIFT;
+	}
+
+	*aux_val &= ~mask;
+	*aux_val |= val;
+	*aux_mask &= ~mask;
+}
+
+static void l2x0_resume(void)
+{
+	if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
+		/* restore aux ctrl and enable l2 */
+		l2x0_unlock(readl_relaxed(l2x0_base + L2X0_CACHE_ID));
+
+		writel_relaxed(l2x0_saved_regs.aux_ctrl, l2x0_base +
+			L2X0_AUX_CTRL);
+
+		l2x0_inv_all();
+
+		writel_relaxed(L2X0_CTRL_EN, l2x0_base + L2X0_CTRL);
+	}
+}
+
+static const struct l2c_init_data of_l2x0_data __initconst = {
+	.of_parse = l2x0_of_parse,
+	.outer_cache = {
+		.inv_range   = l2x0_inv_range,
+		.clean_range = l2x0_clean_range,
+		.flush_range = l2x0_flush_range,
+		.flush_all   = l2x0_flush_all,
+		.disable     = l2x0_disable,
+		.sync        = l2x0_cache_sync,
+		.resume      = l2x0_resume,
+	},
+};
+
+static void __init pl310_of_parse(const struct device_node *np,
+				  u32 *aux_val, u32 *aux_mask)
+{
+	u32 data[3] = { 0, 0, 0 };
+	u32 tag[3] = { 0, 0, 0 };
+	u32 filter[2] = { 0, 0 };
+
+	of_property_read_u32_array(np, "arm,tag-latency", tag, ARRAY_SIZE(tag));
+	if (tag[0] && tag[1] && tag[2])
+		writel_relaxed(
+			((tag[0] - 1) << L2X0_LATENCY_CTRL_RD_SHIFT) |
+			((tag[1] - 1) << L2X0_LATENCY_CTRL_WR_SHIFT) |
+			((tag[2] - 1) << L2X0_LATENCY_CTRL_SETUP_SHIFT),
+			l2x0_base + L2X0_TAG_LATENCY_CTRL);
+
+	of_property_read_u32_array(np, "arm,data-latency",
+				   data, ARRAY_SIZE(data));
+	if (data[0] && data[1] && data[2])
+		writel_relaxed(
+			((data[0] - 1) << L2X0_LATENCY_CTRL_RD_SHIFT) |
+			((data[1] - 1) << L2X0_LATENCY_CTRL_WR_SHIFT) |
+			((data[2] - 1) << L2X0_LATENCY_CTRL_SETUP_SHIFT),
+			l2x0_base + L2X0_DATA_LATENCY_CTRL);
+
+	of_property_read_u32_array(np, "arm,filter-ranges",
+				   filter, ARRAY_SIZE(filter));
+	if (filter[1]) {
+		writel_relaxed(ALIGN(filter[0] + filter[1], SZ_1M),
+			       l2x0_base + L2X0_ADDR_FILTER_END);
+		writel_relaxed((filter[0] & ~(SZ_1M - 1)) | L2X0_ADDR_FILTER_EN,
+			       l2x0_base + L2X0_ADDR_FILTER_START);
+	}
+}
+
+static void __init pl310_save(void __iomem *base)
+{
+	u32 l2x0_revision = readl_relaxed(base + L2X0_CACHE_ID) &
+		L2X0_CACHE_ID_RTL_MASK;
+
+	l2x0_saved_regs.tag_latency = readl_relaxed(base +
+		L2X0_TAG_LATENCY_CTRL);
+	l2x0_saved_regs.data_latency = readl_relaxed(base +
+		L2X0_DATA_LATENCY_CTRL);
+	l2x0_saved_regs.filter_end = readl_relaxed(base +
+		L2X0_ADDR_FILTER_END);
+	l2x0_saved_regs.filter_start = readl_relaxed(base +
+		L2X0_ADDR_FILTER_START);
+
+	if (l2x0_revision >= L310_CACHE_ID_RTL_R2P0) {
+		/*
+		 * From r2p0, there is Prefetch offset/control register
+		 */
+		l2x0_saved_regs.prefetch_ctrl = readl_relaxed(base +
+			L2X0_PREFETCH_CTRL);
+		/*
+		 * From r3p0, there is Power control register
+		 */
+		if (l2x0_revision >= L310_CACHE_ID_RTL_R3P0)
+			l2x0_saved_regs.pwr_ctrl = readl_relaxed(base +
+				L2X0_POWER_CTRL);
+	}
+}
+
+static void pl310_resume(void)
+{
+	u32 l2x0_revision;
+
+	if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
+		/* restore pl310 setup */
+		writel_relaxed(l2x0_saved_regs.tag_latency,
+			l2x0_base + L2X0_TAG_LATENCY_CTRL);
+		writel_relaxed(l2x0_saved_regs.data_latency,
+			l2x0_base + L2X0_DATA_LATENCY_CTRL);
+		writel_relaxed(l2x0_saved_regs.filter_end,
+			l2x0_base + L2X0_ADDR_FILTER_END);
+		writel_relaxed(l2x0_saved_regs.filter_start,
+			l2x0_base + L2X0_ADDR_FILTER_START);
+
+		l2x0_revision = readl_relaxed(l2x0_base + L2X0_CACHE_ID) &
+			L2X0_CACHE_ID_RTL_MASK;
+
+		if (l2x0_revision >= L310_CACHE_ID_RTL_R2P0) {
+			writel_relaxed(l2x0_saved_regs.prefetch_ctrl,
+				l2x0_base + L2X0_PREFETCH_CTRL);
+			if (l2x0_revision >= L310_CACHE_ID_RTL_R3P0)
+				writel_relaxed(l2x0_saved_regs.pwr_ctrl,
+					l2x0_base + L2X0_POWER_CTRL);
+		}
+	}
+
+	l2x0_resume();
+}
+
+static const struct l2c_init_data of_pl310_data __initconst = {
+	.of_parse = pl310_of_parse,
+	.save  = pl310_save,
+	.outer_cache = {
+		.inv_range   = l2x0_inv_range,
+		.clean_range = l2x0_clean_range,
+		.flush_range = l2x0_flush_range,
+		.flush_all   = l2x0_flush_all,
+		.disable     = l2x0_disable,
+		.sync        = l2x0_cache_sync,
+		.resume      = pl310_resume,
+	},
+};
+
 /*
  * Note that the end addresses passed to Linux primitives are
  * noninclusive, while the hardware cache range operations use
@@ -562,6 +731,75 @@ static void aurora_flush_range(unsigned long start, unsigned long end)
 	}
 }
 
+static void aurora_save(void __iomem *base)
+{
+	l2x0_saved_regs.ctrl = readl_relaxed(base + L2X0_CTRL);
+	l2x0_saved_regs.aux_ctrl = readl_relaxed(base + L2X0_AUX_CTRL);
+}
+
+static void aurora_resume(void)
+{
+	if (!(readl(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
+		writel_relaxed(l2x0_saved_regs.aux_ctrl,
+				l2x0_base + L2X0_AUX_CTRL);
+		writel_relaxed(l2x0_saved_regs.ctrl, l2x0_base + L2X0_CTRL);
+	}
+}
+
+static void __init aurora_broadcast_l2_commands(void)
+{
+	__u32 u;
+	/* Enable Broadcasting of cache commands to L2*/
+	__asm__ __volatile__("mrc p15, 1, %0, c15, c2, 0" : "=r"(u));
+	u |= AURORA_CTRL_FW;		/* Set the FW bit */
+	__asm__ __volatile__("mcr p15, 1, %0, c15, c2, 0\n" : : "r"(u));
+	isb();
+}
+
+static void __init aurora_of_parse(const struct device_node *np,
+				u32 *aux_val, u32 *aux_mask)
+{
+	u32 val = AURORA_ACR_REPLACEMENT_TYPE_SEMIPLRU;
+	u32 mask =  AURORA_ACR_REPLACEMENT_MASK;
+
+	of_property_read_u32(np, "cache-id-part",
+			&cache_id_part_number_from_dt);
+
+	/* Determine and save the write policy */
+	l2_wt_override = of_property_read_bool(np, "wt-override");
+
+	if (l2_wt_override) {
+		val |= AURORA_ACR_FORCE_WRITE_THRO_POLICY;
+		mask |= AURORA_ACR_FORCE_WRITE_POLICY_MASK;
+	}
+
+	*aux_val &= ~mask;
+	*aux_val |= val;
+	*aux_mask &= ~mask;
+}
+
+static const struct l2c_init_data of_aurora_with_outer_data __initconst = {
+	.of_parse = aurora_of_parse,
+	.save  = aurora_save,
+	.outer_cache = {
+		.inv_range   = aurora_inv_range,
+		.clean_range = aurora_clean_range,
+		.flush_range = aurora_flush_range,
+		.flush_all   = l2x0_flush_all,
+		.disable     = l2x0_disable,
+		.sync        = l2x0_cache_sync,
+		.resume      = aurora_resume,
+	},
+};
+
+static const struct l2c_init_data of_aurora_no_outer_data __initconst = {
+	.of_parse = aurora_of_parse,
+	.save  = aurora_save,
+	.outer_cache = {
+		.resume      = aurora_resume,
+	},
+};
+
 /*
  * For certain Broadcom SoCs, depending on the address range, different offsets
  * need to be added to the address before passing it to L2 for
@@ -703,108 +941,19 @@ static void bcm_flush_range(unsigned long start, unsigned long end)
 		new_end);
 }
 
-static void __init l2x0_of_parse(const struct device_node *np,
-				 u32 *aux_val, u32 *aux_mask)
-{
-	u32 data[2] = { 0, 0 };
-	u32 tag = 0;
-	u32 dirty = 0;
-	u32 val = 0, mask = 0;
-
-	of_property_read_u32(np, "arm,tag-latency", &tag);
-	if (tag) {
-		mask |= L2X0_AUX_CTRL_TAG_LATENCY_MASK;
-		val |= (tag - 1) << L2X0_AUX_CTRL_TAG_LATENCY_SHIFT;
-	}
-
-	of_property_read_u32_array(np, "arm,data-latency",
-				   data, ARRAY_SIZE(data));
-	if (data[0] && data[1]) {
-		mask |= L2X0_AUX_CTRL_DATA_RD_LATENCY_MASK |
-			L2X0_AUX_CTRL_DATA_WR_LATENCY_MASK;
-		val |= ((data[0] - 1) << L2X0_AUX_CTRL_DATA_RD_LATENCY_SHIFT) |
-		       ((data[1] - 1) << L2X0_AUX_CTRL_DATA_WR_LATENCY_SHIFT);
-	}
-
-	of_property_read_u32(np, "arm,dirty-latency", &dirty);
-	if (dirty) {
-		mask |= L2X0_AUX_CTRL_DIRTY_LATENCY_MASK;
-		val |= (dirty - 1) << L2X0_AUX_CTRL_DIRTY_LATENCY_SHIFT;
-	}
-
-	*aux_val &= ~mask;
-	*aux_val |= val;
-	*aux_mask &= ~mask;
-}
-
-static void __init pl310_of_parse(const struct device_node *np,
-				  u32 *aux_val, u32 *aux_mask)
-{
-	u32 data[3] = { 0, 0, 0 };
-	u32 tag[3] = { 0, 0, 0 };
-	u32 filter[2] = { 0, 0 };
-
-	of_property_read_u32_array(np, "arm,tag-latency", tag, ARRAY_SIZE(tag));
-	if (tag[0] && tag[1] && tag[2])
-		writel_relaxed(
-			((tag[0] - 1) << L2X0_LATENCY_CTRL_RD_SHIFT) |
-			((tag[1] - 1) << L2X0_LATENCY_CTRL_WR_SHIFT) |
-			((tag[2] - 1) << L2X0_LATENCY_CTRL_SETUP_SHIFT),
-			l2x0_base + L2X0_TAG_LATENCY_CTRL);
-
-	of_property_read_u32_array(np, "arm,data-latency",
-				   data, ARRAY_SIZE(data));
-	if (data[0] && data[1] && data[2])
-		writel_relaxed(
-			((data[0] - 1) << L2X0_LATENCY_CTRL_RD_SHIFT) |
-			((data[1] - 1) << L2X0_LATENCY_CTRL_WR_SHIFT) |
-			((data[2] - 1) << L2X0_LATENCY_CTRL_SETUP_SHIFT),
-			l2x0_base + L2X0_DATA_LATENCY_CTRL);
-
-	of_property_read_u32_array(np, "arm,filter-ranges",
-				   filter, ARRAY_SIZE(filter));
-	if (filter[1]) {
-		writel_relaxed(ALIGN(filter[0] + filter[1], SZ_1M),
-			       l2x0_base + L2X0_ADDR_FILTER_END);
-		writel_relaxed((filter[0] & ~(SZ_1M - 1)) | L2X0_ADDR_FILTER_EN,
-			       l2x0_base + L2X0_ADDR_FILTER_START);
-	}
-}
-
-static void __init pl310_save(void __iomem *base)
-{
-	u32 l2x0_revision = readl_relaxed(base + L2X0_CACHE_ID) &
-		L2X0_CACHE_ID_RTL_MASK;
-
-	l2x0_saved_regs.tag_latency = readl_relaxed(base +
-		L2X0_TAG_LATENCY_CTRL);
-	l2x0_saved_regs.data_latency = readl_relaxed(base +
-		L2X0_DATA_LATENCY_CTRL);
-	l2x0_saved_regs.filter_end = readl_relaxed(base +
-		L2X0_ADDR_FILTER_END);
-	l2x0_saved_regs.filter_start = readl_relaxed(base +
-		L2X0_ADDR_FILTER_START);
-
-	if (l2x0_revision >= L310_CACHE_ID_RTL_R2P0) {
-		/*
-		 * From r2p0, there is Prefetch offset/control register
-		 */
-		l2x0_saved_regs.prefetch_ctrl = readl_relaxed(base +
-			L2X0_PREFETCH_CTRL);
-		/*
-		 * From r3p0, there is Power control register
-		 */
-		if (l2x0_revision >= L310_CACHE_ID_RTL_R3P0)
-			l2x0_saved_regs.pwr_ctrl = readl_relaxed(base +
-				L2X0_POWER_CTRL);
-	}
-}
-
-static void aurora_save(void __iomem *base)
-{
-	l2x0_saved_regs.ctrl = readl_relaxed(base + L2X0_CTRL);
-	l2x0_saved_regs.aux_ctrl = readl_relaxed(base + L2X0_AUX_CTRL);
-}
+static const struct l2c_init_data of_bcm_l2x0_data __initconst = {
+	.of_parse = pl310_of_parse,
+	.save  = pl310_save,
+	.outer_cache = {
+		.inv_range   = bcm_inv_range,
+		.clean_range = bcm_clean_range,
+		.flush_range = bcm_flush_range,
+		.flush_all   = l2x0_flush_all,
+		.disable     = l2x0_disable,
+		.sync        = l2x0_cache_sync,
+		.resume      = pl310_resume,
+	},
+};
 
 static void __init tauros3_save(void __iomem *base)
 {
@@ -814,60 +963,6 @@ static void __init tauros3_save(void __iomem *base)
 		readl_relaxed(base + L2X0_PREFETCH_CTRL);
 }
 
-static void l2x0_resume(void)
-{
-	if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
-		/* restore aux ctrl and enable l2 */
-		l2x0_unlock(readl_relaxed(l2x0_base + L2X0_CACHE_ID));
-
-		writel_relaxed(l2x0_saved_regs.aux_ctrl, l2x0_base +
-			L2X0_AUX_CTRL);
-
-		l2x0_inv_all();
-
-		writel_relaxed(L2X0_CTRL_EN, l2x0_base + L2X0_CTRL);
-	}
-}
-
-static void pl310_resume(void)
-{
-	u32 l2x0_revision;
-
-	if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
-		/* restore pl310 setup */
-		writel_relaxed(l2x0_saved_regs.tag_latency,
-			l2x0_base + L2X0_TAG_LATENCY_CTRL);
-		writel_relaxed(l2x0_saved_regs.data_latency,
-			l2x0_base + L2X0_DATA_LATENCY_CTRL);
-		writel_relaxed(l2x0_saved_regs.filter_end,
-			l2x0_base + L2X0_ADDR_FILTER_END);
-		writel_relaxed(l2x0_saved_regs.filter_start,
-			l2x0_base + L2X0_ADDR_FILTER_START);
-
-		l2x0_revision = readl_relaxed(l2x0_base + L2X0_CACHE_ID) &
-			L2X0_CACHE_ID_RTL_MASK;
-
-		if (l2x0_revision >= L310_CACHE_ID_RTL_R2P0) {
-			writel_relaxed(l2x0_saved_regs.prefetch_ctrl,
-				l2x0_base + L2X0_PREFETCH_CTRL);
-			if (l2x0_revision >= L310_CACHE_ID_RTL_R3P0)
-				writel_relaxed(l2x0_saved_regs.pwr_ctrl,
-					l2x0_base + L2X0_POWER_CTRL);
-		}
-	}
-
-	l2x0_resume();
-}
-
-static void aurora_resume(void)
-{
-	if (!(readl(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
-		writel_relaxed(l2x0_saved_regs.aux_ctrl,
-				l2x0_base + L2X0_AUX_CTRL);
-		writel_relaxed(l2x0_saved_regs.ctrl, l2x0_base + L2X0_CTRL);
-	}
-}
-
 static void tauros3_resume(void)
 {
 	if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
@@ -880,87 +975,6 @@ static void tauros3_resume(void)
 	l2x0_resume();
 }
 
-static void __init aurora_broadcast_l2_commands(void)
-{
-	__u32 u;
-	/* Enable Broadcasting of cache commands to L2*/
-	__asm__ __volatile__("mrc p15, 1, %0, c15, c2, 0" : "=r"(u));
-	u |= AURORA_CTRL_FW;		/* Set the FW bit */
-	__asm__ __volatile__("mcr p15, 1, %0, c15, c2, 0\n" : : "r"(u));
-	isb();
-}
-
-static void __init aurora_of_parse(const struct device_node *np,
-				u32 *aux_val, u32 *aux_mask)
-{
-	u32 val = AURORA_ACR_REPLACEMENT_TYPE_SEMIPLRU;
-	u32 mask =  AURORA_ACR_REPLACEMENT_MASK;
-
-	of_property_read_u32(np, "cache-id-part",
-			&cache_id_part_number_from_dt);
-
-	/* Determine and save the write policy */
-	l2_wt_override = of_property_read_bool(np, "wt-override");
-
-	if (l2_wt_override) {
-		val |= AURORA_ACR_FORCE_WRITE_THRO_POLICY;
-		mask |= AURORA_ACR_FORCE_WRITE_POLICY_MASK;
-	}
-
-	*aux_val &= ~mask;
-	*aux_val |= val;
-	*aux_mask &= ~mask;
-}
-
-static const struct l2c_init_data of_pl310_data __initconst = {
-	.of_parse = pl310_of_parse,
-	.save  = pl310_save,
-	.outer_cache = {
-		.inv_range   = l2x0_inv_range,
-		.clean_range = l2x0_clean_range,
-		.flush_range = l2x0_flush_range,
-		.flush_all   = l2x0_flush_all,
-		.disable     = l2x0_disable,
-		.sync        = l2x0_cache_sync,
-		.resume      = pl310_resume,
-	},
-};
-
-static const struct l2c_init_data of_l2x0_data __initconst = {
-	.of_parse = l2x0_of_parse,
-	.outer_cache = {
-		.inv_range   = l2x0_inv_range,
-		.clean_range = l2x0_clean_range,
-		.flush_range = l2x0_flush_range,
-		.flush_all   = l2x0_flush_all,
-		.disable     = l2x0_disable,
-		.sync        = l2x0_cache_sync,
-		.resume      = l2x0_resume,
-	},
-};
-
-static const struct l2c_init_data of_aurora_with_outer_data __initconst = {
-	.of_parse = aurora_of_parse,
-	.save  = aurora_save,
-	.outer_cache = {
-		.inv_range   = aurora_inv_range,
-		.clean_range = aurora_clean_range,
-		.flush_range = aurora_flush_range,
-		.flush_all   = l2x0_flush_all,
-		.disable     = l2x0_disable,
-		.sync        = l2x0_cache_sync,
-		.resume      = aurora_resume,
-	},
-};
-
-static const struct l2c_init_data of_aurora_no_outer_data __initconst = {
-	.of_parse = aurora_of_parse,
-	.save  = aurora_save,
-	.outer_cache = {
-		.resume      = aurora_resume,
-	},
-};
-
 static const struct l2c_init_data of_tauros3_data __initconst = {
 	.save  = tauros3_save,
 	/* Tauros3 broadcasts L1 cache operations to L2 */
@@ -969,20 +983,6 @@ static const struct l2c_init_data of_tauros3_data __initconst = {
 	},
 };
 
-static const struct l2c_init_data of_bcm_l2x0_data __initconst = {
-	.of_parse = pl310_of_parse,
-	.save  = pl310_save,
-	.outer_cache = {
-		.inv_range   = bcm_inv_range,
-		.clean_range = bcm_clean_range,
-		.flush_range = bcm_flush_range,
-		.flush_all   = l2x0_flush_all,
-		.disable     = l2x0_disable,
-		.sync        = l2x0_cache_sync,
-		.resume      = pl310_resume,
-	},
-};
-
 #define L2C_ID(name, fns) { .compatible = name, .data = (void *)&fns }
 static const struct of_device_id l2x0_ids[] __initconst = {
 	L2C_ID("arm,l210-cache", of_l2x0_data),
Signature · 1 line
1.8.3.1
#20 lore raw

PATCH 20/222 ARM: l2c: provide enable method

In reply to Russell King - ARM Linux

Providing an enable method gives L2 cache controllers a chance to do special handling at enable time. This allows us to remove a hack in l2x0_unlock() for Marvell Aurora L2 caches.

---
 arch/arm/mm/cache-l2x0.c | 80 +++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 62 insertions(+), 18 deletions(-)
arch/arm/mm/cache-l2x0.c +62 -18
index 09fe0f5eada5..2adb82e7f4b3 100644
@@ -29,7 +29,9 @@
 #include "cache-aurora-l2.h"
 
 struct l2c_init_data {
+	unsigned num_lock;
 	void (*of_parse)(const struct device_node *, u32 *, u32 *);
+	void (*enable)(void __iomem *, u32, unsigned);
 	void (*save)(void __iomem *);
 	struct outer_cache_fns outer_cache;
 };
@@ -82,6 +84,36 @@ static inline void l2c_unlock(void __iomem *base, unsigned num)
 	}
 }
 
+/*
+ * Enable the L2 cache controller.  This function must only be
+ * called when the cache controller is known to be disabled.
+ */
+static void l2c_enable(void __iomem *base, u32 aux, unsigned num_lock)
+{
+	unsigned long flags;
+
+	l2c_unlock(base, num_lock);
+
+	writel_relaxed(aux, base + L2X0_AUX_CTRL);
+
+	local_irq_save(flags);
+	__l2c_op_way(base + L2X0_INV_WAY);
+	writel_relaxed(0, base + sync_reg_offset);
+	l2c_wait_mask(base + sync_reg_offset, 1);
+	local_irq_restore(flags);
+
+	writel_relaxed(L2X0_CTRL_EN, base + L2X0_CTRL);
+}
+
+static void l2c_disable(void)
+{
+	void __iomem *base = l2x0_base;
+
+	outer_cache.flush_all();
+	writel_relaxed(0, base + L2X0_CTRL);
+	dsb(st);
+}
+
 #ifdef CONFIG_CACHE_PL310
 static inline void cache_wait(void __iomem *reg, unsigned long mask)
 {
@@ -325,9 +357,6 @@ static void l2x0_unlock(u32 cache_id)
 	case L2X0_CACHE_ID_PART_L310:
 		lockregs = 8;
 		break;
-	case AURORA_CACHE_ID:
-		lockregs = 4;
-		break;
 	default:
 		/* L210 and unknown types */
 		lockregs = 1;
@@ -337,7 +366,22 @@ static void l2x0_unlock(u32 cache_id)
 	l2c_unlock(l2x0_base, lockregs);
 }
 
+static void l2x0_enable(void __iomem *base, u32 aux, unsigned num_lock)
+{
+	/* Make sure that I&D is not locked down when starting */
+	l2x0_unlock(readl_relaxed(base + L2X0_CACHE_ID));
+
+	/* l2x0 controller is disabled */
+	writel_relaxed(aux, base + L2X0_AUX_CTRL);
+
+	l2x0_inv_all();
+
+	/* enable L2X0 */
+	writel_relaxed(L2X0_CTRL_EN, base + L2X0_CTRL);
+}
+
 static const struct l2c_init_data l2x0_init_fns __initconst = {
+	.enable = l2x0_enable,
 	.outer_cache = {
 		.inv_range = l2x0_inv_range,
 		.clean_range = l2x0_clean_range,
@@ -412,22 +456,11 @@ static void __init __l2c_init(const struct l2c_init_data *data,
 	l2x0_size = ways * way_size * SZ_1K;
 
 	/*
-	 * Check if l2x0 controller is already enabled.
-	 * If you are booting from non-secure mode
-	 * accessing the below registers will fault.
+	 * Check if l2x0 controller is already enabled.  If we are booting
+	 * in non-secure mode accessing the below registers will fault.
 	 */
-	if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
-		/* Make sure that I&D is not locked down when starting */
-		l2x0_unlock(cache_id);
-
-		/* l2x0 controller is disabled */
-		writel_relaxed(aux, l2x0_base + L2X0_AUX_CTRL);
-
-		l2x0_inv_all();
-
-		/* enable L2X0 */
-		writel_relaxed(L2X0_CTRL_EN, l2x0_base + L2X0_CTRL);
-	}
+	if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN))
+		data->enable(l2x0_base, aux, data->num_lock);
 
 	/* Re-read it in case some bits are reserved. */
 	aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
@@ -515,6 +548,7 @@ static void l2x0_resume(void)
 
 static const struct l2c_init_data of_l2x0_data __initconst = {
 	.of_parse = l2x0_of_parse,
+	.enable = l2x0_enable,
 	.outer_cache = {
 		.inv_range   = l2x0_inv_range,
 		.clean_range = l2x0_clean_range,
@@ -620,7 +654,9 @@ static void pl310_resume(void)
 }
 
 static const struct l2c_init_data of_pl310_data __initconst = {
+	.num_lock = 8,
 	.of_parse = pl310_of_parse,
+	.enable = l2c_enable,
 	.save  = pl310_save,
 	.outer_cache = {
 		.inv_range   = l2x0_inv_range,
@@ -779,7 +815,9 @@ static void __init aurora_of_parse(const struct device_node *np,
 }
 
 static const struct l2c_init_data of_aurora_with_outer_data __initconst = {
+	.num_lock = 4,
 	.of_parse = aurora_of_parse,
+	.enable = l2c_enable,
 	.save  = aurora_save,
 	.outer_cache = {
 		.inv_range   = aurora_inv_range,
@@ -793,7 +831,9 @@ static const struct l2c_init_data of_aurora_with_outer_data __initconst = {
 };
 
 static const struct l2c_init_data of_aurora_no_outer_data __initconst = {
+	.num_lock = 4,
 	.of_parse = aurora_of_parse,
+	.enable = l2c_enable,
 	.save  = aurora_save,
 	.outer_cache = {
 		.resume      = aurora_resume,
@@ -942,7 +982,9 @@ static void bcm_flush_range(unsigned long start, unsigned long end)
 }
 
 static const struct l2c_init_data of_bcm_l2x0_data __initconst = {
+	.num_lock = 8,
 	.of_parse = pl310_of_parse,
+	.enable = l2c_enable,
 	.save  = pl310_save,
 	.outer_cache = {
 		.inv_range   = bcm_inv_range,
@@ -976,6 +1018,8 @@ static void tauros3_resume(void)
 }
 
 static const struct l2c_init_data of_tauros3_data __initconst = {
+	.num_lock = 8,
+	.enable = l2c_enable,
 	.save  = tauros3_save,
 	/* Tauros3 broadcasts L1 cache operations to L2 */
 	.outer_cache = {
Signature · 1 line
1.8.3.1
#21 lore raw

PATCH 21/222 ARM: l2c: write auxctrl register before unlocking

In reply to Russell King - ARM Linux

We should write the auxillary control register before unlocking: the write may be necessary to enable non-secure access to the lock registers.

---
 arch/arm/mm/cache-l2x0.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)
arch/arm/mm/cache-l2x0.c +5 -5
index 2adb82e7f4b3..fc609550b7fa 100644
@@ -92,10 +92,10 @@ static void l2c_enable(void __iomem *base, u32 aux, unsigned num_lock)
 {
 	unsigned long flags;
 
-	l2c_unlock(base, num_lock);
-
 	writel_relaxed(aux, base + L2X0_AUX_CTRL);
 
+	l2c_unlock(base, num_lock);
+
 	local_irq_save(flags);
 	__l2c_op_way(base + L2X0_INV_WAY);
 	writel_relaxed(0, base + sync_reg_offset);
@@ -368,12 +368,12 @@ static void l2x0_unlock(u32 cache_id)
 
 static void l2x0_enable(void __iomem *base, u32 aux, unsigned num_lock)
 {
-	/* Make sure that I&D is not locked down when starting */
-	l2x0_unlock(readl_relaxed(base + L2X0_CACHE_ID));
-
 	/* l2x0 controller is disabled */
 	writel_relaxed(aux, base + L2X0_AUX_CTRL);
 
+	/* Make sure that I&D is not locked down when starting */
+	l2x0_unlock(readl_relaxed(base + L2X0_CACHE_ID));
+
 	l2x0_inv_all();
 
 	/* enable L2X0 */
Signature · 1 line
1.8.3.1
#22 lore raw

PATCH 22/222 ARM: l2c: only write the auxiliary control register if required

In reply to Russell King - ARM Linux

Avoid unnecessary writes to the auxiliary control register if the register already contains the required value. This allows us to avoid invoking the platforms secure monitor code unnecessarily.

---
 arch/arm/mm/cache-l2x0.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
arch/arm/mm/cache-l2x0.c +3 -1
index fc609550b7fa..1c947b4c7f05 100644
@@ -92,7 +92,9 @@ static void l2c_enable(void __iomem *base, u32 aux, unsigned num_lock)
 {
 	unsigned long flags;
 
-	writel_relaxed(aux, base + L2X0_AUX_CTRL);
+	/* Only write the aux register if it needs changing */
+	if (readl_relaxed(base + L2X0_AUX_CTRL) != aux)
+		writel_relaxed(aux, base + L2X0_AUX_CTRL);
 
 	l2c_unlock(base, num_lock);
 
Signature · 1 line
1.8.3.1
#23 lore raw

PATCH 23/222 ARM: l2c: move aurora broadcast setup to enable function

In reply to Russell King - ARM Linux

Rather than having this hacked into the OF initialiation function, we can handle this via the enable function instead. While here, clean up that code and comments a little.

---
 arch/arm/mm/cache-l2x0.c | 28 +++++++++++++++-------------
 1 file changed, 15 insertions(+), 13 deletions(-)
arch/arm/mm/cache-l2x0.c +15 -13
index 1c947b4c7f05..5f381af1a7a4 100644
@@ -784,14 +784,22 @@ static void aurora_resume(void)
 	}
 }
 
-static void __init aurora_broadcast_l2_commands(void)
+/*
+ * For Aurora cache in no outer mode, enable via the CP15 coprocessor
+ * broadcasting of cache commands to L2.
+ */
+static void __init aurora_enable_no_outer(void __iomem *base, u32 aux,
+	unsigned num_lock)
 {
-	__u32 u;
-	/* Enable Broadcasting of cache commands to L2*/
-	__asm__ __volatile__("mrc p15, 1, %0, c15, c2, 0" : "=r"(u));
+	u32 u;
+
+	asm volatile("mrc p15, 1, %0, c15, c2, 0" : "=r" (u));
 	u |= AURORA_CTRL_FW;		/* Set the FW bit */
-	__asm__ __volatile__("mcr p15, 1, %0, c15, c2, 0\n" : : "r"(u));
+	asm volatile("mcr p15, 1, %0, c15, c2, 0" : : "r" (u));
+
 	isb();
+
+	l2c_enable(base, aux, num_lock);
 }
 
 static void __init aurora_of_parse(const struct device_node *np,
@@ -835,7 +843,7 @@ static const struct l2c_init_data of_aurora_with_outer_data __initconst = {
 static const struct l2c_init_data of_aurora_no_outer_data __initconst = {
 	.num_lock = 4,
 	.of_parse = aurora_of_parse,
-	.enable = l2c_enable,
+	.enable = aurora_enable_no_outer,
 	.save  = aurora_save,
 	.outer_cache = {
 		.resume      = aurora_resume,
@@ -1066,16 +1074,10 @@ int __init l2x0_of_init(u32 aux_val, u32 aux_mask)
 	data = of_match_node(l2x0_ids, np)->data;
 
 	/* L2 configuration can only be changed if the cache is disabled */
-	if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
+	if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN))
 		if (data->of_parse)
 			data->of_parse(np, &aux_val, &aux_mask);
 
-		/* For aurora cache in no outer mode select the
-		 * correct mode using the coprocessor*/
-		if (data == &of_aurora_no_outer_data)
-			aurora_broadcast_l2_commands();
-	}
-
 	if (cache_id_part_number_from_dt)
 		cache_id = cache_id_part_number_from_dt;
 	else
Signature · 1 line
1.8.3.1
#24 lore raw

PATCH 24/222 ARM: l2c: implement fixups for L2 cache controller quirks/errata

In reply to Russell King - ARM Linux

Rather than putting quirk handling in __l2c_init(), move it out to a separate function which individual implementations can specify. This helps to localise the quirks to those implementations which require them.

---
 arch/arm/mm/cache-l2x0.c | 112 ++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 101 insertions(+), 11 deletions(-)
arch/arm/mm/cache-l2x0.c +101 -11
index 5f381af1a7a4..a544f19c448f 100644
@@ -32,6 +32,7 @@ struct l2c_init_data {
 	unsigned num_lock;
 	void (*of_parse)(const struct device_node *, u32 *, u32 *);
 	void (*enable)(void __iomem *, u32, unsigned);
+	void (*fixup)(void __iomem *, u32, struct outer_cache_fns *);
 	void (*save)(void __iomem *);
 	struct outer_cache_fns outer_cache;
 };
@@ -394,9 +395,80 @@ static const struct l2c_init_data l2x0_init_fns __initconst = {
 	},
 };
 
+/*
+ * L2C-310 specific code.
+ *
+ * Errata:
+ * 588369: PL310 R0P0->R1P0, fixed R2P0.
+ *	Affects: all clean+invalidate operations
+ *	clean and invalidate skips the invalidate step, so we need to issue
+ *	separate operations.  We also require the above debug workaround
+ *	enclosing this code fragment on affected parts.  On unaffected parts,
+ *	we must not use this workaround without the debug register writes
+ *	to avoid exposing a problem similar to 727915.
+ *
+ * 727915: PL310 R2P0->R3P0, fixed R3P1.
+ *	Affects: clean+invalidate by way
+ *	clean and invalidate by way runs in the background, and a store can
+ *	hit the line between the clean operation and invalidate operation,
+ *	resulting in the store being lost.
+ *
+ * 753970: PL310 R3P0, fixed R3P1.
+ *	Affects: sync
+ *	prevents merging writes after the sync operation, until another L2C
+ *	operation is performed (or a number of other conditions.)
+ *
+ * 769419: PL310 R0P0->R3P1, fixed R3P2.
+ *	Affects: store buffer
+ *	store buffer is not automatically drained.
+ */
+static void __init l2c310_fixup(void __iomem *base, u32 cache_id,
+	struct outer_cache_fns *fns)
+{
+	unsigned revision = cache_id & L2X0_CACHE_ID_RTL_MASK;
+	const char *errata[4];
+	unsigned n = 0;
+
+	if (revision <= L310_CACHE_ID_RTL_R3P0)
+		fns->set_debug = pl310_set_debug;
+
+	if (IS_ENABLED(CONFIG_PL310_ERRATA_753970) &&
+	    revision == L310_CACHE_ID_RTL_R3P0) {
+		sync_reg_offset = L2X0_DUMMY_REG;
+		errata[n++] = "753970";
+	}
+
+	if (IS_ENABLED(CONFIG_PL310_ERRATA_769419))
+		errata[n++] = "769419";
+
+	if (n) {
+		unsigned i;
+
+		pr_info("L2C-310 errat%s", n > 1 ? "a" : "um");
+		for (i = 0; i < n; i++)
+			pr_cont(" %s", errata[i]);
+		pr_cont(" enabled\n");
+	}
+}
+
+static const struct l2c_init_data l2c310_init_fns __initconst = {
+	.num_lock = 8,
+	.enable = l2c_enable,
+	.fixup = l2c310_fixup,
+	.outer_cache = {
+		.inv_range = l2x0_inv_range,
+		.clean_range = l2x0_clean_range,
+		.flush_range = l2x0_flush_range,
+		.flush_all = l2x0_flush_all,
+		.disable = l2x0_disable,
+		.sync = l2x0_cache_sync,
+	},
+};
+
 static void __init __l2c_init(const struct l2c_init_data *data,
 	u32 aux_val, u32 aux_mask, u32 cache_id)
 {
+	struct outer_cache_fns fns;
 	u32 aux;
 	u32 way_size = 0;
 	int ways;
@@ -423,23 +495,20 @@ static void __init __l2c_init(const struct l2c_init_data *data,
 		else
 			ways = 8;
 		type = "L310";
-#ifdef CONFIG_PL310_ERRATA_753970
-		/* Unmapped register. */
-		sync_reg_offset = L2X0_DUMMY_REG;
-#endif
 		break;
+
 	case L2X0_CACHE_ID_PART_L210:
 		ways = (aux >> 13) & 0xf;
 		type = "L210";
 		break;
 
 	case AURORA_CACHE_ID:
-		sync_reg_offset = AURORA_SYNC_REG;
 		ways = (aux >> 13) & 0xf;
 		ways = 2 << ((ways + 1) >> 2);
 		way_size_shift = AURORA_WAY_SIZE_SHIFT;
 		type = "Aurora";
 		break;
+
 	default:
 		/* Assume unknown chips have 8 ways */
 		ways = 8;
@@ -457,6 +526,10 @@ static void __init __l2c_init(const struct l2c_init_data *data,
 
 	l2x0_size = ways * way_size * SZ_1K;
 
+	fns = data->outer_cache;
+	if (data->fixup)
+		data->fixup(l2x0_base, cache_id, &fns);
+
 	/*
 	 * Check if l2x0 controller is already enabled.  If we are booting
 	 * in non-secure mode accessing the below registers will fault.
@@ -470,11 +543,7 @@ static void __init __l2c_init(const struct l2c_init_data *data,
 	/* Save the value for resuming. */
 	l2x0_saved_regs.aux_ctrl = aux;
 
-	outer_cache = data->outer_cache;
-
-	if ((cache_id & L2X0_CACHE_ID_PART_MASK) == L2X0_CACHE_ID_PART_L310 &&
-	    (cache_id & L2X0_CACHE_ID_RTL_MASK) <= L310_CACHE_ID_RTL_R3P0)
-		outer_cache.set_debug = pl310_set_debug;
+	outer_cache = fns;
 
 	pr_info("%s cache controller enabled\n", type);
 	pr_info("l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x, Cache size: %d kB\n",
@@ -483,13 +552,24 @@ static void __init __l2c_init(const struct l2c_init_data *data,
 
 void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
 {
+	const struct l2c_init_data *data;
 	u32 cache_id;
 
 	l2x0_base = base;
 
 	cache_id = readl_relaxed(base + L2X0_CACHE_ID);
 
-	__l2c_init(&l2x0_init_fns, aux_val, aux_mask, cache_id);
+	switch (cache_id & L2X0_CACHE_ID_PART_MASK) {
+	default:
+		data = &l2x0_init_fns;
+		break;
+
+	case L2X0_CACHE_ID_PART_L310:
+		data = &l2c310_init_fns;
+		break;
+	}
+
+	__l2c_init(data, aux_val, aux_mask, cache_id);
 }
 
 #ifdef CONFIG_OF
@@ -659,6 +739,7 @@ static const struct l2c_init_data of_pl310_data __initconst = {
 	.num_lock = 8,
 	.of_parse = pl310_of_parse,
 	.enable = l2c_enable,
+	.fixup = l2c310_fixup,
 	.save  = pl310_save,
 	.outer_cache = {
 		.inv_range   = l2x0_inv_range,
@@ -802,6 +883,12 @@ static void __init aurora_enable_no_outer(void __iomem *base, u32 aux,
 	l2c_enable(base, aux, num_lock);
 }
 
+static void __init aurora_fixup(void __iomem *base, u32 cache_id,
+	struct outer_cache_fns *fns)
+{
+	sync_reg_offset = AURORA_SYNC_REG;
+}
+
 static void __init aurora_of_parse(const struct device_node *np,
 				u32 *aux_val, u32 *aux_mask)
 {
@@ -828,6 +915,7 @@ static const struct l2c_init_data of_aurora_with_outer_data __initconst = {
 	.num_lock = 4,
 	.of_parse = aurora_of_parse,
 	.enable = l2c_enable,
+	.fixup = aurora_fixup,
 	.save  = aurora_save,
 	.outer_cache = {
 		.inv_range   = aurora_inv_range,
@@ -844,6 +932,7 @@ static const struct l2c_init_data of_aurora_no_outer_data __initconst = {
 	.num_lock = 4,
 	.of_parse = aurora_of_parse,
 	.enable = aurora_enable_no_outer,
+	.fixup = aurora_fixup,
 	.save  = aurora_save,
 	.outer_cache = {
 		.resume      = aurora_resume,
@@ -995,6 +1084,7 @@ static const struct l2c_init_data of_bcm_l2x0_data __initconst = {
 	.num_lock = 8,
 	.of_parse = pl310_of_parse,
 	.enable = l2c_enable,
+	.fixup = l2c310_fixup,
 	.save  = pl310_save,
 	.outer_cache = {
 		.inv_range   = bcm_inv_range,
Signature · 1 line
1.8.3.1
#25 lore raw

PATCH 25/222 ARM: l2c: clean up L2 cache initialisation messages

In reply to Russell King - ARM Linux

Make one of them purely "English", and the other purely technical.

---
 arch/arm/mm/cache-l2x0.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
arch/arm/mm/cache-l2x0.c +4 -3
index a544f19c448f..713cdcef25d1 100644
@@ -545,9 +545,10 @@ static void __init __l2c_init(const struct l2c_init_data *data,
 
 	outer_cache = fns;
 
-	pr_info("%s cache controller enabled\n", type);
-	pr_info("l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x, Cache size: %d kB\n",
-		ways, cache_id, aux, l2x0_size >> 10);
+	pr_info("%s cache controller enabled, %d ways, %d kB\n",
+		type, ways, l2x0_size >> 10);
+	pr_info("%s: CACHE_ID 0x%08x, AUX_CTRL 0x%08x\n",
+		type, cache_id, aux);
 }
 
 void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
Signature · 1 line
1.8.3.1
#26 lore raw

PATCH 26/222 ARM: l2c: move and add ARM L2C-2x0/L2C-310 save/resume code to non-OF

In reply to Russell King - ARM Linux

Add the save/resume code hooks to the non-OF implementations as well. There's no reason for the non-OF implementations to be any different from the OF implementations.

---
 arch/arm/mm/cache-l2x0.c | 151 ++++++++++++++++++++++++-----------------------
 1 file changed, 77 insertions(+), 74 deletions(-)
arch/arm/mm/cache-l2x0.c +77 -74
index 713cdcef25d1..4d985c17291c 100644
@@ -383,6 +383,21 @@ static void l2x0_enable(void __iomem *base, u32 aux, unsigned num_lock)
 	writel_relaxed(L2X0_CTRL_EN, base + L2X0_CTRL);
 }
 
+static void l2x0_resume(void)
+{
+	if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
+		/* restore aux ctrl and enable l2 */
+		l2x0_unlock(readl_relaxed(l2x0_base + L2X0_CACHE_ID));
+
+		writel_relaxed(l2x0_saved_regs.aux_ctrl, l2x0_base +
+			L2X0_AUX_CTRL);
+
+		l2x0_inv_all();
+
+		writel_relaxed(L2X0_CTRL_EN, l2x0_base + L2X0_CTRL);
+	}
+}
+
 static const struct l2c_init_data l2x0_init_fns __initconst = {
 	.enable = l2x0_enable,
 	.outer_cache = {
@@ -392,6 +407,7 @@ static const struct l2c_init_data l2x0_init_fns __initconst = {
 		.flush_all = l2x0_flush_all,
 		.disable = l2x0_disable,
 		.sync = l2x0_cache_sync,
+		.resume = l2x0_resume,
 	},
 };
 
@@ -422,6 +438,65 @@ static const struct l2c_init_data l2x0_init_fns __initconst = {
  *	Affects: store buffer
  *	store buffer is not automatically drained.
  */
+static void __init pl310_save(void __iomem *base)
+{
+	u32 l2x0_revision = readl_relaxed(base + L2X0_CACHE_ID) &
+		L2X0_CACHE_ID_RTL_MASK;
+
+	l2x0_saved_regs.tag_latency = readl_relaxed(base +
+		L2X0_TAG_LATENCY_CTRL);
+	l2x0_saved_regs.data_latency = readl_relaxed(base +
+		L2X0_DATA_LATENCY_CTRL);
+	l2x0_saved_regs.filter_end = readl_relaxed(base +
+		L2X0_ADDR_FILTER_END);
+	l2x0_saved_regs.filter_start = readl_relaxed(base +
+		L2X0_ADDR_FILTER_START);
+
+	if (l2x0_revision >= L310_CACHE_ID_RTL_R2P0) {
+		/*
+		 * From r2p0, there is Prefetch offset/control register
+		 */
+		l2x0_saved_regs.prefetch_ctrl = readl_relaxed(base +
+			L2X0_PREFETCH_CTRL);
+		/*
+		 * From r3p0, there is Power control register
+		 */
+		if (l2x0_revision >= L310_CACHE_ID_RTL_R3P0)
+			l2x0_saved_regs.pwr_ctrl = readl_relaxed(base +
+				L2X0_POWER_CTRL);
+	}
+}
+
+static void pl310_resume(void)
+{
+	u32 l2x0_revision;
+
+	if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
+		/* restore pl310 setup */
+		writel_relaxed(l2x0_saved_regs.tag_latency,
+			l2x0_base + L2X0_TAG_LATENCY_CTRL);
+		writel_relaxed(l2x0_saved_regs.data_latency,
+			l2x0_base + L2X0_DATA_LATENCY_CTRL);
+		writel_relaxed(l2x0_saved_regs.filter_end,
+			l2x0_base + L2X0_ADDR_FILTER_END);
+		writel_relaxed(l2x0_saved_regs.filter_start,
+			l2x0_base + L2X0_ADDR_FILTER_START);
+
+		l2x0_revision = readl_relaxed(l2x0_base + L2X0_CACHE_ID) &
+			L2X0_CACHE_ID_RTL_MASK;
+
+		if (l2x0_revision >= L310_CACHE_ID_RTL_R2P0) {
+			writel_relaxed(l2x0_saved_regs.prefetch_ctrl,
+				l2x0_base + L2X0_PREFETCH_CTRL);
+			if (l2x0_revision >= L310_CACHE_ID_RTL_R3P0)
+				writel_relaxed(l2x0_saved_regs.pwr_ctrl,
+					l2x0_base + L2X0_POWER_CTRL);
+		}
+	}
+
+	l2x0_resume();
+}
+
 static void __init l2c310_fixup(void __iomem *base, u32 cache_id,
 	struct outer_cache_fns *fns)
 {
@@ -455,6 +530,7 @@ static const struct l2c_init_data l2c310_init_fns __initconst = {
 	.num_lock = 8,
 	.enable = l2c_enable,
 	.fixup = l2c310_fixup,
+	.save = pl310_save,
 	.outer_cache = {
 		.inv_range = l2x0_inv_range,
 		.clean_range = l2x0_clean_range,
@@ -462,6 +538,7 @@ static const struct l2c_init_data l2c310_init_fns __initconst = {
 		.flush_all = l2x0_flush_all,
 		.disable = l2x0_disable,
 		.sync = l2x0_cache_sync,
+		.resume = pl310_resume,
 	},
 };
 
@@ -614,21 +691,6 @@ static void __init l2x0_of_parse(const struct device_node *np,
 	*aux_mask &= ~mask;
 }
 
-static void l2x0_resume(void)
-{
-	if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
-		/* restore aux ctrl and enable l2 */
-		l2x0_unlock(readl_relaxed(l2x0_base + L2X0_CACHE_ID));
-
-		writel_relaxed(l2x0_saved_regs.aux_ctrl, l2x0_base +
-			L2X0_AUX_CTRL);
-
-		l2x0_inv_all();
-
-		writel_relaxed(L2X0_CTRL_EN, l2x0_base + L2X0_CTRL);
-	}
-}
-
 static const struct l2c_init_data of_l2x0_data __initconst = {
 	.of_parse = l2x0_of_parse,
 	.enable = l2x0_enable,
@@ -677,65 +739,6 @@ static void __init pl310_of_parse(const struct device_node *np,
 	}
 }
 
-static void __init pl310_save(void __iomem *base)
-{
-	u32 l2x0_revision = readl_relaxed(base + L2X0_CACHE_ID) &
-		L2X0_CACHE_ID_RTL_MASK;
-
-	l2x0_saved_regs.tag_latency = readl_relaxed(base +
-		L2X0_TAG_LATENCY_CTRL);
-	l2x0_saved_regs.data_latency = readl_relaxed(base +
-		L2X0_DATA_LATENCY_CTRL);
-	l2x0_saved_regs.filter_end = readl_relaxed(base +
-		L2X0_ADDR_FILTER_END);
-	l2x0_saved_regs.filter_start = readl_relaxed(base +
-		L2X0_ADDR_FILTER_START);
-
-	if (l2x0_revision >= L310_CACHE_ID_RTL_R2P0) {
-		/*
-		 * From r2p0, there is Prefetch offset/control register
-		 */
-		l2x0_saved_regs.prefetch_ctrl = readl_relaxed(base +
-			L2X0_PREFETCH_CTRL);
-		/*
-		 * From r3p0, there is Power control register
-		 */
-		if (l2x0_revision >= L310_CACHE_ID_RTL_R3P0)
-			l2x0_saved_regs.pwr_ctrl = readl_relaxed(base +
-				L2X0_POWER_CTRL);
-	}
-}
-
-static void pl310_resume(void)
-{
-	u32 l2x0_revision;
-
-	if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
-		/* restore pl310 setup */
-		writel_relaxed(l2x0_saved_regs.tag_latency,
-			l2x0_base + L2X0_TAG_LATENCY_CTRL);
-		writel_relaxed(l2x0_saved_regs.data_latency,
-			l2x0_base + L2X0_DATA_LATENCY_CTRL);
-		writel_relaxed(l2x0_saved_regs.filter_end,
-			l2x0_base + L2X0_ADDR_FILTER_END);
-		writel_relaxed(l2x0_saved_regs.filter_start,
-			l2x0_base + L2X0_ADDR_FILTER_START);
-
-		l2x0_revision = readl_relaxed(l2x0_base + L2X0_CACHE_ID) &
-			L2X0_CACHE_ID_RTL_MASK;
-
-		if (l2x0_revision >= L310_CACHE_ID_RTL_R2P0) {
-			writel_relaxed(l2x0_saved_regs.prefetch_ctrl,
-				l2x0_base + L2X0_PREFETCH_CTRL);
-			if (l2x0_revision >= L310_CACHE_ID_RTL_R3P0)
-				writel_relaxed(l2x0_saved_regs.pwr_ctrl,
-					l2x0_base + L2X0_POWER_CTRL);
-		}
-	}
-
-	l2x0_resume();
-}
-
 static const struct l2c_init_data of_pl310_data __initconst = {
 	.num_lock = 8,
 	.of_parse = pl310_of_parse,
Signature · 1 line
1.8.3.1
#27 lore raw

PATCH 27/222 ARM: l2c: clean up save/resume functions

In reply to Russell King - ARM Linux

Rename the pl310 save/resume functions to have a l2c310 prefix - this is it's official name. Use a local cached copy of the l2x0_base virtual address, and also realise that many of the resume function tails are the same as the enable functions, so make a call to the enable function instead of duplicating that code.

---
 arch/arm/mm/cache-l2x0.c | 109 ++++++++++++++++++++++-------------------------
 1 file changed, 52 insertions(+), 57 deletions(-)
arch/arm/mm/cache-l2x0.c +52 -57
index 4d985c17291c..e3f4fcbcc88b 100644
@@ -385,17 +385,10 @@ static void l2x0_enable(void __iomem *base, u32 aux, unsigned num_lock)
 
 static void l2x0_resume(void)
 {
-	if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
-		/* restore aux ctrl and enable l2 */
-		l2x0_unlock(readl_relaxed(l2x0_base + L2X0_CACHE_ID));
-
-		writel_relaxed(l2x0_saved_regs.aux_ctrl, l2x0_base +
-			L2X0_AUX_CTRL);
-
-		l2x0_inv_all();
+	void __iomem *base = l2x0_base;
 
-		writel_relaxed(L2X0_CTRL_EN, l2x0_base + L2X0_CTRL);
-	}
+	if (!(readl_relaxed(base + L2X0_CTRL) & L2X0_CTRL_EN))
+		l2x0_enable(base, l2x0_saved_regs.aux_ctrl, 0);
 }
 
 static const struct l2c_init_data l2x0_init_fns __initconst = {
@@ -438,10 +431,9 @@ static const struct l2c_init_data l2x0_init_fns __initconst = {
  *	Affects: store buffer
  *	store buffer is not automatically drained.
  */
-static void __init pl310_save(void __iomem *base)
+static void __init l2c310_save(void __iomem *base)
 {
-	u32 l2x0_revision = readl_relaxed(base + L2X0_CACHE_ID) &
-		L2X0_CACHE_ID_RTL_MASK;
+	unsigned revision;
 
 	l2x0_saved_regs.tag_latency = readl_relaxed(base +
 		L2X0_TAG_LATENCY_CTRL);
@@ -452,49 +444,49 @@ static void __init pl310_save(void __iomem *base)
 	l2x0_saved_regs.filter_start = readl_relaxed(base +
 		L2X0_ADDR_FILTER_START);
 
-	if (l2x0_revision >= L310_CACHE_ID_RTL_R2P0) {
-		/*
-		 * From r2p0, there is Prefetch offset/control register
-		 */
+	revision = readl_relaxed(base + L2X0_CACHE_ID) &
+			L2X0_CACHE_ID_RTL_MASK;
+
+	/* From r2p0, there is Prefetch offset/control register */
+	if (revision >= L310_CACHE_ID_RTL_R2P0)
 		l2x0_saved_regs.prefetch_ctrl = readl_relaxed(base +
-			L2X0_PREFETCH_CTRL);
-		/*
-		 * From r3p0, there is Power control register
-		 */
-		if (l2x0_revision >= L310_CACHE_ID_RTL_R3P0)
-			l2x0_saved_regs.pwr_ctrl = readl_relaxed(base +
-				L2X0_POWER_CTRL);
-	}
+							L2X0_PREFETCH_CTRL);
+
+	/* From r3p0, there is Power control register */
+	if (revision >= L310_CACHE_ID_RTL_R3P0)
+		l2x0_saved_regs.pwr_ctrl = readl_relaxed(base +
+							L2X0_POWER_CTRL);
 }
 
-static void pl310_resume(void)
+static void l2c310_resume(void)
 {
-	u32 l2x0_revision;
+	void __iomem *base = l2x0_base;
+
+	if (!(readl_relaxed(base + L2X0_CTRL) & L2X0_CTRL_EN)) {
+		unsigned revision;
 
-	if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
 		/* restore pl310 setup */
 		writel_relaxed(l2x0_saved_regs.tag_latency,
-			l2x0_base + L2X0_TAG_LATENCY_CTRL);
+			       base + L2X0_TAG_LATENCY_CTRL);
 		writel_relaxed(l2x0_saved_regs.data_latency,
-			l2x0_base + L2X0_DATA_LATENCY_CTRL);
+			       base + L2X0_DATA_LATENCY_CTRL);
 		writel_relaxed(l2x0_saved_regs.filter_end,
-			l2x0_base + L2X0_ADDR_FILTER_END);
+			       base + L2X0_ADDR_FILTER_END);
 		writel_relaxed(l2x0_saved_regs.filter_start,
-			l2x0_base + L2X0_ADDR_FILTER_START);
+			       base + L2X0_ADDR_FILTER_START);
 
-		l2x0_revision = readl_relaxed(l2x0_base + L2X0_CACHE_ID) &
-			L2X0_CACHE_ID_RTL_MASK;
+		revision = readl_relaxed(base + L2X0_CACHE_ID) &
+				L2X0_CACHE_ID_RTL_MASK;
 
-		if (l2x0_revision >= L310_CACHE_ID_RTL_R2P0) {
+		if (revision >= L310_CACHE_ID_RTL_R2P0)
 			writel_relaxed(l2x0_saved_regs.prefetch_ctrl,
-				l2x0_base + L2X0_PREFETCH_CTRL);
-			if (l2x0_revision >= L310_CACHE_ID_RTL_R3P0)
-				writel_relaxed(l2x0_saved_regs.pwr_ctrl,
-					l2x0_base + L2X0_POWER_CTRL);
-		}
-	}
+				       base + L2X0_PREFETCH_CTRL);
+		if (revision >= L310_CACHE_ID_RTL_R3P0)
+			writel_relaxed(l2x0_saved_regs.pwr_ctrl,
+				       base + L2X0_POWER_CTRL);
 
-	l2x0_resume();
+		l2c_enable(base, l2x0_saved_regs.aux_ctrl, 8);
+	}
 }
 
 static void __init l2c310_fixup(void __iomem *base, u32 cache_id,
@@ -530,7 +522,7 @@ static const struct l2c_init_data l2c310_init_fns __initconst = {
 	.num_lock = 8,
 	.enable = l2c_enable,
 	.fixup = l2c310_fixup,
-	.save = pl310_save,
+	.save = l2c310_save,
 	.outer_cache = {
 		.inv_range = l2x0_inv_range,
 		.clean_range = l2x0_clean_range,
@@ -538,7 +530,7 @@ static const struct l2c_init_data l2c310_init_fns __initconst = {
 		.flush_all = l2x0_flush_all,
 		.disable = l2x0_disable,
 		.sync = l2x0_cache_sync,
-		.resume = pl310_resume,
+		.resume = l2c310_resume,
 	},
 };
 
@@ -744,7 +736,7 @@ static const struct l2c_init_data of_pl310_data __initconst = {
 	.of_parse = pl310_of_parse,
 	.enable = l2c_enable,
 	.fixup = l2c310_fixup,
-	.save  = pl310_save,
+	.save  = l2c310_save,
 	.outer_cache = {
 		.inv_range   = l2x0_inv_range,
 		.clean_range = l2x0_clean_range,
@@ -752,7 +744,7 @@ static const struct l2c_init_data of_pl310_data __initconst = {
 		.flush_all   = l2x0_flush_all,
 		.disable     = l2x0_disable,
 		.sync        = l2x0_cache_sync,
-		.resume      = pl310_resume,
+		.resume      = l2c310_resume,
 	},
 };
 
@@ -862,10 +854,11 @@ static void aurora_save(void __iomem *base)
 
 static void aurora_resume(void)
 {
-	if (!(readl(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
-		writel_relaxed(l2x0_saved_regs.aux_ctrl,
-				l2x0_base + L2X0_AUX_CTRL);
-		writel_relaxed(l2x0_saved_regs.ctrl, l2x0_base + L2X0_CTRL);
+	void __iomem *base = l2x0_base;
+
+	if (!(readl(base + L2X0_CTRL) & L2X0_CTRL_EN)) {
+		writel_relaxed(l2x0_saved_regs.aux_ctrl, base + L2X0_AUX_CTRL);
+		writel_relaxed(l2x0_saved_regs.ctrl, base + L2X0_CTRL);
 	}
 }
 
@@ -1089,7 +1082,7 @@ static const struct l2c_init_data of_bcm_l2x0_data __initconst = {
 	.of_parse = pl310_of_parse,
 	.enable = l2c_enable,
 	.fixup = l2c310_fixup,
-	.save  = pl310_save,
+	.save  = l2c310_save,
 	.outer_cache = {
 		.inv_range   = bcm_inv_range,
 		.clean_range = bcm_clean_range,
@@ -1097,7 +1090,7 @@ static const struct l2c_init_data of_bcm_l2x0_data __initconst = {
 		.flush_all   = l2x0_flush_all,
 		.disable     = l2x0_disable,
 		.sync        = l2x0_cache_sync,
-		.resume      = pl310_resume,
+		.resume      = l2c310_resume,
 	},
 };
 
@@ -1111,14 +1104,16 @@ static void __init tauros3_save(void __iomem *base)
 
 static void tauros3_resume(void)
 {
-	if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) {
+	void __iomem *base = l2x0_base;
+
+	if (!(readl_relaxed(base + L2X0_CTRL) & L2X0_CTRL_EN)) {
 		writel_relaxed(l2x0_saved_regs.aux2_ctrl,
-			       l2x0_base + TAUROS3_AUX2_CTRL);
+			       base + TAUROS3_AUX2_CTRL);
 		writel_relaxed(l2x0_saved_regs.prefetch_ctrl,
-			       l2x0_base + L2X0_PREFETCH_CTRL);
-	}
+			       base + L2X0_PREFETCH_CTRL);
 
-	l2x0_resume();
+		l2c_enable(base, l2x0_saved_regs.aux_ctrl, 8);
+	}
 }
 
 static const struct l2c_init_data of_tauros3_data __initconst = {
Signature · 1 line
1.8.3.1
#28 lore raw

PATCH 28/222 ARM: l2c: simplify l2x0 unlocking code

In reply to Russell King - ARM Linux

The l2x0 unlocking code is only called from l2x0_enable() now, so move the logic entirely into that function and simplify it.

---
 arch/arm/mm/cache-l2x0.c | 25 ++++++++-----------------
 1 file changed, 8 insertions(+), 17 deletions(-)
arch/arm/mm/cache-l2x0.c +8 -17
index e3f4fcbcc88b..157fd7ae331a 100644
@@ -352,30 +352,21 @@ static void l2x0_disable(void)
 	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
-static void l2x0_unlock(u32 cache_id)
+static void l2x0_enable(void __iomem *base, u32 aux, unsigned num_lock)
 {
-	int lockregs;
+	unsigned id;
 
-	switch (cache_id & L2X0_CACHE_ID_PART_MASK) {
-	case L2X0_CACHE_ID_PART_L310:
-		lockregs = 8;
-		break;
-	default:
-		/* L210 and unknown types */
-		lockregs = 1;
-		break;
-	}
-
-	l2c_unlock(l2x0_base, lockregs);
-}
+	id = readl_relaxed(base + L2X0_CACHE_ID) & L2X0_CACHE_ID_PART_MASK;
+	if (id == L2X0_CACHE_ID_PART_L310)
+		num_lock = 8;
+	else
+		num_lock = 1;
 
-static void l2x0_enable(void __iomem *base, u32 aux, unsigned num_lock)
-{
 	/* l2x0 controller is disabled */
 	writel_relaxed(aux, base + L2X0_AUX_CTRL);
 
 	/* Make sure that I&D is not locked down when starting */
-	l2x0_unlock(readl_relaxed(base + L2X0_CACHE_ID));
+	l2c_unlock(base, num_lock);
 
 	l2x0_inv_all();
 
Signature · 1 line
1.8.3.1
#29 lore raw

PATCH 29/222 ARM: l2c: move pl310_set_debug() into l2c-310 code

In reply to Russell King - ARM Linux

Move the pl310_set_debug() into the l2c-310 code area, and don't hide it with ifdefs. Rename it to l2c310_set_debug().

---
 arch/arm/mm/cache-l2x0.c | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)
arch/arm/mm/cache-l2x0.c +6 -8
index 157fd7ae331a..9586be73ca4f 100644
@@ -154,18 +154,11 @@ static inline void debug_writel(unsigned long val)
 	if (outer_cache.set_debug)
 		l2c_set_debug(l2x0_base, val);
 }
-
-static void pl310_set_debug(unsigned long val)
-{
-	writel_relaxed(val, l2x0_base + L2X0_DEBUG_CTRL);
-}
 #else
 /* Optimised out for non-errata case */
 static inline void debug_writel(unsigned long val)
 {
 }
-
-#define pl310_set_debug	NULL
 #endif
 
 #ifdef CONFIG_PL310_ERRATA_588369
@@ -422,6 +415,11 @@ static const struct l2c_init_data l2x0_init_fns __initconst = {
  *	Affects: store buffer
  *	store buffer is not automatically drained.
  */
+static void l2c310_set_debug(unsigned long val)
+{
+	writel_relaxed(val, l2x0_base + L2X0_DEBUG_CTRL);
+}
+
 static void __init l2c310_save(void __iomem *base)
 {
 	unsigned revision;
@@ -488,7 +486,7 @@ static void __init l2c310_fixup(void __iomem *base, u32 cache_id,
 	unsigned n = 0;
 
 	if (revision <= L310_CACHE_ID_RTL_R3P0)
-		fns->set_debug = pl310_set_debug;
+		fns->set_debug = l2c310_set_debug;
 
 	if (IS_ENABLED(CONFIG_PL310_ERRATA_753970) &&
 	    revision == L310_CACHE_ID_RTL_R3P0) {
Signature · 1 line
1.8.3.1
#30 lore raw

PATCH 30/222 ARM: l2c: add L2C-210 specific handlers

In reply to Russell King - ARM Linux

Add L2C-210 specific cache operation handlers. These are tailored to the requirements of the L2C-210 cache controller, which doesn't require any workarounds. We avoid using the way operations during normal operation, which means we can avoid locking: the only time we use the way operations are during initialisation, and when disabling the cache.

---
 arch/arm/mm/cache-l2x0.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 122 insertions(+), 1 deletion(-)
arch/arm/mm/cache-l2x0.c +122 -1
index 9586be73ca4f..d07fa4fc95a3 100644
@@ -389,6 +389,108 @@ static const struct l2c_init_data l2x0_init_fns __initconst = {
 };
 
 /*
+ * L2C-210 specific code.
+ *
+ * The L2C-2x0 PA, set/way and sync operations are atomic, but we must
+ * ensure that no background operation is running.  The way operations
+ * are all background tasks.
+ *
+ * While a background operation is in progress, any new operation is
+ * ignored (unspecified whether this causes an error.)  Thankfully, not
+ * used on SMP.
+ *
+ * Never has a different sync register other than L2X0_CACHE_SYNC, but
+ * we use sync_reg_offset here so we can share some of this with L2C-310.
+ */
+static void __l2c210_cache_sync(void __iomem *base)
+{
+	writel_relaxed(0, base + sync_reg_offset);
+}
+
+static void __l2c210_op_pa_range(void __iomem *reg, unsigned long start,
+	unsigned long end)
+{
+	while (start < end) {
+		writel_relaxed(start, reg);
+		start += CACHE_LINE_SIZE;
+	}
+}
+
+static void l2c210_inv_range(unsigned long start, unsigned long end)
+{
+	void __iomem *base = l2x0_base;
+
+	if (start & (CACHE_LINE_SIZE - 1)) {
+		start &= ~(CACHE_LINE_SIZE - 1);
+		writel_relaxed(start, base + L2X0_CLEAN_INV_LINE_PA);
+		start += CACHE_LINE_SIZE;
+	}
+
+	if (end & (CACHE_LINE_SIZE - 1)) {
+		end &= ~(CACHE_LINE_SIZE - 1);
+		writel_relaxed(end, base + L2X0_CLEAN_INV_LINE_PA);
+	}
+
+	__l2c210_op_pa_range(base + L2X0_INV_LINE_PA, start, end);
+	__l2c210_cache_sync(base);
+}
+
+static void l2c210_clean_range(unsigned long start, unsigned long end)
+{
+	void __iomem *base = l2x0_base;
+
+	start &= ~(CACHE_LINE_SIZE - 1);
+	__l2c210_op_pa_range(base + L2X0_CLEAN_LINE_PA, start, end);
+	__l2c210_cache_sync(base);
+}
+
+static void l2c210_flush_range(unsigned long start, unsigned long end)
+{
+	void __iomem *base = l2x0_base;
+
+	start &= ~(CACHE_LINE_SIZE - 1);
+	__l2c210_op_pa_range(base + L2X0_CLEAN_INV_LINE_PA, start, end);
+	__l2c210_cache_sync(base);
+}
+
+static void l2c210_flush_all(void)
+{
+	void __iomem *base = l2x0_base;
+
+	BUG_ON(!irqs_disabled());
+
+	__l2c_op_way(base + L2X0_CLEAN_INV_WAY);
+	__l2c210_cache_sync(base);
+}
+
+static void l2c210_sync(void)
+{
+	__l2c210_cache_sync(l2x0_base);
+}
+
+static void l2c210_resume(void)
+{
+	void __iomem *base = l2x0_base;
+
+	if (!(readl_relaxed(base + L2X0_CTRL) & L2X0_CTRL_EN))
+		l2c_enable(base, l2x0_saved_regs.aux_ctrl, 1);
+}
+
+static const struct l2c_init_data l2c210_data __initconst = {
+	.num_lock = 1,
+	.enable = l2c_enable,
+	.outer_cache = {
+		.inv_range = l2c210_inv_range,
+		.clean_range = l2c210_clean_range,
+		.flush_range = l2c210_flush_range,
+		.flush_all = l2c210_flush_all,
+		.disable = l2c_disable,
+		.sync = l2c210_sync,
+		.resume = l2c210_resume,
+	},
+};
+
+/*
  * L2C-310 specific code.
  *
  * Errata:
@@ -623,6 +725,10 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
 		data = &l2x0_init_fns;
 		break;
 
+	case L2X0_CACHE_ID_PART_L210:
+		data = &l2c210_data;
+		break;
+
 	case L2X0_CACHE_ID_PART_L310:
 		data = &l2c310_init_fns;
 		break;
@@ -672,6 +778,21 @@ static void __init l2x0_of_parse(const struct device_node *np,
 	*aux_mask &= ~mask;
 }
 
+static const struct l2c_init_data of_l2c210_data __initconst = {
+	.num_lock = 1,
+	.of_parse = l2x0_of_parse,
+	.enable = l2c_enable,
+	.outer_cache = {
+		.inv_range   = l2c210_inv_range,
+		.clean_range = l2c210_clean_range,
+		.flush_range = l2c210_flush_range,
+		.flush_all   = l2c210_flush_all,
+		.disable     = l2c_disable,
+		.sync        = l2c210_sync,
+		.resume      = l2c210_resume,
+	},
+};
+
 static const struct l2c_init_data of_l2x0_data __initconst = {
 	.of_parse = l2x0_of_parse,
 	.enable = l2x0_enable,
@@ -1117,7 +1238,7 @@ static const struct l2c_init_data of_tauros3_data __initconst = {
 
 #define L2C_ID(name, fns) { .compatible = name, .data = (void *)&fns }
 static const struct of_device_id l2x0_ids[] __initconst = {
-	L2C_ID("arm,l210-cache", of_l2x0_data),
+	L2C_ID("arm,l210-cache", of_l2c210_data),
 	L2C_ID("arm,l220-cache", of_l2x0_data),
 	L2C_ID("arm,pl310-cache", of_pl310_data),
 	L2C_ID("brcm,bcm11351-a2-pl310-cache", of_bcm_l2x0_data),
Signature · 1 line
1.8.3.1
#31 lore raw

PATCH 31/222 ARM: l2c: implement L2C-310 erratum 727915 as a method override

In reply to Russell King - ARM Linux

Implement L2C-310 erratum 727915 by overriding the flush_all method in the outer_cache operations structure. This allows us to sensibly contain the erratum code in one place without affecting other locations or implementations.

---
 arch/arm/mm/cache-l2x0.c | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)
arch/arm/mm/cache-l2x0.c +20 -0
index d07fa4fc95a3..6161232c8a85 100644
@@ -522,6 +522,19 @@ static void l2c310_set_debug(unsigned long val)
 	writel_relaxed(val, l2x0_base + L2X0_DEBUG_CTRL);
 }
 
+static void l2c310_flush_all_erratum(void)
+{
+	void __iomem *base = l2x0_base;
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&l2x0_lock, flags);
+	l2c_set_debug(base, 0x03);
+	__l2c_op_way(base + L2X0_CLEAN_INV_WAY);
+	l2c_set_debug(base, 0x00);
+	__l2c210_cache_sync(base);
+	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
+}
+
 static void __init l2c310_save(void __iomem *base)
 {
 	unsigned revision;
@@ -590,6 +603,13 @@ static void __init l2c310_fixup(void __iomem *base, u32 cache_id,
 	if (revision <= L310_CACHE_ID_RTL_R3P0)
 		fns->set_debug = l2c310_set_debug;
 
+	if (IS_ENABLED(CONFIG_PL310_ERRATA_727915) &&
+	    revision >= L310_CACHE_ID_RTL_R2P0 &&
+	    revision < L310_CACHE_ID_RTL_R3P1) {
+		fns->flush_all = l2c310_flush_all_erratum;
+		errata[n++] = "727915";
+	}
+
 	if (IS_ENABLED(CONFIG_PL310_ERRATA_753970) &&
 	    revision == L310_CACHE_ID_RTL_R3P0) {
 		sync_reg_offset = L2X0_DUMMY_REG;
Signature · 1 line
1.8.3.1
#32 lore raw

PATCH 32/222 ARM: l2c: implement L2C-310 erratum 588369 as a method override

In reply to Russell King - ARM Linux

Implement L2C-310 erratum 588369 by overriding the invalidate range and flush range methods in the outer_cache operations structure. This allows us to sensibly contain the erratum code in one place without affecting other locations/implemetations.

---
 arch/arm/mm/cache-l2x0.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 69 insertions(+)
arch/arm/mm/cache-l2x0.c +69 -0
index 6161232c8a85..79ff08db204d 100644
@@ -522,6 +522,65 @@ static void l2c310_set_debug(unsigned long val)
 	writel_relaxed(val, l2x0_base + L2X0_DEBUG_CTRL);
 }
 
+static void l2c310_inv_range_erratum(unsigned long start, unsigned long end)
+{
+	void __iomem *base = l2x0_base;
+
+	if ((start | end) & (CACHE_LINE_SIZE - 1)) {
+		unsigned long flags;
+
+		/* Erratum 588369 for both clean+invalidate operations */
+		raw_spin_lock_irqsave(&l2x0_lock, flags);
+		l2c_set_debug(base, 0x03);
+
+		if (start & (CACHE_LINE_SIZE - 1)) {
+			start &= ~(CACHE_LINE_SIZE - 1);
+			writel_relaxed(start, base + L2X0_CLEAN_LINE_PA);
+			writel_relaxed(start, base + L2X0_INV_LINE_PA);
+			start += CACHE_LINE_SIZE;
+		}
+
+		if (end & (CACHE_LINE_SIZE - 1)) {
+			end &= ~(CACHE_LINE_SIZE - 1);
+			writel_relaxed(end, base + L2X0_CLEAN_LINE_PA);
+			writel_relaxed(end, base + L2X0_INV_LINE_PA);
+		}
+
+		l2c_set_debug(base, 0x00);
+		raw_spin_unlock_irqrestore(&l2x0_lock, flags);
+	}
+
+	__l2c210_op_pa_range(base + L2X0_INV_LINE_PA, start, end);
+	__l2c210_cache_sync(base);
+}
+
+static void l2c310_flush_range_erratum(unsigned long start, unsigned long end)
+{
+	raw_spinlock_t *lock = &l2x0_lock;
+	unsigned long flags;
+	void __iomem *base = l2x0_base;
+
+	raw_spin_lock_irqsave(lock, flags);
+	while (start < end) {
+		unsigned long blk_end = start + min(end - start, 4096UL);
+
+		l2c_set_debug(base, 0x03);
+		while (start < blk_end) {
+			writel_relaxed(start, base + L2X0_CLEAN_LINE_PA);
+			writel_relaxed(start, base + L2X0_INV_LINE_PA);
+			start += CACHE_LINE_SIZE;
+		}
+		l2c_set_debug(base, 0x00);
+
+		if (blk_end < end) {
+			raw_spin_unlock_irqrestore(lock, flags);
+			raw_spin_lock_irqsave(lock, flags);
+		}
+	}
+	raw_spin_unlock_irqrestore(lock, flags);
+	__l2c210_cache_sync(base);
+}
+
 static void l2c310_flush_all_erratum(void)
 {
 	void __iomem *base = l2x0_base;
@@ -600,9 +659,19 @@ static void __init l2c310_fixup(void __iomem *base, u32 cache_id,
 	const char *errata[4];
 	unsigned n = 0;
 
+	/* For compatibility */
 	if (revision <= L310_CACHE_ID_RTL_R3P0)
 		fns->set_debug = l2c310_set_debug;
 
+	if (IS_ENABLED(CONFIG_PL310_ERRATA_588369) &&
+	    revision < L310_CACHE_ID_RTL_R2P0 &&
+	    /* For bcm compatibility */
+	    fns->inv_range == l2x0_inv_range) {
+		fns->inv_range = l2c310_inv_range_erratum;
+		fns->flush_range = l2c310_flush_range_erratum;
+		errata[n++] = "588369";
+	}
+
 	if (IS_ENABLED(CONFIG_PL310_ERRATA_727915) &&
 	    revision >= L310_CACHE_ID_RTL_R2P0 &&
 	    revision < L310_CACHE_ID_RTL_R3P1) {
Signature · 1 line
1.8.3.1
#33 lore raw

PATCH 33/222 ARM: l2c: use L2C-210 handlers for L2C-310 errata-less implementations

In reply to Russell King - ARM Linux

Where no errata affect the L2C-310 handlers, they are functionally equivalent to L2C-210. Re-use the L2C-210 handlers for the L2C-310 part.

---
 arch/arm/mm/cache-l2x0.c | 58 ++++++++++++++++++++++++++++++------------------
 1 file changed, 36 insertions(+), 22 deletions(-)
arch/arm/mm/cache-l2x0.c +36 -22
index 79ff08db204d..49ddff972cb3 100644
@@ -493,6 +493,18 @@ static const struct l2c_init_data l2c210_data __initconst = {
 /*
  * L2C-310 specific code.
  *
+ * Very similar to L2C-210, the PA, set/way and sync operations are atomic,
+ * and the way operations are all background tasks.  However, issuing an
+ * operation while a background operation is in progress results in a
+ * SLVERR response.  We can reuse:
+ *
+ *  __l2c210_cache_sync (using sync_reg_offset)
+ *  l2c210_sync
+ *  l2c210_inv_range (if 588369 is not applicable)
+ *  l2c210_clean_range
+ *  l2c210_flush_range (if 588369 is not applicable)
+ *  l2c210_flush_all (if 727915 is not applicable)
+ *
  * Errata:
  * 588369: PL310 R0P0->R1P0, fixed R2P0.
  *	Affects: all clean+invalidate operations
@@ -666,7 +678,7 @@ static void __init l2c310_fixup(void __iomem *base, u32 cache_id,
 	if (IS_ENABLED(CONFIG_PL310_ERRATA_588369) &&
 	    revision < L310_CACHE_ID_RTL_R2P0 &&
 	    /* For bcm compatibility */
-	    fns->inv_range == l2x0_inv_range) {
+	    fns->inv_range == l2c210_inv_range) {
 		fns->inv_range = l2c310_inv_range_erratum;
 		fns->flush_range = l2c310_flush_range_erratum;
 		errata[n++] = "588369";
@@ -704,12 +716,13 @@ static const struct l2c_init_data l2c310_init_fns __initconst = {
 	.fixup = l2c310_fixup,
 	.save = l2c310_save,
 	.outer_cache = {
-		.inv_range = l2x0_inv_range,
-		.clean_range = l2x0_clean_range,
-		.flush_range = l2x0_flush_range,
-		.flush_all = l2x0_flush_all,
-		.disable = l2x0_disable,
-		.sync = l2x0_cache_sync,
+		.inv_range = l2c210_inv_range,
+		.clean_range = l2c210_clean_range,
+		.flush_range = l2c210_flush_range,
+		.flush_all = l2c210_flush_all,
+		.disable = l2c_disable,
+		.sync = l2c210_sync,
+		.set_debug = l2c310_set_debug,
 		.resume = l2c310_resume,
 	},
 };
@@ -896,8 +909,8 @@ static const struct l2c_init_data of_l2x0_data __initconst = {
 	},
 };
 
-static void __init pl310_of_parse(const struct device_node *np,
-				  u32 *aux_val, u32 *aux_mask)
+static void __init l2c310_of_parse(const struct device_node *np,
+	u32 *aux_val, u32 *aux_mask)
 {
 	u32 data[3] = { 0, 0, 0 };
 	u32 tag[3] = { 0, 0, 0 };
@@ -930,19 +943,20 @@ static void __init pl310_of_parse(const struct device_node *np,
 	}
 }
 
-static const struct l2c_init_data of_pl310_data __initconst = {
+static const struct l2c_init_data of_l2c310_data __initconst = {
 	.num_lock = 8,
-	.of_parse = pl310_of_parse,
+	.of_parse = l2c310_of_parse,
 	.enable = l2c_enable,
 	.fixup = l2c310_fixup,
 	.save  = l2c310_save,
 	.outer_cache = {
-		.inv_range   = l2x0_inv_range,
-		.clean_range = l2x0_clean_range,
-		.flush_range = l2x0_flush_range,
-		.flush_all   = l2x0_flush_all,
-		.disable     = l2x0_disable,
-		.sync        = l2x0_cache_sync,
+		.inv_range   = l2c210_inv_range,
+		.clean_range = l2c210_clean_range,
+		.flush_range = l2c210_flush_range,
+		.flush_all   = l2c210_flush_all,
+		.disable     = l2c_disable,
+		.sync        = l2c210_sync,
+		.set_debug   = l2c310_set_debug,
 		.resume      = l2c310_resume,
 	},
 };
@@ -1278,7 +1292,7 @@ static void bcm_flush_range(unsigned long start, unsigned long end)
 
 static const struct l2c_init_data of_bcm_l2x0_data __initconst = {
 	.num_lock = 8,
-	.of_parse = pl310_of_parse,
+	.of_parse = l2c310_of_parse,
 	.enable = l2c_enable,
 	.fixup = l2c310_fixup,
 	.save  = l2c310_save,
@@ -1286,9 +1300,9 @@ static const struct l2c_init_data of_bcm_l2x0_data __initconst = {
 		.inv_range   = bcm_inv_range,
 		.clean_range = bcm_clean_range,
 		.flush_range = bcm_flush_range,
-		.flush_all   = l2x0_flush_all,
-		.disable     = l2x0_disable,
-		.sync        = l2x0_cache_sync,
+		.flush_all   = l2c210_flush_all,
+		.disable     = l2c_disable,
+		.sync        = l2c210_sync,
 		.resume      = l2c310_resume,
 	},
 };
@@ -1329,7 +1343,7 @@ static const struct l2c_init_data of_tauros3_data __initconst = {
 static const struct of_device_id l2x0_ids[] __initconst = {
 	L2C_ID("arm,l210-cache", of_l2c210_data),
 	L2C_ID("arm,l220-cache", of_l2x0_data),
-	L2C_ID("arm,pl310-cache", of_pl310_data),
+	L2C_ID("arm,pl310-cache", of_l2c310_data),
 	L2C_ID("brcm,bcm11351-a2-pl310-cache", of_bcm_l2x0_data),
 	L2C_ID("marvell,aurora-outer-cache", of_aurora_with_outer_data),
 	L2C_ID("marvell,aurora-system-cache", of_aurora_no_outer_data),
Signature · 1 line
1.8.3.1
#34 lore raw

PATCH 34/222 ARM: l2c: add L2C-220 specific handlers

In reply to Russell King - ARM Linux

The L2C-220 is different from the L2C-210 and L2C-310 in that every operation is a background operation: this means we have to use spinlocks to protect all operations, and we have to wait for every operation to complete.

Should a second operation be attempted while a previous operation is in progress, the response will be an imprecise abort.

---
 arch/arm/mm/cache-l2x0.c | 167 ++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 157 insertions(+), 10 deletions(-)
arch/arm/mm/cache-l2x0.c +157 -10
index 49ddff972cb3..751c3d7a22b3 100644
@@ -491,6 +491,148 @@ static const struct l2c_init_data l2c210_data __initconst = {
 };
 
 /*
+ * L2C-220 specific code.
+ *
+ * All operations are background operations: they have to be waited for.
+ * Conflicting requests generate a slave error (which will cause an
+ * imprecise abort.)  Never uses sync_reg_offset, so we hard-code the
+ * sync register here.
+ *
+ * However, we can re-use the l2c210_resume call.
+ */
+static inline void __l2c220_cache_sync(void __iomem *base)
+{
+	writel_relaxed(0, base + L2X0_CACHE_SYNC);
+	l2c_wait_mask(base + L2X0_CACHE_SYNC, 1);
+}
+
+static void l2c220_op_way(void __iomem *base, unsigned reg)
+{
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&l2x0_lock, flags);
+	__l2c_op_way(base + reg);
+	__l2c220_cache_sync(base);
+	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
+}
+
+static unsigned long l2c220_op_pa_range(void __iomem *reg, unsigned long start,
+	unsigned long end, unsigned long flags)
+{
+	raw_spinlock_t *lock = &l2x0_lock;
+
+	while (start < end) {
+		unsigned long blk_end = start + min(end - start, 4096UL);
+
+		while (start < blk_end) {
+			l2c_wait_mask(reg, 1);
+			writel_relaxed(start, reg);
+			start += CACHE_LINE_SIZE;
+		}
+
+		if (blk_end < end) {
+			raw_spin_unlock_irqrestore(lock, flags);
+			raw_spin_lock_irqsave(lock, flags);
+		}
+	}
+
+	return flags;
+}
+
+static void l2c220_inv_range(unsigned long start, unsigned long end)
+{
+	void __iomem *base = l2x0_base;
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&l2x0_lock, flags);
+	if ((start | end) & (CACHE_LINE_SIZE - 1)) {
+		if (start & (CACHE_LINE_SIZE - 1)) {
+			start &= ~(CACHE_LINE_SIZE - 1);
+			writel_relaxed(start, base + L2X0_CLEAN_INV_LINE_PA);
+			start += CACHE_LINE_SIZE;
+		}
+
+		if (end & (CACHE_LINE_SIZE - 1)) {
+			end &= ~(CACHE_LINE_SIZE - 1);
+			l2c_wait_mask(base + L2X0_CLEAN_INV_LINE_PA, 1);
+			writel_relaxed(end, base + L2X0_CLEAN_INV_LINE_PA);
+		}
+	}
+
+	flags = l2c220_op_pa_range(base + L2X0_INV_LINE_PA,
+				   start, end, flags);
+	l2c_wait_mask(base + L2X0_INV_LINE_PA, 1);
+	__l2c220_cache_sync(base);
+	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
+}
+
+static void l2c220_clean_range(unsigned long start, unsigned long end)
+{
+	void __iomem *base = l2x0_base;
+	unsigned long flags;
+
+	start &= ~(CACHE_LINE_SIZE - 1);
+	if ((end - start) >= l2x0_size) {
+		l2c220_op_way(base, L2X0_CLEAN_WAY);
+		return;
+	}
+
+	raw_spin_lock_irqsave(&l2x0_lock, flags);
+	flags = l2c220_op_pa_range(base + L2X0_CLEAN_LINE_PA,
+				   start, end, flags);
+	l2c_wait_mask(base + L2X0_CLEAN_INV_LINE_PA, 1);
+	__l2c220_cache_sync(base);
+	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
+}
+
+static void l2c220_flush_range(unsigned long start, unsigned long end)
+{
+	void __iomem *base = l2x0_base;
+	unsigned long flags;
+
+	start &= ~(CACHE_LINE_SIZE - 1);
+	if ((end - start) >= l2x0_size) {
+		l2c220_op_way(base, L2X0_CLEAN_INV_WAY);
+		return;
+	}
+
+	raw_spin_lock_irqsave(&l2x0_lock, flags);
+	flags = l2c220_op_pa_range(base + L2X0_CLEAN_INV_LINE_PA,
+				   start, end, flags);
+	l2c_wait_mask(base + L2X0_CLEAN_INV_LINE_PA, 1);
+	__l2c220_cache_sync(base);
+	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
+}
+
+static void l2c220_flush_all(void)
+{
+	l2c220_op_way(l2x0_base, L2X0_CLEAN_INV_WAY);
+}
+
+static void l2c220_sync(void)
+{
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&l2x0_lock, flags);
+	__l2c220_cache_sync(l2x0_base);
+	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
+}
+
+static const struct l2c_init_data l2c220_data = {
+	.num_lock = 1,
+	.enable = l2c_enable,
+	.outer_cache = {
+		.inv_range = l2c220_inv_range,
+		.clean_range = l2c220_clean_range,
+		.flush_range = l2c220_flush_range,
+		.flush_all = l2c220_flush_all,
+		.disable = l2c_disable,
+		.sync = l2c220_sync,
+		.resume = l2c210_resume,
+	},
+};
+
+/*
  * L2C-310 specific code.
  *
  * Very similar to L2C-210, the PA, set/way and sync operations are atomic,
@@ -831,6 +973,10 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
 		data = &l2c210_data;
 		break;
 
+	case L2X0_CACHE_ID_PART_L220:
+		data = &l2c220_data;
+		break;
+
 	case L2X0_CACHE_ID_PART_L310:
 		data = &l2c310_init_fns;
 		break;
@@ -895,17 +1041,18 @@ static const struct l2c_init_data of_l2c210_data __initconst = {
 	},
 };
 
-static const struct l2c_init_data of_l2x0_data __initconst = {
+static const struct l2c_init_data of_l2c220_data __initconst = {
+	.num_lock = 1,
 	.of_parse = l2x0_of_parse,
-	.enable = l2x0_enable,
+	.enable = l2c_enable,
 	.outer_cache = {
-		.inv_range   = l2x0_inv_range,
-		.clean_range = l2x0_clean_range,
-		.flush_range = l2x0_flush_range,
-		.flush_all   = l2x0_flush_all,
-		.disable     = l2x0_disable,
-		.sync        = l2x0_cache_sync,
-		.resume      = l2x0_resume,
+		.inv_range   = l2c220_inv_range,
+		.clean_range = l2c220_clean_range,
+		.flush_range = l2c220_flush_range,
+		.flush_all   = l2c220_flush_all,
+		.disable     = l2c_disable,
+		.sync        = l2c220_sync,
+		.resume      = l2c210_resume,
 	},
 };
 
@@ -1342,7 +1489,7 @@ static const struct l2c_init_data of_tauros3_data __initconst = {
 #define L2C_ID(name, fns) { .compatible = name, .data = (void *)&fns }
 static const struct of_device_id l2x0_ids[] __initconst = {
 	L2C_ID("arm,l210-cache", of_l2c210_data),
-	L2C_ID("arm,l220-cache", of_l2x0_data),
+	L2C_ID("arm,l220-cache", of_l2c220_data),
 	L2C_ID("arm,pl310-cache", of_l2c310_data),
 	L2C_ID("brcm,bcm11351-a2-pl310-cache", of_bcm_l2x0_data),
 	L2C_ID("marvell,aurora-outer-cache", of_aurora_with_outer_data),
Signature · 1 line
1.8.3.1
#35 lore raw

PATCH 35/222 ARM: l2c: convert Broadcom L2C-310 to new code

In reply to Russell King - ARM Linux

The Broadcom L2C-310 devices use ARMs L2C-310 R2P3 or later. These require no errata workarounds, and so we can directly call the l2c210 functions from their methods.

---
 arch/arm/mm/cache-l2x0.c | 27 +++++++++++----------------
 1 file changed, 11 insertions(+), 16 deletions(-)
arch/arm/mm/cache-l2x0.c +11 -16
index 751c3d7a22b3..57680e03da84 100644
@@ -1360,16 +1360,16 @@ static void bcm_inv_range(unsigned long start, unsigned long end)
 
 	/* normal case, no cross section between start and end */
 	if (likely(bcm_addr_is_sys_emi(end) || !bcm_addr_is_sys_emi(start))) {
-		l2x0_inv_range(new_start, new_end);
+		l2c210_inv_range(new_start, new_end);
 		return;
 	}
 
 	/* They cross sections, so it can only be a cross from section
 	 * 2 to section 3
 	 */
-	l2x0_inv_range(new_start,
+	l2c210_inv_range(new_start,
 		bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR-1));
-	l2x0_inv_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR),
+	l2c210_inv_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR),
 		new_end);
 }
 
@@ -1382,26 +1382,21 @@ static void bcm_clean_range(unsigned long start, unsigned long end)
 	if (unlikely(end <= start))
 		return;
 
-	if ((end - start) >= l2x0_size) {
-		l2x0_clean_all();
-		return;
-	}
-
 	new_start = bcm_l2_phys_addr(start);
 	new_end = bcm_l2_phys_addr(end);
 
 	/* normal case, no cross section between start and end */
 	if (likely(bcm_addr_is_sys_emi(end) || !bcm_addr_is_sys_emi(start))) {
-		l2x0_clean_range(new_start, new_end);
+		l2c210_clean_range(new_start, new_end);
 		return;
 	}
 
 	/* They cross sections, so it can only be a cross from section
 	 * 2 to section 3
 	 */
-	l2x0_clean_range(new_start,
+	l2c210_clean_range(new_start,
 		bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR-1));
-	l2x0_clean_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR),
+	l2c210_clean_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR),
 		new_end);
 }
 
@@ -1415,7 +1410,7 @@ static void bcm_flush_range(unsigned long start, unsigned long end)
 		return;
 
 	if ((end - start) >= l2x0_size) {
-		l2x0_flush_all();
+		outer_cache.flush_all();
 		return;
 	}
 
@@ -1424,24 +1419,24 @@ static void bcm_flush_range(unsigned long start, unsigned long end)
 
 	/* normal case, no cross section between start and end */
 	if (likely(bcm_addr_is_sys_emi(end) || !bcm_addr_is_sys_emi(start))) {
-		l2x0_flush_range(new_start, new_end);
+		l2c210_flush_range(new_start, new_end);
 		return;
 	}
 
 	/* They cross sections, so it can only be a cross from section
 	 * 2 to section 3
 	 */
-	l2x0_flush_range(new_start,
+	l2c210_flush_range(new_start,
 		bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR-1));
-	l2x0_flush_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR),
+	l2c210_flush_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR),
 		new_end);
 }
 
+/* Broadcom L2C-310 start from ARMs R3P2 or later, and require no fixups */
 static const struct l2c_init_data of_bcm_l2x0_data __initconst = {
 	.num_lock = 8,
 	.of_parse = l2c310_of_parse,
 	.enable = l2c_enable,
-	.fixup = l2c310_fixup,
 	.save  = l2c310_save,
 	.outer_cache = {
 		.inv_range   = bcm_inv_range,
Signature · 1 line
1.8.3.1
#36 lore raw

PATCH 36/222 ARM: l2c: remove obsolete l2x0 ops for non-OF init

In reply to Russell King - ARM Linux

non-OF initialisation has never been used with any cache controller which isn't an ARM cache controller, so we can safely get rid of the old (and buggy) l2x0_*-based operations structure.

This is also the last reference to:
- l2x0_clean_line()
- l2x0_inv_line()
- l2x0_flush_line()
- l2x0_flush_all()
- l2x0_clean_all()
- l2x0_inv_all()
- l2x0_inv_range()
- l2x0_clean_range()
- l2x0_flush_range()
- l2x0_enable()
- l2x0_resume()
so kill those functions too.

---
 arch/arm/mm/cache-l2x0.c | 206 -----------------------------------------------
 1 file changed, 206 deletions(-)
arch/arm/mm/cache-l2x0.c +0 -206
index 57680e03da84..c5d754912f96 100644
@@ -134,20 +134,6 @@ static inline void cache_sync(void)
 	cache_wait(base + L2X0_CACHE_SYNC, 1);
 }
 
-static inline void l2x0_clean_line(unsigned long addr)
-{
-	void __iomem *base = l2x0_base;
-	cache_wait(base + L2X0_CLEAN_LINE_PA, 1);
-	writel_relaxed(addr, base + L2X0_CLEAN_LINE_PA);
-}
-
-static inline void l2x0_inv_line(unsigned long addr)
-{
-	void __iomem *base = l2x0_base;
-	cache_wait(base + L2X0_INV_LINE_PA, 1);
-	writel_relaxed(addr, base + L2X0_INV_LINE_PA);
-}
-
 #if defined(CONFIG_PL310_ERRATA_588369) || defined(CONFIG_PL310_ERRATA_727915)
 static inline void debug_writel(unsigned long val)
 {
@@ -161,27 +147,6 @@ static inline void debug_writel(unsigned long val)
 }
 #endif
 
-#ifdef CONFIG_PL310_ERRATA_588369
-static inline void l2x0_flush_line(unsigned long addr)
-{
-	void __iomem *base = l2x0_base;
-
-	/* Clean by PA followed by Invalidate by PA */
-	cache_wait(base + L2X0_CLEAN_LINE_PA, 1);
-	writel_relaxed(addr, base + L2X0_CLEAN_LINE_PA);
-	cache_wait(base + L2X0_INV_LINE_PA, 1);
-	writel_relaxed(addr, base + L2X0_INV_LINE_PA);
-}
-#else
-
-static inline void l2x0_flush_line(unsigned long addr)
-{
-	void __iomem *base = l2x0_base;
-	cache_wait(base + L2X0_CLEAN_INV_LINE_PA, 1);
-	writel_relaxed(addr, base + L2X0_CLEAN_INV_LINE_PA);
-}
-#endif
-
 static void l2x0_cache_sync(void)
 {
 	unsigned long flags;
@@ -209,131 +174,6 @@ static void l2x0_flush_all(void)
 	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
-static void l2x0_clean_all(void)
-{
-	unsigned long flags;
-
-	/* clean all ways */
-	raw_spin_lock_irqsave(&l2x0_lock, flags);
-	__l2c_op_way(l2x0_base + L2X0_CLEAN_WAY);
-	cache_sync();
-	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
-}
-
-static void l2x0_inv_all(void)
-{
-	unsigned long flags;
-
-	/* invalidate all ways */
-	raw_spin_lock_irqsave(&l2x0_lock, flags);
-	/* Invalidating when L2 is enabled is a nono */
-	BUG_ON(readl(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN);
-	__l2c_op_way(l2x0_base + L2X0_INV_WAY);
-	cache_sync();
-	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
-}
-
-static void l2x0_inv_range(unsigned long start, unsigned long end)
-{
-	void __iomem *base = l2x0_base;
-	unsigned long flags;
-
-	raw_spin_lock_irqsave(&l2x0_lock, flags);
-	if (start & (CACHE_LINE_SIZE - 1)) {
-		start &= ~(CACHE_LINE_SIZE - 1);
-		debug_writel(0x03);
-		l2x0_flush_line(start);
-		debug_writel(0x00);
-		start += CACHE_LINE_SIZE;
-	}
-
-	if (end & (CACHE_LINE_SIZE - 1)) {
-		end &= ~(CACHE_LINE_SIZE - 1);
-		debug_writel(0x03);
-		l2x0_flush_line(end);
-		debug_writel(0x00);
-	}
-
-	while (start < end) {
-		unsigned long blk_end = start + min(end - start, 4096UL);
-
-		while (start < blk_end) {
-			l2x0_inv_line(start);
-			start += CACHE_LINE_SIZE;
-		}
-
-		if (blk_end < end) {
-			raw_spin_unlock_irqrestore(&l2x0_lock, flags);
-			raw_spin_lock_irqsave(&l2x0_lock, flags);
-		}
-	}
-	cache_wait(base + L2X0_INV_LINE_PA, 1);
-	cache_sync();
-	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
-}
-
-static void l2x0_clean_range(unsigned long start, unsigned long end)
-{
-	void __iomem *base = l2x0_base;
-	unsigned long flags;
-
-	if ((end - start) >= l2x0_size) {
-		l2x0_clean_all();
-		return;
-	}
-
-	raw_spin_lock_irqsave(&l2x0_lock, flags);
-	start &= ~(CACHE_LINE_SIZE - 1);
-	while (start < end) {
-		unsigned long blk_end = start + min(end - start, 4096UL);
-
-		while (start < blk_end) {
-			l2x0_clean_line(start);
-			start += CACHE_LINE_SIZE;
-		}
-
-		if (blk_end < end) {
-			raw_spin_unlock_irqrestore(&l2x0_lock, flags);
-			raw_spin_lock_irqsave(&l2x0_lock, flags);
-		}
-	}
-	cache_wait(base + L2X0_CLEAN_LINE_PA, 1);
-	cache_sync();
-	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
-}
-
-static void l2x0_flush_range(unsigned long start, unsigned long end)
-{
-	void __iomem *base = l2x0_base;
-	unsigned long flags;
-
-	if ((end - start) >= l2x0_size) {
-		l2x0_flush_all();
-		return;
-	}
-
-	raw_spin_lock_irqsave(&l2x0_lock, flags);
-	start &= ~(CACHE_LINE_SIZE - 1);
-	while (start < end) {
-		unsigned long blk_end = start + min(end - start, 4096UL);
-
-		debug_writel(0x03);
-		while (start < blk_end) {
-			l2x0_flush_line(start);
-			start += CACHE_LINE_SIZE;
-		}
-		debug_writel(0x00);
-
-		if (blk_end < end) {
-			raw_spin_unlock_irqrestore(&l2x0_lock, flags);
-			raw_spin_lock_irqsave(&l2x0_lock, flags);
-		}
-	}
-	cache_wait(base + L2X0_CLEAN_INV_LINE_PA, 1);
-	cache_sync();
-	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
-}
-
 static void l2x0_disable(void)
 {
 	unsigned long flags;
@@ -345,49 +185,6 @@ static void l2x0_disable(void)
 	raw_spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
-static void l2x0_enable(void __iomem *base, u32 aux, unsigned num_lock)
-{
-	unsigned id;
-
-	id = readl_relaxed(base + L2X0_CACHE_ID) & L2X0_CACHE_ID_PART_MASK;
-	if (id == L2X0_CACHE_ID_PART_L310)
-		num_lock = 8;
-	else
-		num_lock = 1;
-
-	/* l2x0 controller is disabled */
-	writel_relaxed(aux, base + L2X0_AUX_CTRL);
-
-	/* Make sure that I&D is not locked down when starting */
-	l2c_unlock(base, num_lock);
-
-	l2x0_inv_all();
-
-	/* enable L2X0 */
-	writel_relaxed(L2X0_CTRL_EN, base + L2X0_CTRL);
-}
-
-static void l2x0_resume(void)
-{
-	void __iomem *base = l2x0_base;
-
-	if (!(readl_relaxed(base + L2X0_CTRL) & L2X0_CTRL_EN))
-		l2x0_enable(base, l2x0_saved_regs.aux_ctrl, 0);
-}
-
-static const struct l2c_init_data l2x0_init_fns __initconst = {
-	.enable = l2x0_enable,
-	.outer_cache = {
-		.inv_range = l2x0_inv_range,
-		.clean_range = l2x0_clean_range,
-		.flush_range = l2x0_flush_range,
-		.flush_all = l2x0_flush_all,
-		.disable = l2x0_disable,
-		.sync = l2x0_cache_sync,
-		.resume = l2x0_resume,
-	},
-};
-
 /*
  * L2C-210 specific code.
  *
@@ -966,9 +763,6 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
 
 	switch (cache_id & L2X0_CACHE_ID_PART_MASK) {
 	default:
-		data = &l2x0_init_fns;
-		break;
-
 	case L2X0_CACHE_ID_PART_L210:
 		data = &l2c210_data;
 		break;
Signature · 1 line
1.8.3.1
#37 lore raw

PATCH 37/222 ARM: l2c: move type string into l2c_init_data structure

In reply to Russell King - ARM Linux

Rather than decoding this from the ID register, store it in the l2c_init_data structure. This simplifies things some more, and allows us to better provide further details as to how we're driving the cache. We print the cache ID value anyway should we need to precisely identify the cache hardware.

---
 arch/arm/mm/cache-l2x0.c | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)
arch/arm/mm/cache-l2x0.c +13 -7
index c5d754912f96..b4dd2f4b491b 100644
@@ -29,6 +29,7 @@
 #include "cache-aurora-l2.h"
 
 struct l2c_init_data {
+	const char *type;
 	unsigned num_lock;
 	void (*of_parse)(const struct device_node *, u32 *, u32 *);
 	void (*enable)(void __iomem *, u32, unsigned);
@@ -274,6 +275,7 @@ static void l2c210_resume(void)
 }
 
 static const struct l2c_init_data l2c210_data __initconst = {
+	.type = "L2C-210",
 	.num_lock = 1,
 	.enable = l2c_enable,
 	.outer_cache = {
@@ -416,6 +418,7 @@ static void l2c220_sync(void)
 }
 
 static const struct l2c_init_data l2c220_data = {
+	.type = "L2C-220",
 	.num_lock = 1,
 	.enable = l2c_enable,
 	.outer_cache = {
@@ -650,6 +653,7 @@ static void __init l2c310_fixup(void __iomem *base, u32 cache_id,
 }
 
 static const struct l2c_init_data l2c310_init_fns __initconst = {
+	.type = "L2C-310",
 	.num_lock = 8,
 	.enable = l2c_enable,
 	.fixup = l2c310_fixup,
@@ -674,7 +678,6 @@ static void __init __l2c_init(const struct l2c_init_data *data,
 	u32 way_size = 0;
 	int ways;
 	int way_size_shift = L2X0_WAY_SIZE_SHIFT;
-	const char *type;
 
 	/*
 	 * It is strange to save the register state before initialisation,
@@ -695,25 +698,21 @@ static void __init __l2c_init(const struct l2c_init_data *data,
 			ways = 16;
 		else
 			ways = 8;
-		type = "L310";
 		break;
 
 	case L2X0_CACHE_ID_PART_L210:
 		ways = (aux >> 13) & 0xf;
-		type = "L210";
 		break;
 
 	case AURORA_CACHE_ID:
 		ways = (aux >> 13) & 0xf;
 		ways = 2 << ((ways + 1) >> 2);
 		way_size_shift = AURORA_WAY_SIZE_SHIFT;
-		type = "Aurora";
 		break;
 
 	default:
 		/* Assume unknown chips have 8 ways */
 		ways = 8;
-		type = "L2x0 series";
 		break;
 	}
 
@@ -747,9 +746,9 @@ static void __init __l2c_init(const struct l2c_init_data *data,
 	outer_cache = fns;
 
 	pr_info("%s cache controller enabled, %d ways, %d kB\n",
-		type, ways, l2x0_size >> 10);
+		data->type, ways, l2x0_size >> 10);
 	pr_info("%s: CACHE_ID 0x%08x, AUX_CTRL 0x%08x\n",
-		type, cache_id, aux);
+		data->type, cache_id, aux);
 }
 
 void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
@@ -821,6 +820,7 @@ static void __init l2x0_of_parse(const struct device_node *np,
 }
 
 static const struct l2c_init_data of_l2c210_data __initconst = {
+	.type = "L2C-210",
 	.num_lock = 1,
 	.of_parse = l2x0_of_parse,
 	.enable = l2c_enable,
@@ -836,6 +836,7 @@ static const struct l2c_init_data of_l2c210_data __initconst = {
 };
 
 static const struct l2c_init_data of_l2c220_data __initconst = {
+	.type = "L2C-220",
 	.num_lock = 1,
 	.of_parse = l2x0_of_parse,
 	.enable = l2c_enable,
@@ -885,6 +886,7 @@ static void __init l2c310_of_parse(const struct device_node *np,
 }
 
 static const struct l2c_init_data of_l2c310_data __initconst = {
+	.type = "L2C-310",
 	.num_lock = 8,
 	.of_parse = l2c310_of_parse,
 	.enable = l2c_enable,
@@ -1063,6 +1065,7 @@ static void __init aurora_of_parse(const struct device_node *np,
 }
 
 static const struct l2c_init_data of_aurora_with_outer_data __initconst = {
+	.type = "Aurora",
 	.num_lock = 4,
 	.of_parse = aurora_of_parse,
 	.enable = l2c_enable,
@@ -1080,6 +1083,7 @@ static const struct l2c_init_data of_aurora_with_outer_data __initconst = {
 };
 
 static const struct l2c_init_data of_aurora_no_outer_data __initconst = {
+	.type = "Aurora",
 	.num_lock = 4,
 	.of_parse = aurora_of_parse,
 	.enable = aurora_enable_no_outer,
@@ -1228,6 +1232,7 @@ static void bcm_flush_range(unsigned long start, unsigned long end)
 
 /* Broadcom L2C-310 start from ARMs R3P2 or later, and require no fixups */
 static const struct l2c_init_data of_bcm_l2x0_data __initconst = {
+	.type = "BCM-L2C-310",
 	.num_lock = 8,
 	.of_parse = l2c310_of_parse,
 	.enable = l2c_enable,
@@ -1266,6 +1271,7 @@ static void tauros3_resume(void)
 }
 
 static const struct l2c_init_data of_tauros3_data __initconst = {
+	.type = "Tauros3",
 	.num_lock = 8,
 	.enable = l2c_enable,
 	.save  = tauros3_save,
Signature · 1 line
1.8.3.1
#38 lore raw

PATCH 38/222 ARM: l2c: add decode for L2C-220 cache ways

In reply to Russell King - ARM Linux

Rather than assuming these are always 8-way, it can be decoded from the auxillary register in the same manner as L2C-210.

---
 arch/arm/mm/cache-l2x0.c | 1 +
 1 file changed, 1 insertion(+)
arch/arm/mm/cache-l2x0.c +1 -0
index b4dd2f4b491b..69a18316b239 100644
@@ -701,6 +701,7 @@ static void __init __l2c_init(const struct l2c_init_data *data,
 		break;
 
 	case L2X0_CACHE_ID_PART_L210:
+	case L2X0_CACHE_ID_PART_L220:
 		ways = (aux >> 13) & 0xf;
 		break;
 
Signature · 1 line
1.8.3.1
#39 lore raw

PATCH 39/222 ARM: l2c: move way size calculation data into l2c_init_data

In reply to Russell King - ARM Linux

Move the way size calculation data (base of way size) out of the switch statement into the provided initialisation data.

---
 arch/arm/mm/cache-l2x0.c | 29 ++++++++++++++++++++---------
 1 file changed, 20 insertions(+), 9 deletions(-)
arch/arm/mm/cache-l2x0.c +20 -9
index 69a18316b239..b4d373ab1a5c 100644
@@ -30,6 +30,7 @@
 
 struct l2c_init_data {
 	const char *type;
+	unsigned way_size_0;
 	unsigned num_lock;
 	void (*of_parse)(const struct device_node *, u32 *, u32 *);
 	void (*enable)(void __iomem *, u32, unsigned);
@@ -276,6 +277,7 @@ static void l2c210_resume(void)
 
 static const struct l2c_init_data l2c210_data __initconst = {
 	.type = "L2C-210",
+	.way_size_0 = SZ_8K,
 	.num_lock = 1,
 	.enable = l2c_enable,
 	.outer_cache = {
@@ -419,6 +421,7 @@ static void l2c220_sync(void)
 
 static const struct l2c_init_data l2c220_data = {
 	.type = "L2C-220",
+	.way_size_0 = SZ_8K,
 	.num_lock = 1,
 	.enable = l2c_enable,
 	.outer_cache = {
@@ -654,6 +657,7 @@ static void __init l2c310_fixup(void __iomem *base, u32 cache_id,
 
 static const struct l2c_init_data l2c310_init_fns __initconst = {
 	.type = "L2C-310",
+	.way_size_0 = SZ_8K,
 	.num_lock = 8,
 	.enable = l2c_enable,
 	.fixup = l2c310_fixup,
@@ -674,10 +678,8 @@ static void __init __l2c_init(const struct l2c_init_data *data,
 	u32 aux_val, u32 aux_mask, u32 cache_id)
 {
 	struct outer_cache_fns fns;
+	unsigned way_size_bits, ways;
 	u32 aux;
-	u32 way_size = 0;
-	int ways;
-	int way_size_shift = L2X0_WAY_SIZE_SHIFT;
 
 	/*
 	 * It is strange to save the register state before initialisation,
@@ -708,7 +710,6 @@ static void __init __l2c_init(const struct l2c_init_data *data,
 	case AURORA_CACHE_ID:
 		ways = (aux >> 13) & 0xf;
 		ways = 2 << ((ways + 1) >> 2);
-		way_size_shift = AURORA_WAY_SIZE_SHIFT;
 		break;
 
 	default:
@@ -720,12 +721,15 @@ static void __init __l2c_init(const struct l2c_init_data *data,
 	l2x0_way_mask = (1 << ways) - 1;
 
 	/*
-	 * L2 cache Size =  Way size * Number of ways
+	 * way_size_0 is the size that a way_size value of zero would be
+	 * given the calculation: way_size = way_size_0 << way_size_bits.
+	 * So, if way_size_bits=0 is reserved, but way_size_bits=1 is 16k,
+	 * then way_size_0 would be 8k.
+	 *
+	 * L2 cache size = number of ways * way size.
 	 */
-	way_size = (aux & L2X0_AUX_CTRL_WAY_SIZE_MASK) >> 17;
-	way_size = 1 << (way_size + way_size_shift);
-
-	l2x0_size = ways * way_size * SZ_1K;
+	way_size_bits = (aux & L2X0_AUX_CTRL_WAY_SIZE_MASK) >> 17;
+	l2x0_size = ways * (data->way_size_0 << way_size_bits);
 
 	fns = data->outer_cache;
 	if (data->fixup)
@@ -822,6 +826,7 @@ static void __init l2x0_of_parse(const struct device_node *np,
 
 static const struct l2c_init_data of_l2c210_data __initconst = {
 	.type = "L2C-210",
+	.way_size_0 = SZ_8K,
 	.num_lock = 1,
 	.of_parse = l2x0_of_parse,
 	.enable = l2c_enable,
@@ -838,6 +843,7 @@ static const struct l2c_init_data of_l2c210_data __initconst = {
 
 static const struct l2c_init_data of_l2c220_data __initconst = {
 	.type = "L2C-220",
+	.way_size_0 = SZ_8K,
 	.num_lock = 1,
 	.of_parse = l2x0_of_parse,
 	.enable = l2c_enable,
@@ -888,6 +894,7 @@ static void __init l2c310_of_parse(const struct device_node *np,
 
 static const struct l2c_init_data of_l2c310_data __initconst = {
 	.type = "L2C-310",
+	.way_size_0 = SZ_8K,
 	.num_lock = 8,
 	.of_parse = l2c310_of_parse,
 	.enable = l2c_enable,
@@ -1067,6 +1074,7 @@ static void __init aurora_of_parse(const struct device_node *np,
 
 static const struct l2c_init_data of_aurora_with_outer_data __initconst = {
 	.type = "Aurora",
+	.way_size_0 = SZ_4K,
 	.num_lock = 4,
 	.of_parse = aurora_of_parse,
 	.enable = l2c_enable,
@@ -1085,6 +1093,7 @@ static const struct l2c_init_data of_aurora_with_outer_data __initconst = {
 
 static const struct l2c_init_data of_aurora_no_outer_data __initconst = {
 	.type = "Aurora",
+	.way_size_0 = SZ_4K,
 	.num_lock = 4,
 	.of_parse = aurora_of_parse,
 	.enable = aurora_enable_no_outer,
@@ -1234,6 +1243,7 @@ static void bcm_flush_range(unsigned long start, unsigned long end)
 /* Broadcom L2C-310 start from ARMs R3P2 or later, and require no fixups */
 static const struct l2c_init_data of_bcm_l2x0_data __initconst = {
 	.type = "BCM-L2C-310",
+	.way_size_0 = SZ_8K,
 	.num_lock = 8,
 	.of_parse = l2c310_of_parse,
 	.enable = l2c_enable,
@@ -1273,6 +1283,7 @@ static void tauros3_resume(void)
 
 static const struct l2c_init_data of_tauros3_data __initconst = {
 	.type = "Tauros3",
+	.way_size_0 = SZ_8K,
 	.num_lock = 8,
 	.enable = l2c_enable,
 	.save  = tauros3_save,
Signature · 1 line
1.8.3.1
#40 lore raw

PATCH 40/222 ARM: l2c: move errata configuration options to arch/arm/mm/Kconfig

In reply to Russell King - ARM Linux

Move the L2C-310 errata configuration options to arch/arm/mm/Kconfig along side the option which enables support for this device.

---
 arch/arm/Kconfig    | 51 ---------------------------------------------------
 arch/arm/mm/Kconfig | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 51 insertions(+), 51 deletions(-)
arch/arm/Kconfig +0 -51
index ab438cb5af55..b22f990bdd06 100644
@@ -1229,19 +1229,6 @@ config ARM_ERRATA_742231
 	  register of the Cortex-A9 which reduces the linefill issuing
 	  capabilities of the processor.
 
-config PL310_ERRATA_588369
-	bool "PL310 errata: Clean & Invalidate maintenance operations do not invalidate clean lines"
-	depends on CACHE_L2X0
-	help
-	   The PL310 L2 cache controller implements three types of Clean &
-	   Invalidate maintenance operations: by Physical Address
-	   (offset 0x7F0), by Index/Way (0x7F8) and by Way (0x7FC).
-	   They are architecturally defined to behave as the execution of a
-	   clean operation followed immediately by an invalidate operation,
-	   both performing to the same memory location. This functionality
-	   is not correctly implemented in PL310 as clean lines are not
-	   invalidated as a result of these operations.
-
 config ARM_ERRATA_643719
 	bool "ARM errata: LoUIS bit field in CLIDR register is incorrect"
 	depends on CPU_V7 && SMP
@@ -1264,17 +1251,6 @@ config ARM_ERRATA_720789
 	  tables. The workaround changes the TLB flushing routines to invalidate
 	  entries regardless of the ASID.
 
-config PL310_ERRATA_727915
-	bool "PL310 errata: Background Clean & Invalidate by Way operation can cause data corruption"
-	depends on CACHE_L2X0
-	help
-	  PL310 implements the Clean & Invalidate by Way L2 cache maintenance
-	  operation (offset 0x7FC). This operation runs in background so that
-	  PL310 can handle normal accesses while it is in progress. Under very
-	  rare circumstances, due to this erratum, write data can be lost when
-	  PL310 treats a cacheable write transaction during a Clean &
-	  Invalidate by Way operation.
-
 config ARM_ERRATA_743622
 	bool "ARM errata: Faulty hazard checking in the Store Buffer may lead to data corruption"
 	depends on CPU_V7
@@ -1300,21 +1276,6 @@ config ARM_ERRATA_751472
 	  operation is received by a CPU before the ICIALLUIS has completed,
 	  potentially leading to corrupted entries in the cache or TLB.
 
-config PL310_ERRATA_753970
-	bool "PL310 errata: cache sync operation may be faulty"
-	depends on CACHE_PL310
-	help
-	  This option enables the workaround for the 753970 PL310 (r3p0) erratum.
-
-	  Under some condition the effect of cache sync operation on
-	  the store buffer still remains when the operation completes.
-	  This means that the store buffer is always asked to drain and
-	  this prevents it from merging any further writes. The workaround
-	  is to replace the normal offset of cache sync operation (0x730)
-	  by another offset targeting an unmapped PL310 register 0x740.
-	  This has the same effect as the cache sync operation: store buffer
-	  drain and waiting for all buffers empty.
-
 config ARM_ERRATA_754322
 	bool "ARM errata: possible faulty MMU translations following an ASID switch"
 	depends on CPU_V7
@@ -1363,18 +1324,6 @@ config ARM_ERRATA_764369
 	  relevant cache maintenance functions and sets a specific bit
 	  in the diagnostic control register of the SCU.
 
-config PL310_ERRATA_769419
-	bool "PL310 errata: no automatic Store Buffer drain"
-	depends on CACHE_L2X0
-	help
-	  On revisions of the PL310 prior to r3p2, the Store Buffer does
-	  not automatically drain. This can cause normal, non-cacheable
-	  writes to be retained when the memory system is idle, leading
-	  to suboptimal I/O performance for drivers using coherent DMA.
-	  This option adds a write barrier to the cpu_idle loop so that,
-	  on systems with an outer cache, the store buffer is drained
-	  explicitly.
-
 config ARM_ERRATA_775420
        bool "ARM errata: A data cache maintenance operation which aborts, might lead to deadlock"
        depends on CPU_V7
arch/arm/mm/Kconfig +51 -0

57 lines not on this page · raw message

Signature · 1 line
1.8.3.1
#41 lore raw

PATCH 41/222 ARM: l2c: provide generic hook to intercept writes to secure registers

In reply to Russell King - ARM Linux

When Linux is running in the non-secure world, any write to a secure L2C register will generate an abort. Platforms normally have to call firmware to work around this. Provide a hook for them to intercept any L2C secure register write.

l2c_write_sec() avoids writes to secure registers which are already set to the appropriate value, thus avoiding the overhead of needlessly calling into the secure monitor.

---
 arch/arm/include/asm/outercache.h |  5 ++++-
 arch/arm/mm/cache-l2x0.c          | 42 ++++++++++++++++++++++++++++-----------
 2 files changed, 34 insertions(+), 13 deletions(-)
arch/arm/include/asm/outercache.h +4 -1
index e9a0797fe188..5066fa2f73ab 100644
@@ -33,8 +33,11 @@ struct outer_cache_fns {
 #ifdef CONFIG_OUTER_CACHE_SYNC
 	void (*sync)(void);
 #endif
-	void (*set_debug)(unsigned long);
 	void (*resume)(void);
+
+	/* This is an ARM L2C thing */
+	void (*set_debug)(unsigned long);
+	void (*write_sec)(unsigned long, unsigned);
 };
 
 extern struct outer_cache_fns outer_cache;
arch/arm/mm/cache-l2x0.c +30 -12

99 lines not on this page · raw message

Signature · 1 line
1.8.3.1
#42 lore raw

PATCH 42/222 ARM: l2c: omap2: implement new write_sec method

In reply to Russell King - ARM Linux

With the write_sec method, we no longer need to override the default L2C disable method, and we no longer need the L2C set_debug method. Both of these can be handled via the write_sec method.

---
 arch/arm/mach-omap2/omap4-common.c | 42 +++++++++++++++++++++++---------------
 1 file changed, 26 insertions(+), 16 deletions(-)
arch/arm/mach-omap2/omap4-common.c +26 -16
index 48cf74d284ec..a51501ad7e83 100644
@@ -167,17 +167,33 @@ void __iomem *omap4_get_l2cache_base(void)
 	return l2cache_base;
 }
 
-static void omap4_l2x0_disable(void)
+static void omap4_l2c310_write_sec(unsigned long val, unsigned reg)
 {
-	outer_flush_all();
-	/* Disable PL310 L2 Cache controller */
-	omap_smc1(0x102, 0x0);
-}
+	unsigned smc_op;
 
-static void omap4_l2x0_set_debug(unsigned long val)
-{
-	/* Program PL310 L2 Cache controller debug register */
-	omap_smc1(0x100, val);
+	switch (reg) {
+	case L2X0_CTRL:
+		smc_op = OMAP4_MON_L2X0_CTRL_INDEX;
+		break;
+
+	case L2X0_AUX_CTRL:
+		smc_op = OMAP4_MON_L2X0_AUXCTRL_INDEX;
+		break;
+
+	case L2X0_DEBUG_CTRL:
+		smc_op = OMAP4_MON_L2X0_DBG_CTRL_INDEX;
+		break;
+
+	case L310_PREFETCH_CTRL:
+		smc_op = OMAP4_MON_L2X0_PREFETCH_INDEX;
+		break;
+
+	default:
+		WARN_ONCE(1, "OMAP L2C310: ignoring write to reg 0x%x\n", reg);
+		return;
+	}
+
+	omap_smc1(smc_op, val);
 }
 
 static int __init omap_l2_cache_init(void)
@@ -212,18 +228,12 @@ static int __init omap_l2_cache_init(void)
 	/* Enable PL310 L2 Cache controller */
 	omap_smc1(0x102, 0x1);
 
+	outer_cache.write_sec = omap4_l2c310_write_sec;
 	if (of_have_populated_dt())
 		l2x0_of_init(aux_ctrl, L2X0_AUX_CTRL_MASK);
 	else
 		l2x0_init(l2cache_base, aux_ctrl, L2X0_AUX_CTRL_MASK);
 
-	/*
-	 * Override default outer_cache.disable with a OMAP4
-	 * specific one
-	*/
-	outer_cache.disable = omap4_l2x0_disable;
-	outer_cache.set_debug = omap4_l2x0_set_debug;
-
 	return 0;
 }
 omap_early_initcall(omap_l2_cache_init);
Signature · 1 line
1.8.3.1
#43 lore raw

PATCH 43/222 ARM: l2c: omap2: remove explicit SMI calls to enable L2 cache

In reply to Russell King - ARM Linux

Now that OMAP2 uses the write_sec method, we don't need to enable the L2 cache in OMAP2 specific code; this can be done via the normal mechanisms in the L2C code. Remove the OMAP2 specific code.

---
 arch/arm/mach-omap2/omap4-common.c | 5 -----
 1 file changed, 5 deletions(-)
arch/arm/mach-omap2/omap4-common.c +0 -5
index a51501ad7e83..46dac72aaa4c 100644
@@ -223,11 +223,6 @@ static int __init omap_l2_cache_init(void)
 			(1 << L2X0_AUX_CTRL_INSTR_PREFETCH_SHIFT) |
 			(1 << L2X0_AUX_CTRL_EARLY_BRESP_SHIFT);
 
-	omap_smc1(0x109, aux_ctrl);
-
-	/* Enable PL310 L2 Cache controller */
-	omap_smc1(0x102, 0x1);
-
 	outer_cache.write_sec = omap4_l2c310_write_sec;
 	if (of_have_populated_dt())
 		l2x0_of_init(aux_ctrl, L2X0_AUX_CTRL_MASK);
Signature · 1 line
1.8.3.1
#44 lore raw

PATCH 44/222 ARM: l2c: highbank: implement new write_sec method

In reply to Russell King - ARM Linux

With the write_sec method, we no longer need to override the default L2C disable method. This can be handled via the write_sec method instead.

---
 arch/arm/mach-highbank/highbank.c | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)
arch/arm/mach-highbank/highbank.c +7 -5
index 38e1dc3b4c6e..4712aed3d9f6 100644
@@ -51,11 +51,13 @@ static void __init highbank_scu_map_io(void)
 }
 
 
-static void highbank_l2x0_disable(void)
+static void highbank_l2c310_write_sec(unsigned long val, unsigned reg)
 {
-	outer_flush_all();
-	/* Disable PL310 L2 Cache controller */
-	highbank_smc1(0x102, 0x0);
+	if (reg == L2X0_CTRL)
+		highbank_smc1(0x102, val);
+	else
+		WARN_ONCE(1, "Highbank L2C310: ignoring write to reg 0x%x\n",
+			  reg);
 }
 
 static void __init highbank_init_irq(void)
@@ -69,8 +71,8 @@ static void __init highbank_init_irq(void)
 	if (IS_ENABLED(CONFIG_CACHE_L2X0) &&
 	    of_find_compatible_node(NULL, NULL, "arm,pl310-cache")) {
 		highbank_smc1(0x102, 0x1);
+		outer_cache.write_sec = highbank_l2c310_write_sec;
 		l2x0_of_init(0, ~0);
-		outer_cache.disable = highbank_l2x0_disable;
 	}
 }
 
Signature · 1 line
1.8.3.1
#45 lore raw

PATCH 45/222 ARM: l2c: highbank: remove explicit SMI call in L2 cache initialisation

In reply to Russell King - ARM Linux

Now that highbank uses the write_sec method, we don't need to enable the L2 cache in SoC specific code; this can be done via the normal mechanisms in the L2C code.

Checking with Rob Herring:

Quoted 7 lines
Quoted 3 lines

Can we kill the "highbank_smc1(0x102, 0x1);" here? That means
l2x0_of_init() will see the L2 cache disabled, and will try to enable it via the write_sec hook, so it should do the right thing.

Yes, that should work. You should be able to just call l2x0_of_init unconditionally. The condition was really to just avoid the smc on Midway which does get handled on h/w, but not if running virtualized.

So also drop the DT check too. I'm leaving the config check in place so that if L2 is disabled, the write_sec hook can be optimised away.

---
 arch/arm/mach-highbank/highbank.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)
arch/arm/mach-highbank/highbank.c +1 -3
index 4712aed3d9f6..245e588859ec 100644
@@ -68,9 +68,7 @@ static void __init highbank_init_irq(void)
 		highbank_scu_map_io();
 
 	/* Enable PL310 L2 Cache controller */
-	if (IS_ENABLED(CONFIG_CACHE_L2X0) &&
-	    of_find_compatible_node(NULL, NULL, "arm,pl310-cache")) {
-		highbank_smc1(0x102, 0x1);
+	if (IS_ENABLED(CONFIG_CACHE_L2X0)) {
 		outer_cache.write_sec = highbank_l2c310_write_sec;
 		l2x0_of_init(0, ~0);
 	}
Signature · 1 line
1.8.3.1
#46 lore raw

PATCH 46/222 ARM: l2c: ux500: implement dummy write_sec method

In reply to Russell King - ARM Linux

ux500 can't write to any of the secure registers on the L2C controllers, so provide a dummy handler which ignores all writes.

---
 arch/arm/mach-ux500/cache-l2x0.c | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)
arch/arm/mach-ux500/cache-l2x0.c +10 -9
index 264f894c0e3d..5cc7e3625d8c 100644
@@ -35,6 +35,14 @@ static int __init ux500_l2x0_unlock(void)
 	return 0;
 }
 
+static void ux500_l2c310_write_sec(unsigned long val, unsigned reg)
+{
+	/*
+	 * We can't write to secure registers as we are in non-secure
+	 * mode, until we have some SMI service available.
+	 */
+}
+
 static int __init ux500_l2x0_init(void)
 {
 	u32 aux_val = 0x3e000000;
@@ -56,21 +64,14 @@ static int __init ux500_l2x0_init(void)
 		/* 64KB way size */
 		aux_val |= (0x3 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT);
 
+	outer_cache.write_sec = ux500_l2c310_write_sec;
+
 	/* 64KB way size, 8 way associativity, force WA */
 	if (of_have_populated_dt())
 		l2x0_of_init(aux_val, 0xc0000fff);
 	else
 		l2x0_init(l2x0_base, aux_val, 0xc0000fff);
 
-	/*
-	 * We can't disable l2 as we are in non secure mode, currently
-	 * this seems be called only during kexec path. So let's
-	 * override outer.disable with nasty assignment until we have
-	 * some SMI service available.
-	 */
-	outer_cache.disable = NULL;
-	outer_cache.set_debug = NULL;
-
 	return 0;
 }
 
Signature · 1 line
1.8.3.1
#47 lore raw

PATCH 47/222 ARM: l2c: remove old .set_debug method

In reply to Russell King - ARM Linux

We no longer need or require the .set_debug method; we handle everything it used to do via the .write_sec method instead.

---
 arch/arm/include/asm/outercache.h |  1 -
 arch/arm/mm/cache-l2x0.c          | 21 ++-------------------
 2 files changed, 2 insertions(+), 20 deletions(-)
arch/arm/include/asm/outercache.h +0 -1
index 5066fa2f73ab..eaa8a28c6871 100644
@@ -36,7 +36,6 @@ struct outer_cache_fns {
 	void (*resume)(void);
 
 	/* This is an ARM L2C thing */
-	void (*set_debug)(unsigned long);
 	void (*write_sec)(unsigned long, unsigned);
 };
 
arch/arm/mm/cache-l2x0.c +2 -19
index 369a9d01d94f..b13276c9971b 100644
@@ -80,10 +80,7 @@ static void l2c_write_sec(unsigned long val, void __iomem *base, unsigned reg)
  */
 static inline void l2c_set_debug(void __iomem *base, unsigned long val)
 {
-	if (outer_cache.set_debug)
-		outer_cache.set_debug(val);
-	else
-		l2c_write_sec(val, base, L2X0_DEBUG_CTRL);
+	l2c_write_sec(val, base, L2X0_DEBUG_CTRL);
 }
 
 static void __l2c_op_way(void __iomem *reg)
@@ -154,8 +151,7 @@ static inline void cache_sync(void)
 #if defined(CONFIG_PL310_ERRATA_588369) || defined(CONFIG_PL310_ERRATA_727915)
 static inline void debug_writel(unsigned long val)
 {
-	if (outer_cache.set_debug || outer_cache.write_sec)
-		l2c_set_debug(l2x0_base, val);
+	l2c_set_debug(l2x0_base, val);
 }
 #else
 /* Optimised out for non-errata case */
@@ -489,11 +485,6 @@ static const struct l2c_init_data l2c220_data = {
  *	Affects: store buffer
  *	store buffer is not automatically drained.
  */
-static void l2c310_set_debug(unsigned long val)
-{
-	writel_relaxed(val, l2x0_base + L2X0_DEBUG_CTRL);
-}
-
 static void l2c310_inv_range_erratum(unsigned long start, unsigned long end)
 {
 	void __iomem *base = l2x0_base;
@@ -631,10 +622,6 @@ static void __init l2c310_fixup(void __iomem *base, u32 cache_id,
 	const char *errata[4];
 	unsigned n = 0;
 
-	/* For compatibility */
-	if (revision <= L310_CACHE_ID_RTL_R3P0)
-		fns->set_debug = l2c310_set_debug;
-
 	if (IS_ENABLED(CONFIG_PL310_ERRATA_588369) &&
 	    revision < L310_CACHE_ID_RTL_R2P0 &&
 	    /* For bcm compatibility */
@@ -684,7 +671,6 @@ static const struct l2c_init_data l2c310_init_fns __initconst = {
 		.flush_all = l2c210_flush_all,
 		.disable = l2c_disable,
 		.sync = l2c210_sync,
-		.set_debug = l2c310_set_debug,
 		.resume = l2c310_resume,
 	},
 };
@@ -750,8 +736,6 @@ static void __init __l2c_init(const struct l2c_init_data *data,
 	fns.write_sec = outer_cache.write_sec;
 	if (data->fixup)
 		data->fixup(l2x0_base, cache_id, &fns);
-	if (fns.write_sec)
-		fns.set_debug = NULL;
 
 	/*
 	 * Check if l2x0 controller is already enabled.  If we are booting
@@ -925,7 +909,6 @@ static const struct l2c_init_data of_l2c310_data __initconst = {
 		.flush_all   = l2c210_flush_all,
 		.disable     = l2c_disable,
 		.sync        = l2c210_sync,
-		.set_debug   = l2c310_set_debug,
 		.resume      = l2c310_resume,
 	},
 };
Signature · 1 line
1.8.3.1
#48 lore raw

PATCH 48/222 ARM: l2c: implement L2C-310 erratum 752271 in core L2C code

In reply to Russell King - ARM Linux

Rather than having SoCs work around L2C erratum themselves, move them into core code. This erratum affects the double linefill feature which needs to be disabled for r3p0 to r3p1-50rel0.

---
 arch/arm/mm/cache-l2x0.c | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)
arch/arm/mm/cache-l2x0.c +17 -1
index b13276c9971b..b1fa825c133f 100644
@@ -476,6 +476,11 @@ static const struct l2c_init_data l2c220_data = {
  *	hit the line between the clean operation and invalidate operation,
  *	resulting in the store being lost.
  *
+ * 752271: PL310 R3P0->R3P1-50REL0, fixed R3P2.
+ *	Affects: 8x64-bit (double fill) line fetches
+ *	double fill line fetches can fail to cause dirty data to be evicted
+ *	from the cache before the new data overwrites the second line.
+ *
  * 753970: PL310 R3P0, fixed R3P1.
  *	Affects: sync
  *	prevents merging writes after the sync operation, until another L2C
@@ -619,7 +624,7 @@ static void __init l2c310_fixup(void __iomem *base, u32 cache_id,
 	struct outer_cache_fns *fns)
 {
 	unsigned revision = cache_id & L2X0_CACHE_ID_RTL_MASK;
-	const char *errata[4];
+	const char *errata[8];
 	unsigned n = 0;
 
 	if (IS_ENABLED(CONFIG_PL310_ERRATA_588369) &&
@@ -638,6 +643,17 @@ static void __init l2c310_fixup(void __iomem *base, u32 cache_id,
 		errata[n++] = "727915";
 	}
 
+	if (revision >= L310_CACHE_ID_RTL_R3P0 &&
+	    revision < L310_CACHE_ID_RTL_R3P2) {
+		u32 val = readl_relaxed(base + L2X0_PREFETCH_CTRL);
+		/* I don't think bit23 is required here... but iMX6 does so */
+		if (val & (BIT(30) | BIT(23))) {
+			val &= ~(BIT(30) | BIT(23));
+			l2c_write_sec(val, base, L2X0_PREFETCH_CTRL);
+			errata[n++] = "752271";
+		}
+	}
+
 	if (IS_ENABLED(CONFIG_PL310_ERRATA_753970) &&
 	    revision == L310_CACHE_ID_RTL_R3P0) {
 		sync_reg_offset = L2X0_DUMMY_REG;
Signature · 1 line
1.8.3.1
#49 lore raw

PATCH 49/222 ARM: l2c: fix register naming

In reply to Russell King - ARM Linux

We have a mixture of different devices with different register layouts, but we group all the bits together in an opaque mess. Split them out into those which are L2C-310 specific and ones which refer to earlier devices. Provide full auxiliary control register definitions.

---
 arch/arm/include/asm/hardware/cache-l2x0.h | 73 ++++++++++++++++++++----------
 arch/arm/mach-cns3xxx/core.c               |  8 ++--
 arch/arm/mach-exynos/sleep.S               |  8 ++--
 arch/arm/mach-imx/system.c                 |  8 ++--
 arch/arm/mach-omap2/omap-mpuss-lowpower.c  |  2 +-
 arch/arm/mach-omap2/omap4-common.c         | 18 ++++----
 arch/arm/mach-prima2/l2x0.c                |  5 +-
 arch/arm/mach-realview/realview_pbx.c      |  4 +-
 arch/arm/mach-spear/spear13xx.c            |  6 +--
 arch/arm/mach-sti/board-dt.c               |  8 ++--
 arch/arm/mach-tegra/sleep.h                |  8 ++--
 arch/arm/mach-ux500/cache-l2x0.c           |  4 +-
 arch/arm/mach-vexpress/ct-ca9x4.c          |  4 +-
 arch/arm/mm/cache-l2x0.c                   | 57 +++++++++++------------
 14 files changed, 118 insertions(+), 95 deletions(-)
arch/arm/include/asm/hardware/cache-l2x0.h +48 -25
index 3af45734b514..b3ee122c6f24 100644
@@ -26,8 +26,8 @@
 #define L2X0_CACHE_TYPE			0x004
 #define L2X0_CTRL			0x100
 #define L2X0_AUX_CTRL			0x104
-#define L2X0_TAG_LATENCY_CTRL		0x108
-#define L2X0_DATA_LATENCY_CTRL		0x10C
+#define L310_TAG_LATENCY_CTRL		0x108
+#define L310_DATA_LATENCY_CTRL		0x10C
 #define L2X0_EVENT_CNT_CTRL		0x200
 #define L2X0_EVENT_CNT1_CFG		0x204
 #define L2X0_EVENT_CNT0_CFG		0x208
@@ -54,16 +54,16 @@
 #define L2X0_LOCKDOWN_WAY_D_BASE	0x900
 #define L2X0_LOCKDOWN_WAY_I_BASE	0x904
 #define L2X0_LOCKDOWN_STRIDE		0x08
-#define L2X0_ADDR_FILTER_START		0xC00
-#define L2X0_ADDR_FILTER_END		0xC04
+#define L310_ADDR_FILTER_START		0xC00
+#define L310_ADDR_FILTER_END		0xC04
 #define L2X0_TEST_OPERATION		0xF00
 #define L2X0_LINE_DATA			0xF10
 #define L2X0_LINE_TAG			0xF30
 #define L2X0_DEBUG_CTRL			0xF40
-#define L2X0_PREFETCH_CTRL		0xF60
-#define L2X0_POWER_CTRL			0xF80
-#define   L2X0_DYNAMIC_CLK_GATING_EN	(1 << 1)
-#define   L2X0_STNDBY_MODE_EN		(1 << 0)
+#define L310_PREFETCH_CTRL		0xF60
+#define L310_POWER_CTRL			0xF80
+#define   L310_DYNAMIC_CLK_GATING_EN	(1 << 1)
+#define   L310_STNDBY_MODE_EN		(1 << 0)
 
 /* Registers shifts and masks */
 #define L2X0_CACHE_ID_PART_MASK		(0xf << 6)
@@ -88,29 +88,52 @@
 #define L310_CACHE_ID_RTL_R3P3		0x09
 
 #define L2X0_AUX_CTRL_MASK			0xc0000fff
+/* L2C auxiliary control register - bits common to L2C-210/220/310 */
+#define L2C_AUX_CTRL_WAY_SIZE_SHIFT		17
+#define L2C_AUX_CTRL_WAY_SIZE_MASK		(7 << 17)
+#define L2C_AUX_CTRL_WAY_SIZE(n)		((n) << 17)
+#define L2C_AUX_CTRL_EVTMON_ENABLE		BIT(20)
+#define L2C_AUX_CTRL_PARITY_ENABLE		BIT(21)
+#define L2C_AUX_CTRL_SHARED_OVERRIDE		BIT(22)
+/* L2C-210/220 common bits */
 #define L2X0_AUX_CTRL_DATA_RD_LATENCY_SHIFT	0
-#define L2X0_AUX_CTRL_DATA_RD_LATENCY_MASK	0x7
+#define L2X0_AUX_CTRL_DATA_RD_LATENCY_MASK	(7 << 0)
 #define L2X0_AUX_CTRL_DATA_WR_LATENCY_SHIFT	3
-#define L2X0_AUX_CTRL_DATA_WR_LATENCY_MASK	(0x7 << 3)
+#define L2X0_AUX_CTRL_DATA_WR_LATENCY_MASK	(7 << 3)
 #define L2X0_AUX_CTRL_TAG_LATENCY_SHIFT		6
-#define L2X0_AUX_CTRL_TAG_LATENCY_MASK		(0x7 << 6)
+#define L2X0_AUX_CTRL_TAG_LATENCY_MASK		(7 << 6)
 #define L2X0_AUX_CTRL_DIRTY_LATENCY_SHIFT	9
-#define L2X0_AUX_CTRL_DIRTY_LATENCY_MASK	(0x7 << 9)
-#define L2X0_AUX_CTRL_ASSOCIATIVITY_SHIFT	16
-#define L2X0_AUX_CTRL_WAY_SIZE_SHIFT		17
-#define L2X0_AUX_CTRL_WAY_SIZE_MASK		(0x7 << 17)
-#define L2X0_AUX_CTRL_SHARE_OVERRIDE_SHIFT	22
-#define L2X0_AUX_CTRL_NS_LOCKDOWN_SHIFT		26
-#define L2X0_AUX_CTRL_NS_INT_CTRL_SHIFT		27
-#define L2X0_AUX_CTRL_DATA_PREFETCH_SHIFT	28
-#define L2X0_AUX_CTRL_INSTR_PREFETCH_SHIFT	29
-#define L2X0_AUX_CTRL_EARLY_BRESP_SHIFT		30
+#define L2X0_AUX_CTRL_DIRTY_LATENCY_MASK	(7 << 9)
+#define L2X0_AUX_CTRL_ASSOC_SHIFT		13
+#define L2X0_AUX_CTRL_ASSOC_MASK		(15 << 13)
+/* L2C-210 specific bits */
+#define L210_AUX_CTRL_WRAP_DISABLE		BIT(12)
+#define L210_AUX_CTRL_WA_OVERRIDE		BIT(23)
+#define L210_AUX_CTRL_EXCLUSIVE_ABORT		BIT(24)
+/* L2C-220 specific bits */
+#define L220_AUX_CTRL_EXCLUSIVE_CACHE		BIT(12)
+#define L220_AUX_CTRL_FWA_SHIFT			23
+#define L220_AUX_CTRL_FWA_MASK			(3 << 23)
+#define L220_AUX_CTRL_NS_LOCKDOWN		BIT(26)
+#define L220_AUX_CTRL_NS_INT_CTRL		BIT(27)
+/* L2C-310 specific bits */
+#define L310_AUX_CTRL_FULL_LINE_ZERO		BIT(0)	/* R2P0+ */
+#define L310_AUX_CTRL_HIGHPRIO_SO_DEV		BIT(10)	/* R2P0+ */
+#define L310_AUX_CTRL_STORE_LIMITATION		BIT(11)	/* R2P0+ */
+#define L310_AUX_CTRL_EXCLUSIVE_CACHE		BIT(12)
+#define L310_AUX_CTRL_ASSOCIATIVITY_16		BIT(16)
+#define L310_AUX_CTRL_CACHE_REPLACE_RR		BIT(25)	/* R2P0+ */
+#define L310_AUX_CTRL_NS_LOCKDOWN		BIT(26)
+#define L310_AUX_CTRL_NS_INT_CTRL		BIT(27)
+#define L310_AUX_CTRL_DATA_PREFETCH		BIT(28)
+#define L310_AUX_CTRL_INSTR_PREFETCH		BIT(29)
+#define L310_AUX_CTRL_EARLY_BRESP		BIT(30)	/* R2P0+ */
 
-#define L2X0_LATENCY_CTRL_SETUP_SHIFT	0
-#define L2X0_LATENCY_CTRL_RD_SHIFT	4
-#define L2X0_LATENCY_CTRL_WR_SHIFT	8
+#define L310_LATENCY_CTRL_SETUP(n)		((n) << 0)
+#define L310_LATENCY_CTRL_RD(n)			((n) << 4)
+#define L310_LATENCY_CTRL_WR(n)			((n) << 8)
 
-#define L2X0_ADDR_FILTER_EN		1
+#define L310_ADDR_FILTER_EN		1
 
 #define L2X0_CTRL_EN			1
 
arch/arm/mach-cns3xxx/core.c +4 -4
index 2ae28a69e3e5..5c31b2638c01 100644
@@ -272,9 +272,9 @@ void __init cns3xxx_l2x0_init(void)
 	 *
 	 * 1 cycle of latency for setup, read and write accesses
 	 */
-	val = readl(base + L2X0_TAG_LATENCY_CTRL);
+	val = readl(base + L310_TAG_LATENCY_CTRL);
 	val &= 0xfffff888;
-	writel(val, base + L2X0_TAG_LATENCY_CTRL);
+	writel(val, base + L310_TAG_LATENCY_CTRL);
 
 	/*
 	 * Data RAM Control register
@@ -285,9 +285,9 @@ void __init cns3xxx_l2x0_init(void)
 	 *
 	 * 1 cycle of latency for setup, read and write accesses
 	 */
-	val = readl(base + L2X0_DATA_LATENCY_CTRL);
+	val = readl(base + L310_DATA_LATENCY_CTRL);
 	val &= 0xfffff888;
-	writel(val, base + L2X0_DATA_LATENCY_CTRL);
+	writel(val, base + L310_DATA_LATENCY_CTRL);
 
 	/* 32 KiB, 8-way, parity disable */
 	l2x0_init(base, 0x00540000, 0xfe000fff);
arch/arm/mach-exynos/sleep.S +4 -4
index a2613e944e10..7e0af530511e 100644
@@ -65,13 +65,13 @@ ENTRY(exynos_cpu_resume)
 	ldr	r2, [r0, #L2X0_R_AUX_CTRL]
 	str	r2, [r1, #L2X0_AUX_CTRL]
 	ldr	r2, [r0, #L2X0_R_TAG_LATENCY]
-	str	r2, [r1, #L2X0_TAG_LATENCY_CTRL]
+	str	r2, [r1, #L310_TAG_LATENCY_CTRL]
 	ldr	r2, [r0, #L2X0_R_DATA_LATENCY]
-	str	r2, [r1, #L2X0_DATA_LATENCY_CTRL]
+	str	r2, [r1, #L310_DATA_LATENCY_CTRL]
 	ldr	r2, [r0, #L2X0_R_PREFETCH_CTRL]
-	str	r2, [r1, #L2X0_PREFETCH_CTRL]
+	str	r2, [r1, #L310_PREFETCH_CTRL]
 	ldr	r2, [r0, #L2X0_R_PWR_CTRL]
-	str	r2, [r1, #L2X0_POWER_CTRL]
+	str	r2, [r1, #L310_POWER_CTRL]
 	mov	r2, #1
 	str	r2, [r1, #L2X0_CTRL]
 skip_l2_resume:
arch/arm/mach-imx/system.c +4 -4
index c6571f1de9fd..59013a81107b 100644
@@ -124,7 +124,7 @@ void __init imx_init_l2cache(void)
 	}
 
 	/* Configure the L2 PREFETCH and POWER registers */
-	val = readl_relaxed(l2x0_base + L2X0_PREFETCH_CTRL);
+	val = readl_relaxed(l2x0_base + L310_PREFETCH_CTRL);
 	val |= 0x70800000;
 	/*
 	 * The L2 cache controller(PL310) version on the i.MX6D/Q is r3p1-50rel0
@@ -137,9 +137,9 @@ void __init imx_init_l2cache(void)
 	 */
 	if (cpu_is_imx6q())
 		val &= ~(1 << 30 | 1 << 23);
-	writel_relaxed(val, l2x0_base + L2X0_PREFETCH_CTRL);
-	val = L2X0_DYNAMIC_CLK_GATING_EN | L2X0_STNDBY_MODE_EN;
-	writel_relaxed(val, l2x0_base + L2X0_POWER_CTRL);
+	writel_relaxed(val, l2x0_base + L310_PREFETCH_CTRL);
+	val = L310_DYNAMIC_CLK_GATING_EN | L310_STNDBY_MODE_EN;
+	writel_relaxed(val, l2x0_base + L310_POWER_CTRL);
 
 	iounmap(l2x0_base);
 	of_node_put(np);
arch/arm/mach-omap2/omap-mpuss-lowpower.c +1 -1
index 667915d236f3..ba43f49fbb59 100644
@@ -194,7 +194,7 @@ static void save_l2x0_context(void)
 	if (l2x0_base) {
 		val = __raw_readl(l2x0_base + L2X0_AUX_CTRL);
 		__raw_writel(val, sar_base + L2X0_AUXCTRL_OFFSET);
-		val = __raw_readl(l2x0_base + L2X0_PREFETCH_CTRL);
+		val = __raw_readl(l2x0_base + L310_PREFETCH_CTRL);
 		__raw_writel(val, sar_base + L2X0_PREFETCH_CTRL_OFFSET);
 	}
 }
arch/arm/mach-omap2/omap4-common.c +9 -9
index 46dac72aaa4c..dc9844a55443 100644
@@ -213,15 +213,15 @@ static int __init omap_l2_cache_init(void)
 		return -ENOMEM;
 
 	/* 16-way associativity, parity disabled, way size - 64KB (es2.0 +) */
-	aux_ctrl = (1 << L2X0_AUX_CTRL_ASSOCIATIVITY_SHIFT) |
-			(0x1 << 25) |
-			(0x1 << L2X0_AUX_CTRL_NS_LOCKDOWN_SHIFT) |
-			(0x1 << L2X0_AUX_CTRL_NS_INT_CTRL_SHIFT)) |
-			(0x3 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT) |
-			(1 << L2X0_AUX_CTRL_SHARE_OVERRIDE_SHIFT) |
-			(1 << L2X0_AUX_CTRL_DATA_PREFETCH_SHIFT) |
-			(1 << L2X0_AUX_CTRL_INSTR_PREFETCH_SHIFT) |
-			(1 << L2X0_AUX_CTRL_EARLY_BRESP_SHIFT);
+	aux_ctrl = L310_AUX_CTRL_ASSOCIATIVITY_16 |
+		   L310_AUX_CTRL_CACHE_REPLACE_RR |
+		   L310_AUX_CTRL_NS_LOCKDOWN |
+		   L310_AUX_CTRL_NS_INT_CTRL |
+		   L2C_AUX_CTRL_WAY_SIZE(3) |
+		   L2C_AUX_CTRL_SHARED_OVERRIDE |
+		   L310_AUX_CTRL_DATA_PREFETCH |
+		   L310_AUX_CTRL_INSTR_PREFETCH |
+		   L310_AUX_CTRL_EARLY_BRESP;
 
 	outer_cache.write_sec = omap4_l2c310_write_sec;
 	if (of_have_populated_dt())
arch/arm/mach-prima2/l2x0.c +2 -3
index c7102539c0b0..2db82742fb74 100644
@@ -17,13 +17,12 @@ struct l2x0_aux {
 };
 
 static const struct l2x0_aux prima2_l2x0_aux __initconst = {
-	.val = 2 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT,
+	.val = L2C_AUX_CTRL_WAY_SIZE(2),
 	.mask =	0,
 };
 
 static const struct l2x0_aux marco_l2x0_aux __initconst = {
-	.val = (2 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT) |
-		(1 << L2X0_AUX_CTRL_ASSOCIATIVITY_SHIFT),
+	.val = L2C_AUX_CTRL_WAY_SIZE(2) | L310_AUX_CTRL_ASSOCIATIVITY_16,
 	.mask = L2X0_AUX_CTRL_MASK,
 };
 
arch/arm/mach-realview/realview_pbx.c +2 -2
index 9d75493e3f0c..f0cfd7e7e569 100644
@@ -370,8 +370,8 @@ static void __init realview_pbx_init(void)
 			__io_address(REALVIEW_PBX_TILE_L220_BASE);
 
 		/* set RAM latencies to 1 cycle for eASIC */
-		writel(0, l2x0_base + L2X0_TAG_LATENCY_CTRL);
-		writel(0, l2x0_base + L2X0_DATA_LATENCY_CTRL);
+		writel(0, l2x0_base + L310_TAG_LATENCY_CTRL);
+		writel(0, l2x0_base + L310_DATA_LATENCY_CTRL);
 
 		/* 16KB way size, 8-way associativity, parity disabled
 		 * Bits:  .. 0 0 0 0 1 00 1 0 1 001 0 000 0 .... .... .... */
arch/arm/mach-spear/spear13xx.c +3 -3
index 7aa6e8cf830f..92860fa01668 100644
@@ -38,14 +38,14 @@ void __init spear13xx_l2x0_init(void)
 	if (!IS_ENABLED(CONFIG_CACHE_L2X0))
 		return;
 
-	writel_relaxed(0x06, VA_L2CC_BASE + L2X0_PREFETCH_CTRL);
+	writel_relaxed(0x06, VA_L2CC_BASE + L310_PREFETCH_CTRL);
 
 	/*
 	 * Program following latencies in order to make
 	 * SPEAr1340 work@600 MHz
 	 */
-	writel_relaxed(0x221, VA_L2CC_BASE + L2X0_TAG_LATENCY_CTRL);
-	writel_relaxed(0x441, VA_L2CC_BASE + L2X0_DATA_LATENCY_CTRL);
+	writel_relaxed(0x221, VA_L2CC_BASE + L310_TAG_LATENCY_CTRL);
+	writel_relaxed(0x441, VA_L2CC_BASE + L310_DATA_LATENCY_CTRL);
 	l2x0_init(VA_L2CC_BASE, 0x70A60001, 0xfe00ffff);
 }
 
arch/arm/mach-sti/board-dt.c +4 -4
index 1217fb598cfd..dc8669efc12d 100644
@@ -19,10 +19,10 @@ void __init stih41x_l2x0_init(void)
 	u32 way_size = 0x4;
 	u32 aux_ctrl;
 	/* may be this can be encoded in macros like BIT*() */
-	aux_ctrl = (0x1 << L2X0_AUX_CTRL_SHARE_OVERRIDE_SHIFT) |
-		(0x1 << L2X0_AUX_CTRL_DATA_PREFETCH_SHIFT) |
-		(0x1 << L2X0_AUX_CTRL_INSTR_PREFETCH_SHIFT) |
-		(way_size << L2X0_AUX_CTRL_WAY_SIZE_SHIFT);
+	aux_ctrl = L2C_AUX_CTRL_SHARED_OVERRIDE |
+		   L310_AUX_CTRL_DATA_PREFETCH |
+		   L310_AUX_CTRL_INSTR_PREFETCH |
+		   L2C_AUX_CTRL_WAY_SIZE(way_size);
 
 	l2x0_of_init(aux_ctrl, L2X0_AUX_CTRL_MASK);
 }
arch/arm/mach-tegra/sleep.h +4 -4
index a4edbb3abd3d..a032820d2fac 100644
@@ -134,13 +134,13 @@
 	tst	\tmp3, #L2X0_CTRL_EN
 	bne	exit_l2_resume
 	ldr	\tmp3, [\tmp1, #L2X0_R_TAG_LATENCY]
-	str	\tmp3, [\tmp2, #L2X0_TAG_LATENCY_CTRL]
+	str	\tmp3, [\tmp2, #L310_TAG_LATENCY_CTRL]
 	ldr	\tmp3, [\tmp1, #L2X0_R_DATA_LATENCY]
-	str	\tmp3, [\tmp2, #L2X0_DATA_LATENCY_CTRL]
+	str	\tmp3, [\tmp2, #L310_DATA_LATENCY_CTRL]
 	ldr	\tmp3, [\tmp1, #L2X0_R_PREFETCH_CTRL]
-	str	\tmp3, [\tmp2, #L2X0_PREFETCH_CTRL]
+	str	\tmp3, [\tmp2, #L310_PREFETCH_CTRL]
 	ldr	\tmp3, [\tmp1, #L2X0_R_PWR_CTRL]
-	str	\tmp3, [\tmp2, #L2X0_POWER_CTRL]
+	str	\tmp3, [\tmp2, #L310_POWER_CTRL]
 	ldr	\tmp3, [\tmp1, #L2X0_R_AUX_CTRL]
 	str	\tmp3, [\tmp2, #L2X0_AUX_CTRL]
 	mov	\tmp3, #L2X0_CTRL_EN
arch/arm/mach-ux500/cache-l2x0.c +2 -2
index 5cc7e3625d8c..067c37a054fb 100644
@@ -59,10 +59,10 @@ static int __init ux500_l2x0_init(void)
 	/* DBx540's L2 has 128KB way size */
 	if (cpu_is_ux540_family())
 		/* 128KB way size */
-		aux_val |= (0x4 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT);
+		aux_val |= L2C_AUX_CTRL_WAY_SIZE(4);
 	else
 		/* 64KB way size */
-		aux_val |= (0x3 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT);
+		aux_val |= L2C_AUX_CTRL_WAY_SIZE(3);
 
 	outer_cache.write_sec = ux500_l2c310_write_sec;
 
arch/arm/mach-vexpress/ct-ca9x4.c +2 -2
index 6f34497a4245..6c4ffb6c5ad8 100644
@@ -145,8 +145,8 @@ static void __init ct_ca9x4_init(void)
 	void __iomem *l2x0_base = ioremap(CT_CA9X4_L2CC, SZ_4K);
 
 	/* set RAM latencies to 1 cycle for this core tile. */
-	writel(0, l2x0_base + L2X0_TAG_LATENCY_CTRL);
-	writel(0, l2x0_base + L2X0_DATA_LATENCY_CTRL);
+	writel(0, l2x0_base + L310_TAG_LATENCY_CTRL);
+	writel(0, l2x0_base + L310_DATA_LATENCY_CTRL);
 
 	l2x0_init(l2x0_base, 0x00400000, 0xfe0fffff);
 #endif
arch/arm/mm/cache-l2x0.c +29 -28

136 lines not on this page · raw message

Signature · 1 line
1.8.3.1
#50 lore raw

PATCH 50/222 ARM: l2c: add automatic enable of early BRESP

In reply to Russell King - ARM Linux

The AXI bus protocol requires that a write response should only be sent back to the master when the last write has been accepted. Early BRESP allows the L2C-310 to send the write response as soon as the store buffer accepts the write address.

Cortex-A9 processors can signal to the L2C-310 that they wish to be notified early, and if this optimisation is enabled, the L2C-310 can signal an early write response.

---
 arch/arm/mm/cache-l2x0.c | 25 ++++++++++++++++++++++---
 1 file changed, 22 insertions(+), 3 deletions(-)
arch/arm/mm/cache-l2x0.c +22 -3
index 3e2c22a12d87..db3f18a968b2 100644
@@ -24,6 +24,7 @@
 #include <linux/of_address.h>
 
 #include <asm/cacheflush.h>
+#include <asm/cputype.h>
 #include <asm/hardware/cache-l2x0.h>
 #include "cache-tauros3.h"
 #include "cache-aurora-l2.h"
@@ -620,6 +621,24 @@ static void l2c310_resume(void)
 	}
 }
 
+static void __init l2c310_enable(void __iomem *base, u32 aux, unsigned num_lock)
+{
+	unsigned rev = readl_relaxed(base + L2X0_CACHE_ID) & L2X0_CACHE_ID_PART_MASK;
+	bool cortex_a9 = read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A9;
+
+	if (rev >= L310_CACHE_ID_RTL_R2P0) {
+		if (cortex_a9) {
+			aux |= L310_AUX_CTRL_EARLY_BRESP;
+			pr_info("L2C-310 enabling early BRESP for Cortex-A9\n");
+		} else if (aux & L310_AUX_CTRL_EARLY_BRESP) {
+			pr_warn("L2C-310 early BRESP only supported with Cortex-A9\n");
+			aux &= ~L310_AUX_CTRL_EARLY_BRESP;
+		}
+	}
+
+	l2c_enable(base, aux, num_lock);
+}
+
 static void __init l2c310_fixup(void __iomem *base, u32 cache_id,
 	struct outer_cache_fns *fns)
 {
@@ -677,7 +696,7 @@ static const struct l2c_init_data l2c310_init_fns __initconst = {
 	.type = "L2C-310",
 	.way_size_0 = SZ_8K,
 	.num_lock = 8,
-	.enable = l2c_enable,
+	.enable = l2c310_enable,
 	.fixup = l2c310_fixup,
 	.save = l2c310_save,
 	.outer_cache = {
@@ -916,7 +935,7 @@ static const struct l2c_init_data of_l2c310_data __initconst = {
 	.way_size_0 = SZ_8K,
 	.num_lock = 8,
 	.of_parse = l2c310_of_parse,
-	.enable = l2c_enable,
+	.enable = l2c310_enable,
 	.fixup = l2c310_fixup,
 	.save  = l2c310_save,
 	.outer_cache = {
@@ -1264,7 +1283,7 @@ static const struct l2c_init_data of_bcm_l2x0_data __initconst = {
 	.way_size_0 = SZ_8K,
 	.num_lock = 8,
 	.of_parse = l2c310_of_parse,
-	.enable = l2c_enable,
+	.enable = l2c310_enable,
 	.save  = l2c310_save,
 	.outer_cache = {
 		.inv_range   = bcm_inv_range,
Signature · 1 line
1.8.3.1
#51 lore raw

PATCH 51/222 ARM: l2c: remove platforms/SoCs setting early BRESP

In reply to Russell King - ARM Linux

Since we now automatically enable early BRESP in core L2C-310 code when we detect a Cortex-A9, we don't need platforms/SoCs to set this bit explicitly. Instead, they should seek to preserve the value of bit 30 in the auxiliary control register.

---
 arch/arm/mach-berlin/berlin.c                            | 2 +-
 arch/arm/mach-exynos/exynos.c                            | 4 ++--
 arch/arm/mach-omap2/omap4-common.c                       | 3 +--
 arch/arm/mach-shmobile/board-armadillo800eva-reference.c | 4 ++--
 arch/arm/mach-shmobile/board-armadillo800eva.c           | 4 ++--
 arch/arm/mach-shmobile/board-kzm9g-reference.c           | 4 ++--
 arch/arm/mach-shmobile/board-kzm9g.c                     | 4 ++--
 arch/arm/mach-shmobile/setup-r8a7778.c                   | 4 ++--
 arch/arm/mach-shmobile/setup-r8a7779.c                   | 4 ++--
 arch/arm/mach-spear/spear13xx.c                          | 2 +-
 arch/arm/mach-tegra/tegra.c                              | 4 ++--
 11 files changed, 19 insertions(+), 20 deletions(-)
arch/arm/mach-berlin/berlin.c +1 -1
index 025bcb5473eb..6709d2a6bec8 100644
@@ -24,7 +24,7 @@ static void __init berlin_init_machine(void)
 	 * with DT probing for L2CCs, berlin_init_machine can be removed.
 	 * Note: 88DE3005 (Armada 1500-mini) uses pl310 l2cc
 	 */
-	l2x0_of_init(0x70c00000, 0xfeffffff);
+	l2x0_of_init(0x30c00000, 0xfeffffff);
 	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
 }
 
arch/arm/mach-exynos/exynos.c +2 -2
index b32a907d021d..e6828fb46034 100644
@@ -32,8 +32,8 @@
 #include "mfc.h"
 #include "regs-pmu.h"
 
-#define L2_AUX_VAL 0x7C470001
-#define L2_AUX_MASK 0xC200ffff
+#define L2_AUX_VAL 0x3c470001
+#define L2_AUX_MASK 0xc200ffff
 
 static struct map_desc exynos4_iodesc[] __initdata = {
 	{
arch/arm/mach-omap2/omap4-common.c +1 -2
index dc9844a55443..9ce52548a484 100644
@@ -220,8 +220,7 @@ static int __init omap_l2_cache_init(void)
 		   L2C_AUX_CTRL_WAY_SIZE(3) |
 		   L2C_AUX_CTRL_SHARED_OVERRIDE |
 		   L310_AUX_CTRL_DATA_PREFETCH |
-		   L310_AUX_CTRL_INSTR_PREFETCH |
-		   L310_AUX_CTRL_EARLY_BRESP;
+		   L310_AUX_CTRL_INSTR_PREFETCH;
 
 	outer_cache.write_sec = omap4_l2c310_write_sec;
 	if (of_have_populated_dt())
arch/arm/mach-shmobile/board-armadillo800eva-reference.c +2 -2
index 57d1a78367b6..34e7f3c17dd2 100644
@@ -164,8 +164,8 @@ static void __init eva_init(void)
 	r8a7740_meram_workaround();
 
 #ifdef CONFIG_CACHE_L2X0
-	/* Early BRESP enable, Shared attribute override enable, 32K*8way */
-	l2x0_init(IOMEM(0xf0002000), 0x40440000, 0x82000fff);
+	/* Shared attribute override enable, 32K*8way */
+	l2x0_init(IOMEM(0xf0002000), 0x00440000, 0xc2000fff);
 #endif
 
 	r8a7740_add_standard_devices_dt();
arch/arm/mach-shmobile/board-armadillo800eva.c +2 -2
index 2858f380beae..7688990edd3a 100644
@@ -1270,8 +1270,8 @@ static void __init eva_init(void)
 
 
 #ifdef CONFIG_CACHE_L2X0
-	/* Early BRESP enable, Shared attribute override enable, 32K*8way */
-	l2x0_init(IOMEM(0xf0002000), 0x40440000, 0x82000fff);
+	/* Shared attribute override enable, 32K*8way */
+	l2x0_init(IOMEM(0xf0002000), 0x00440000, 0xc2000fff);
 #endif
 
 	i2c_register_board_info(0, i2c0_devices, ARRAY_SIZE(i2c0_devices));
arch/arm/mach-shmobile/board-kzm9g-reference.c +2 -2
index 598e32488410..85873f186d77 100644
@@ -36,8 +36,8 @@ static void __init kzm_init(void)
 	sh73a0_add_standard_devices_dt();
 
 #ifdef CONFIG_CACHE_L2X0
-	/* Early BRESP enable, Shared attribute override enable, 64K*8way */
-	l2x0_init(IOMEM(0xf0100000), 0x40460000, 0x82000fff);
+	/* Shared attribute override enable, 64K*8way */
+	l2x0_init(IOMEM(0xf0100000), 0x00460000, 0xc2000fff);
 #endif
 }
 
arch/arm/mach-shmobile/board-kzm9g.c +2 -2
index 03dc3ac84502..ea9bf39fdc10 100644
@@ -876,8 +876,8 @@ static void __init kzm_init(void)
 	gpio_request_one(223, GPIOF_IN, NULL); /* IRQ8 */
 
 #ifdef CONFIG_CACHE_L2X0
-	/* Early BRESP enable, Shared attribute override enable, 64K*8way */
-	l2x0_init(IOMEM(0xf0100000), 0x40460000, 0x82000fff);
+	/* Shared attribute override enable, 64K*8way */
+	l2x0_init(IOMEM(0xf0100000), 0x00460000, 0xc2000fff);
 #endif
 
 	i2c_register_board_info(0, i2c0_devices, ARRAY_SIZE(i2c0_devices));
arch/arm/mach-shmobile/setup-r8a7778.c +2 -2
index 6d694526e4ca..3a8e5316671e 100644
@@ -298,10 +298,10 @@ void __init r8a7778_add_dt_devices(void)
 	void __iomem *base = ioremap_nocache(0xf0100000, 0x1000);
 	if (base) {
 		/*
-		 * Early BRESP enable, Shared attribute override enable, 64K*16way
+		 * Shared attribute override enable, 64K*16way
 		 * don't call iounmap(base)
 		 */
-		l2x0_init(base, 0x40470000, 0x82000fff);
+		l2x0_init(base, 0x00470000, 0xc2000fff);
 	}
 #endif
 
arch/arm/mach-shmobile/setup-r8a7779.c +2 -2
index 8e860b36997a..91c90bf0ae83 100644
@@ -700,8 +700,8 @@ static struct platform_device *r8a7779_standard_devices[] __initdata = {
 void __init r8a7779_add_standard_devices(void)
 {
 #ifdef CONFIG_CACHE_L2X0
-	/* Early BRESP enable, Shared attribute override enable, 64K*16way */
-	l2x0_init(IOMEM(0xf0100000), 0x40470000, 0x82000fff);
+	/* Shared attribute override enable, 64K*16way */
+	l2x0_init(IOMEM(0xf0100000), 0x00470000, 0xc2000fff);
 #endif
 	r8a7779_pm_init();
 
arch/arm/mach-spear/spear13xx.c +1 -1
index 92860fa01668..dcb300443b66 100644
@@ -46,7 +46,7 @@ void __init spear13xx_l2x0_init(void)
 	 */
 	writel_relaxed(0x221, VA_L2CC_BASE + L310_TAG_LATENCY_CTRL);
 	writel_relaxed(0x441, VA_L2CC_BASE + L310_DATA_LATENCY_CTRL);
-	l2x0_init(VA_L2CC_BASE, 0x70A60001, 0xfe00ffff);
+	l2x0_init(VA_L2CC_BASE, 0x30a60001, 0xfe00ffff);
 }
 
 /*
arch/arm/mach-tegra/tegra.c +2 -2
index 6191603379e1..ecbb5411a104 100644
@@ -89,9 +89,9 @@ static void __init tegra_init_cache(void)
 
 	cache_type = readl(p + L2X0_CACHE_TYPE);
 	aux_ctrl = (cache_type & 0x700) << (17-8);
-	aux_ctrl |= 0x7C400001;
+	aux_ctrl |= 0x3c400001;
 
-	ret = l2x0_of_init(aux_ctrl, 0x8200c3fe);
+	ret = l2x0_of_init(aux_ctrl, 0xc200c3fe);
 	if (!ret)
 		l2x0_saved_regs_addr = virt_to_phys(&l2x0_saved_regs);
 #endif
Signature · 1 line
1.8.3.1
#52 lore raw

PATCH 52/222 ARM: l2c: tegra: remove associativity and way size from aux_ctrl

In reply to Russell King - ARM Linux
---
 arch/arm/mach-tegra/tegra.c | 18 +-----------------
 1 file changed, 1 insertion(+), 17 deletions(-)
arch/arm/mach-tegra/tegra.c +1 -17
index ecbb5411a104..fb802e24b647 100644
@@ -73,25 +73,9 @@ u32 tegra_uart_config[3] = {
 static void __init tegra_init_cache(void)
 {
 #ifdef CONFIG_CACHE_L2X0
-	static const struct of_device_id pl310_ids[] __initconst = {
-		{ .compatible = "arm,pl310-cache",  },
-		{}
-	};
-
-	struct device_node *np;
 	int ret;
-	void __iomem *p = IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x3000;
-	u32 aux_ctrl, cache_type;
-
-	np = of_find_matching_node(NULL, pl310_ids);
-	if (!np)
-		return;
-
-	cache_type = readl(p + L2X0_CACHE_TYPE);
-	aux_ctrl = (cache_type & 0x700) << (17-8);
-	aux_ctrl |= 0x3c400001;
 
-	ret = l2x0_of_init(aux_ctrl, 0xc200c3fe);
+	ret = l2x0_of_init(0x3c400001, 0xc20fc3fe);
 	if (!ret)
 		l2x0_saved_regs_addr = virt_to_phys(&l2x0_saved_regs);
 #endif
Signature · 1 line
1.8.3.1
#53 lore raw

PATCH 53/222 ARM: l2c: ux500: remove associativity and way size from aux_ctrl

In reply to Russell King - ARM Linux
---
 arch/arm/mach-ux500/cache-l2x0.c | 15 ++-------------
 1 file changed, 2 insertions(+), 13 deletions(-)
arch/arm/mach-ux500/cache-l2x0.c +2 -13
index 067c37a054fb..5b891d051054 100644
@@ -45,8 +45,6 @@ static void ux500_l2c310_write_sec(unsigned long val, unsigned reg)
 
 static int __init ux500_l2x0_init(void)
 {
-	u32 aux_val = 0x3e000000;
-
 	if (cpu_is_u8500_family() || cpu_is_ux540_family())
 		l2x0_base = __io_address(U8500_L2CC_BASE);
 	else
@@ -56,21 +54,12 @@ static int __init ux500_l2x0_init(void)
 	/* Unlock before init */
 	ux500_l2x0_unlock();
 
-	/* DBx540's L2 has 128KB way size */
-	if (cpu_is_ux540_family())
-		/* 128KB way size */
-		aux_val |= L2C_AUX_CTRL_WAY_SIZE(4);
-	else
-		/* 64KB way size */
-		aux_val |= L2C_AUX_CTRL_WAY_SIZE(3);
-
 	outer_cache.write_sec = ux500_l2c310_write_sec;
 
-	/* 64KB way size, 8 way associativity, force WA */
 	if (of_have_populated_dt())
-		l2x0_of_init(aux_val, 0xc0000fff);
+		l2x0_of_init(0x3e000000, 0xc00f0fff);
 	else
-		l2x0_init(l2x0_base, aux_val, 0xc0000fff);
+		l2x0_init(l2x0_base, 0x3e000000, 0xc00f0fff);
 
 	return 0;
 }
Signature · 1 line
1.8.3.1
#54 lore raw

PATCH 54/222 ARM: l2c: ux500: don't try to change the L2 cache auxiliary control register

In reply to Russell King - ARM Linux

ux500 can't change the auxiliary control register, so there's no point passing values to try and modify it to the l2x0 init functions.

---
 arch/arm/mach-ux500/cache-l2x0.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
arch/arm/mach-ux500/cache-l2x0.c +2 -2
index 5b891d051054..842ebedbdd1c 100644
@@ -57,9 +57,9 @@ static int __init ux500_l2x0_init(void)
 	outer_cache.write_sec = ux500_l2c310_write_sec;
 
 	if (of_have_populated_dt())
-		l2x0_of_init(0x3e000000, 0xc00f0fff);
+		l2x0_of_init(0, ~0);
 	else
-		l2x0_init(l2x0_base, 0x3e000000, 0xc00f0fff);
+		l2x0_init(l2x0_base, 0, ~0);
 
 	return 0;
 }
Signature · 1 line
1.8.3.1
#55 lore raw

PATCH 55/222 ARM: l2c: cns3xxx: remove cache size override

In reply to Russell King - ARM Linux
---
 arch/arm/mach-cns3xxx/core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm/mach-cns3xxx/core.c +1 -1
index 5c31b2638c01..f85449a6accd 100644
@@ -290,7 +290,7 @@ void __init cns3xxx_l2x0_init(void)
 	writel(val, base + L310_DATA_LATENCY_CTRL);
 
 	/* 32 KiB, 8-way, parity disable */
-	l2x0_init(base, 0x00540000, 0xfe000fff);
+	l2x0_init(base, 0x00500000, 0xfe0f0fff);
 }
 
 #endif /* CONFIG_CACHE_L2X0 */
Signature · 1 line
1.8.3.1
#56 lore raw

PATCH 56/222 ARM: l2c: exynos: remove cache size override

In reply to Russell King - ARM Linux
---
 arch/arm/mach-exynos/exynos.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)
arch/arm/mach-exynos/exynos.c +1 -4
index e6828fb46034..a51bf25e7523 100644
@@ -32,9 +32,6 @@
 #include "mfc.h"
 #include "regs-pmu.h"
 
-#define L2_AUX_VAL 0x3c470001
-#define L2_AUX_MASK 0xc200ffff
-
 static struct map_desc exynos4_iodesc[] __initdata = {
 	{
 		.virtual	= (unsigned long)S3C_VA_SYS,
@@ -323,7 +320,7 @@ static int __init exynos4_l2x0_cache_init(void)
 {
 	int ret;
 
-	ret = l2x0_of_init(L2_AUX_VAL, L2_AUX_MASK);
+	ret = l2x0_of_init(0x3c400001, 0xc20fffff);
 	if (ret)
 		return ret;
 
Signature · 1 line
1.8.3.1
#57 lore raw

PATCH 57/222 ARM: l2c: nomadik: remove cache size override

In reply to Russell King - ARM Linux
---
 arch/arm/mach-nomadik/cpu-8815.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm/mach-nomadik/cpu-8815.c +1 -1
index 4a1065e41e9c..0f245bcc6b7e 100644
@@ -147,7 +147,7 @@ static void __init cpu8815_init_of(void)
 {
 #ifdef CONFIG_CACHE_L2X0
 	/* At full speed latency must be >=2, so 0x249 in low bits */
-	l2x0_of_init(0x00730249, 0xfe000fff);
+	l2x0_of_init(0x00700249, 0xfe0fefff);
 #endif
 	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
 }
Signature · 1 line
1.8.3.1
#58 lore raw

PATCH 58/222 ARM: l2c: omap2: remove cache size override

In reply to Russell King - ARM Linux
---
 arch/arm/mach-omap2/omap4-common.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)
arch/arm/mach-omap2/omap4-common.c +3 -5
index 9ce52548a484..06c6a181d6ad 100644
@@ -213,20 +213,18 @@ static int __init omap_l2_cache_init(void)
 		return -ENOMEM;
 
 	/* 16-way associativity, parity disabled, way size - 64KB (es2.0 +) */
-	aux_ctrl = L310_AUX_CTRL_ASSOCIATIVITY_16 |
-		   L310_AUX_CTRL_CACHE_REPLACE_RR |
+	aux_ctrl = L310_AUX_CTRL_CACHE_REPLACE_RR |
 		   L310_AUX_CTRL_NS_LOCKDOWN |
 		   L310_AUX_CTRL_NS_INT_CTRL |
-		   L2C_AUX_CTRL_WAY_SIZE(3) |
 		   L2C_AUX_CTRL_SHARED_OVERRIDE |
 		   L310_AUX_CTRL_DATA_PREFETCH |
 		   L310_AUX_CTRL_INSTR_PREFETCH;
 
 	outer_cache.write_sec = omap4_l2c310_write_sec;
 	if (of_have_populated_dt())
-		l2x0_of_init(aux_ctrl, L2X0_AUX_CTRL_MASK);
+		l2x0_of_init(aux_ctrl, 0xc19fffff);
 	else
-		l2x0_init(l2cache_base, aux_ctrl, L2X0_AUX_CTRL_MASK);
+		l2x0_init(l2cache_base, aux_ctrl, 0xc19fffff);
 
 	return 0;
 }
Signature · 1 line
1.8.3.1
#59 lore raw

PATCH 59/222 ARM: l2c: prima2: remove cache size override

In reply to Russell King - ARM Linux
---
 arch/arm/boot/dts/marco.dtsi  |  2 +-
 arch/arm/boot/dts/prima2.dtsi |  2 +-
 arch/arm/mach-prima2/l2x0.c   | 33 +--------------------------------
 3 files changed, 3 insertions(+), 34 deletions(-)
arch/arm/boot/dts/marco.dtsi +1 -1
index 0c9647d28765..fb354225740a 100644
@@ -36,7 +36,7 @@
 		ranges = <0x40000000 0x40000000 0xa0000000>;
 
 		l2-cache-controller at c0030000 {
-			compatible = "sirf,marco-pl310-cache", "arm,pl310-cache";
+			compatible = "arm,pl310-cache";
 			reg = <0xc0030000 0x1000>;
 			interrupts = <0 59 0>;
 			arm,tag-latency = <1 1 1>;
arch/arm/boot/dts/prima2.dtsi +1 -1
index 1e82571d6823..0d6588d549bf 100644
@@ -48,7 +48,7 @@
 		ranges = <0x40000000 0x40000000 0x80000000>;
 
 		l2-cache-controller at 80040000 {
-			compatible = "arm,pl310-cache", "sirf,prima2-pl310-cache";
+			compatible = "arm,pl310-cache";
 			reg = <0x80040000 0x1000>;
 			interrupts = <59>;
 			arm,tag-latency = <1 1 1>;
arch/arm/mach-prima2/l2x0.c +1 -32
index 2db82742fb74..09f68f046830 100644
@@ -8,41 +8,10 @@
 
 #include <linux/init.h>
 #include <linux/kernel.h>
-#include <linux/of.h>
 #include <asm/hardware/cache-l2x0.h>
 
-struct l2x0_aux {
-	u32 val;
-	u32 mask;
-};
-
-static const struct l2x0_aux prima2_l2x0_aux __initconst = {
-	.val = L2C_AUX_CTRL_WAY_SIZE(2),
-	.mask =	0,
-};
-
-static const struct l2x0_aux marco_l2x0_aux __initconst = {
-	.val = L2C_AUX_CTRL_WAY_SIZE(2) | L310_AUX_CTRL_ASSOCIATIVITY_16,
-	.mask = L2X0_AUX_CTRL_MASK,
-};
-
-static const struct of_device_id sirf_l2x0_ids[] __initconst = {
-	{ .compatible = "sirf,prima2-pl310-cache", .data = &prima2_l2x0_aux, },
-	{ .compatible = "sirf,marco-pl310-cache", .data = &marco_l2x0_aux, },
-	{},
-};
-
 static int __init sirfsoc_l2x0_init(void)
 {
-	struct device_node *np;
-	const struct l2x0_aux *aux;
-
-	np = of_find_matching_node(NULL, sirf_l2x0_ids);
-	if (np) {
-		aux = of_match_node(sirf_l2x0_ids, np)->data;
-		return l2x0_of_init(aux->val, aux->mask);
-	}
-
-	return 0;
+	return l2x0_of_init(0, ~0);
 }
 early_initcall(sirfsoc_l2x0_init);
Signature · 1 line
1.8.3.1
#60 lore raw

PATCH 60/222 ARM: l2c: shmobile: remove cache size override

In reply to Russell King - ARM Linux
---
 arch/arm/mach-shmobile/board-armadillo800eva-reference.c | 2 +-
 arch/arm/mach-shmobile/board-armadillo800eva.c           | 2 +-
 arch/arm/mach-shmobile/board-kzm9g-reference.c           | 2 +-
 arch/arm/mach-shmobile/board-kzm9g.c                     | 2 +-
 arch/arm/mach-shmobile/setup-r8a7778.c                   | 2 +-
 arch/arm/mach-shmobile/setup-r8a7779.c                   | 2 +-
 6 files changed, 6 insertions(+), 6 deletions(-)
arch/arm/mach-shmobile/board-armadillo800eva-reference.c +1 -1
index 34e7f3c17dd2..39e11f48e8bc 100644
@@ -165,7 +165,7 @@ static void __init eva_init(void)
 
 #ifdef CONFIG_CACHE_L2X0
 	/* Shared attribute override enable, 32K*8way */
-	l2x0_init(IOMEM(0xf0002000), 0x00440000, 0xc2000fff);
+	l2x0_init(IOMEM(0xf0002000), 0x00400000, 0xc20f0fff);
 #endif
 
 	r8a7740_add_standard_devices_dt();
arch/arm/mach-shmobile/board-armadillo800eva.c +1 -1
index 7688990edd3a..b0b9435a71ff 100644
@@ -1271,7 +1271,7 @@ static void __init eva_init(void)
 
 #ifdef CONFIG_CACHE_L2X0
 	/* Shared attribute override enable, 32K*8way */
-	l2x0_init(IOMEM(0xf0002000), 0x00440000, 0xc2000fff);
+	l2x0_init(IOMEM(0xf0002000), 0x00400000, 0xc20f0fff);
 #endif
 
 	i2c_register_board_info(0, i2c0_devices, ARRAY_SIZE(i2c0_devices));
arch/arm/mach-shmobile/board-kzm9g-reference.c +1 -1
index 85873f186d77..a735a1d80c28 100644
@@ -37,7 +37,7 @@ static void __init kzm_init(void)
 
 #ifdef CONFIG_CACHE_L2X0
 	/* Shared attribute override enable, 64K*8way */
-	l2x0_init(IOMEM(0xf0100000), 0x00460000, 0xc2000fff);
+	l2x0_init(IOMEM(0xf0100000), 0x00400000, 0xc20f0fff);
 #endif
 }
 
arch/arm/mach-shmobile/board-kzm9g.c +1 -1
index ea9bf39fdc10..f94ec8ca42c1 100644
@@ -877,7 +877,7 @@ static void __init kzm_init(void)
 
 #ifdef CONFIG_CACHE_L2X0
 	/* Shared attribute override enable, 64K*8way */
-	l2x0_init(IOMEM(0xf0100000), 0x00460000, 0xc2000fff);
+	l2x0_init(IOMEM(0xf0100000), 0x00400000, 0xc20f0fff);
 #endif
 
 	i2c_register_board_info(0, i2c0_devices, ARRAY_SIZE(i2c0_devices));
arch/arm/mach-shmobile/setup-r8a7778.c +1 -1
index 3a8e5316671e..6dd7ddf88741 100644
@@ -301,7 +301,7 @@ void __init r8a7778_add_dt_devices(void)
 		 * Shared attribute override enable, 64K*16way
 		 * don't call iounmap(base)
 		 */
-		l2x0_init(base, 0x00470000, 0xc2000fff);
+		l2x0_init(base, 0x00400000, 0xc20f0fff);
 	}
 #endif
 
arch/arm/mach-shmobile/setup-r8a7779.c +1 -1
index 91c90bf0ae83..a6630fccfc45 100644
@@ -701,7 +701,7 @@ void __init r8a7779_add_standard_devices(void)
 {
 #ifdef CONFIG_CACHE_L2X0
 	/* Shared attribute override enable, 64K*16way */
-	l2x0_init(IOMEM(0xf0100000), 0x00470000, 0xc2000fff);
+	l2x0_init(IOMEM(0xf0100000), 0x00400000, 0xc20f0fff);
 #endif
 	r8a7779_pm_init();
 
Signature · 1 line
1.8.3.1
#61 lore raw

PATCH 61/222 ARM: l2c: spear13xx: remove cache size override

In reply to Russell King - ARM Linux
---
 arch/arm/mach-spear/spear13xx.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm/mach-spear/spear13xx.c +1 -1
index dcb300443b66..c9897ea38980 100644
@@ -46,7 +46,7 @@ void __init spear13xx_l2x0_init(void)
 	 */
 	writel_relaxed(0x221, VA_L2CC_BASE + L310_TAG_LATENCY_CTRL);
 	writel_relaxed(0x441, VA_L2CC_BASE + L310_DATA_LATENCY_CTRL);
-	l2x0_init(VA_L2CC_BASE, 0x30a60001, 0xfe00ffff);
+	l2x0_init(VA_L2CC_BASE, 0x30a00001, 0xfe0fffff);
 }
 
 /*
Signature · 1 line
1.8.3.1
#62 lore raw

PATCH 62/222 ARM: l2c: sti: remove cache size override

In reply to Russell King - ARM Linux
---
 arch/arm/mach-sti/board-dt.c | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)
arch/arm/mach-sti/board-dt.c +3 -9
index dc8669efc12d..cf716ae10726 100644
@@ -16,15 +16,9 @@
 
 void __init stih41x_l2x0_init(void)
 {
-	u32 way_size = 0x4;
-	u32 aux_ctrl;
-	/* may be this can be encoded in macros like BIT*() */
-	aux_ctrl = L2C_AUX_CTRL_SHARED_OVERRIDE |
-		   L310_AUX_CTRL_DATA_PREFETCH |
-		   L310_AUX_CTRL_INSTR_PREFETCH |
-		   L2C_AUX_CTRL_WAY_SIZE(way_size);
-
-	l2x0_of_init(aux_ctrl, L2X0_AUX_CTRL_MASK);
+	l2x0_of_init(L2C_AUX_CTRL_SHARED_OVERRIDE |
+		     L310_AUX_CTRL_DATA_PREFETCH |
+		     L310_AUX_CTRL_INSTR_PREFETCH, 0xc00f0fff);
 }
 
 static void __init stih41x_machine_init(void)
Signature · 1 line
1.8.3.1
#63 lore raw

PATCH 63/222 ARM: l2c: zynq: remove cache size override

In reply to Russell King - ARM Linux
---
 arch/arm/mach-zynq/common.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm/mach-zynq/common.c +1 -1
index 6fcc584c1a11..1e617a6dedc3 100644
@@ -70,7 +70,7 @@ static void __init zynq_init_machine(void)
 	/*
 	 * 64KB way size, 8-way associativity, parity disabled
 	 */
-	l2x0_of_init(0x02060000, 0xF0F0FFFF);
+	l2x0_of_init(0x02000000, 0xf0ffffff);
 
 	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
 
Signature · 1 line
1.8.3.1
#64 lore raw

PATCH 64/222 ARM: l2c: realview: improve commentry about the L2 cache requirements

In reply to Russell King - ARM Linux

Add better commentry about the L2 cache requirements on these platforms. Unfortunately, the auxiliary control register is not pre-set to indicate the correct cache parameters, so we have to manually program these.

---
 arch/arm/mach-realview/realview_eb.c     | 9 +++++++--
 arch/arm/mach-realview/realview_pb1176.c | 8 +++++++-
 arch/arm/mach-realview/realview_pb11mp.c | 9 +++++++--
 3 files changed, 21 insertions(+), 5 deletions(-)
arch/arm/mach-realview/realview_eb.c +7 -2
index c85ddb2a0ad0..b575895037b8 100644
@@ -442,8 +442,13 @@ static void __init realview_eb_init(void)
 		realview_eb11mp_fixup();
 
 #ifdef CONFIG_CACHE_L2X0
-		/* 1MB (128KB/way), 8-way associativity, evmon/parity/share enabled
-		 * Bits:  .... ...0 0111 1001 0000 .... .... .... */
+		/*
+		 * The PL220 needs to be manually configured as the hardware
+		 * doesn't report the correct sizes.
+		 * 1MB (128KB/way), 8-way associativity, event monitor and
+		 * parity enabled, ignore share bit, no force write allocate
+		 * Bits:  .... ...0 0111 1001 0000 .... .... ....
+		 */
 		l2x0_init(__io_address(REALVIEW_EB11MP_L220_BASE), 0x00790000, 0xfe000fff);
 #endif
 		platform_device_register(&pmu_device);
arch/arm/mach-realview/realview_pb1176.c +7 -1
index c5eade76461b..e3bddb5ab10f 100644
@@ -355,7 +355,13 @@ static void __init realview_pb1176_init(void)
 	int i;
 
 #ifdef CONFIG_CACHE_L2X0
-	/* 128Kb (16Kb/way) 8-way associativity. evmon/parity/share enabled. */
+	/*
+	 * The PL220 needs to be manually configured as the hardware
+	 * doesn't report the correct sizes.
+	 * 128kB (16kB/way), 8-way associativity, event monitor and
+	 * parity enabled, ignore share bit, no force write allocate
+	 * Bits:  .... ...0 0111 0011 0000 .... .... ....
+	 */
 	l2x0_init(__io_address(REALVIEW_PB1176_L220_BASE), 0x00730000, 0xfe000fff);
 #endif
 
arch/arm/mach-realview/realview_pb11mp.c +7 -2
index f4b0962578fe..101deaf2630b 100644
@@ -337,8 +337,13 @@ static void __init realview_pb11mp_init(void)
 	int i;
 
 #ifdef CONFIG_CACHE_L2X0
-	/* 1MB (128KB/way), 8-way associativity, evmon/parity/share enabled
-	 * Bits:  .... ...0 0111 1001 0000 .... .... .... */
+	/*
+	 * The PL220 needs to be manually configured as the hardware
+	 * doesn't report the correct sizes.
+	 * 1MB (128KB/way), 8-way associativity, event monitor and
+	 * parity enabled, ignore share bit, no force write allocate
+	 * Bits:  .... ...0 0111 1001 0000 .... .... ....
+	 */
 	l2x0_init(__io_address(REALVIEW_TC11MP_L220_BASE), 0x00790000, 0xfe000fff);
 #endif
 
Signature · 1 line
1.8.3.1
#65 lore raw

PATCH 65/222 ARM: l2c: kill L2X0_AUX_CTRL_MASK before anyone else makes use of this

In reply to Russell King - ARM Linux

L2X0_AUX_CTRL_MASK is not useful for PL310s. It would be better if people thought about their value for this rather than cargo-cult programming.

---
 arch/arm/include/asm/hardware/cache-l2x0.h | 1 -
 1 file changed, 1 deletion(-)
arch/arm/include/asm/hardware/cache-l2x0.h +0 -1
index b3ee122c6f24..84bbd31b8910 100644
@@ -87,7 +87,6 @@
 #define L310_CACHE_ID_RTL_R3P2		0x08
 #define L310_CACHE_ID_RTL_R3P3		0x09
 
-#define L2X0_AUX_CTRL_MASK			0xc0000fff
 /* L2C auxiliary control register - bits common to L2C-210/220/310 */
 #define L2C_AUX_CTRL_WAY_SIZE_SHIFT		17
 #define L2C_AUX_CTRL_WAY_SIZE_MASK		(7 << 17)
Signature · 1 line
1.8.3.1
#66 lore raw

PATCH 66/222 ARM: l2c: print a warning with L2C-310 caches if the cache size is modified

In reply to Russell King - ARM Linux

As we have now removed all instances of the L2C-310 having its cache size "modified" via platform/SoC code, discourage new cases showing up by printing a warning.

---
 arch/arm/mm/cache-l2x0.c | 2 ++
 1 file changed, 2 insertions(+)
arch/arm/mm/cache-l2x0.c +2 -0
index db3f18a968b2..48b826e759cb 100644
@@ -732,6 +732,8 @@ static void __init __l2c_init(const struct l2c_init_data *data,
 	/* Determine the number of ways */
 	switch (cache_id & L2X0_CACHE_ID_PART_MASK) {
 	case L2X0_CACHE_ID_PART_L310:
+		if ((aux_val | ~aux_mask) & (L2C_AUX_CTRL_WAY_SIZE_MASK | L310_AUX_CTRL_ASSOCIATIVITY_16))
+			pr_warn("L2C: DT/platform tries to modify or specify cache size\n");
 		if (aux & (1 << 16))
 			ways = 16;
 		else
Signature · 1 line
1.8.3.1
#67 lore raw

PATCH 67/222 ARM: l2c: vexpress ca9x4: move L2 cache initialisation earlier

In reply to Russell King - ARM Linux

It is beneficial to have the L2 cache up and running earlier in the system boot. Not only will this allow for simpler code when we come to enable some features, but it also means that we get a more accurate bogomips value for the udelay() loop. Calibrating the loop with the L2 cache off, and then running with the L2 cache on is not the best idea.

---
 arch/arm/mach-vexpress/ct-ca9x4.c | 28 ++++++++++++++++++----------
 1 file changed, 18 insertions(+), 10 deletions(-)
arch/arm/mach-vexpress/ct-ca9x4.c +18 -10
index 6c4ffb6c5ad8..204038ef3795 100644
@@ -45,6 +45,23 @@ static void __init ct_ca9x4_map_io(void)
 	iotable_init(ct_ca9x4_io_desc, ARRAY_SIZE(ct_ca9x4_io_desc));
 }
 
+static void __init ca9x4_l2_init(void)
+{
+#ifdef CONFIG_CACHE_L2X0
+	void __iomem *l2x0_base = ioremap(CT_CA9X4_L2CC, SZ_4K);
+
+	if (l2x0_base) {
+		/* set RAM latencies to 1 cycle for this core tile. */
+		writel(0, l2x0_base + L310_TAG_LATENCY_CTRL);
+		writel(0, l2x0_base + L310_DATA_LATENCY_CTRL);
+
+		l2x0_init(l2x0_base, 0x00400000, 0xfe0fffff);
+	} else {
+		pr_err("L2C: unable to map L2 cache controller\n");
+	}
+#endif
+}
+
 #ifdef CONFIG_HAVE_ARM_TWD
 static DEFINE_TWD_LOCAL_TIMER(twd_local_timer, A9_MPCORE_TWD, IRQ_LOCALTIMER);
 
@@ -63,6 +80,7 @@ static void __init ct_ca9x4_init_irq(void)
 	gic_init(0, 29, ioremap(A9_MPCORE_GIC_DIST, SZ_4K),
 		 ioremap(A9_MPCORE_GIC_CPU, SZ_256));
 	ca9x4_twd_init();
+	ca9x4_l2_init();
 }
 
 static int ct_ca9x4_clcd_setup(struct clcd_fb *fb)
@@ -141,16 +159,6 @@ static void __init ct_ca9x4_init(void)
 {
 	int i;
 
-#ifdef CONFIG_CACHE_L2X0
-	void __iomem *l2x0_base = ioremap(CT_CA9X4_L2CC, SZ_4K);
-
-	/* set RAM latencies to 1 cycle for this core tile. */
-	writel(0, l2x0_base + L310_TAG_LATENCY_CTRL);
-	writel(0, l2x0_base + L310_DATA_LATENCY_CTRL);
-
-	l2x0_init(l2x0_base, 0x00400000, 0xfe0fffff);
-#endif
-
 	for (i = 0; i < ARRAY_SIZE(ct_ca9x4_amba_devs); i++)
 		amba_device_register(ct_ca9x4_amba_devs[i], &iomem_resource);
 
Signature · 1 line
1.8.3.1
#68 lore raw

PATCH 68/222 ARM: l2c: add L2C-310 power control DT properties

In reply to Russell King - ARM Linux

Add two new properties for setting thte L3 power control register. Two new properties are added:

arm,dynamic-clk-gating
arm,standby-mode

iMX6 sets both these, add the properties there.

---
 Documentation/devicetree/bindings/arm/l2cc.txt |  2 ++
 arch/arm/boot/dts/imx6qdl.dtsi                 |  2 ++
 arch/arm/boot/dts/imx6sl.dtsi                  |  2 ++
 arch/arm/mm/cache-l2x0.c                       | 10 ++++++++++
 4 files changed, 16 insertions(+)
Documentation/devicetree/bindings/arm/l2cc.txt +2 -0
index b513cb8196fe..e0dd400ecea6 100644
@@ -40,6 +40,8 @@ implementations of the L2 cache controller with compatible programming models.
 - arm,filter-ranges : <start length> Starting address and length of window to
   filter. Addresses in the filter window are directed to the M1 port. Other
   addresses will go to the M0 port.
+- arm,dynamic-clk-gating : Enables dynamic clock gating (PL310)
+- arm,standby-mode : Enables standby mode (PL310)
 - interrupts : 1 combined interrupt.
 - cache-id-part: cache id part number to be used if it is not present
   on hardware
arch/arm/boot/dts/imx6qdl.dtsi +2 -0
index 55cb926fa3f7..1bde76683017 100644
@@ -125,6 +125,8 @@
 			cache-level = <2>;
 			arm,tag-latency = <4 2 3>;
 			arm,data-latency = <4 2 3>;
+			arm,dynamic-clk-gating;
+			arm,standby-mode;
 		};
 
 		pcie: pcie at 0x01000000 {
arch/arm/boot/dts/imx6sl.dtsi +2 -0
index 3cb4941afeef..691832f26453 100644
@@ -111,6 +111,8 @@
 			cache-level = <2>;
 			arm,tag-latency = <4 2 3>;
 			arm,data-latency = <4 2 3>;
+			arm,dynamic-clk-gating;
+			arm,standby-mode;
 		};
 
 		pmu {
arch/arm/mm/cache-l2x0.c +10 -0
index 48b826e759cb..8a5110deb657 100644
@@ -904,6 +904,7 @@ static void __init l2c310_of_parse(const struct device_node *np,
 	u32 data[3] = { 0, 0, 0 };
 	u32 tag[3] = { 0, 0, 0 };
 	u32 filter[2] = { 0, 0 };
+	u32 val;
 
 	of_property_read_u32_array(np, "arm,tag-latency", tag, ARRAY_SIZE(tag));
 	if (tag[0] && tag[1] && tag[2])
@@ -930,6 +931,15 @@ static void __init l2c310_of_parse(const struct device_node *np,
 		writel_relaxed((filter[0] & ~(SZ_1M - 1)) | L310_ADDR_FILTER_EN,
 			       l2x0_base + L310_ADDR_FILTER_START);
 	}
+
+	val = 0;
+	if (of_property_read_bool(np, "arm,dynamic-clk-gating"))
+		val |= L310_DYNAMIC_CLK_GATING_EN;
+	if (of_property_read_bool(np, "arm,standby-mode"))
+		val |= L310_STNDBY_MODE_EN;
+
+	if (val)
+		l2c_write_sec(val, l2x0_base, L310_POWER_CTRL);
 }
 
 static const struct l2c_init_data of_l2c310_data __initconst = {
Signature · 1 line
1.8.3.1
#69 lore raw

PATCH 69/222 ARM: l2c: check that DT files specify the required "cache-unified" property

In reply to Russell King - ARM Linux

This is a required property, and should always be specified.

---
 arch/arm/mm/cache-l2x0.c | 4 ++++
 1 file changed, 4 insertions(+)
arch/arm/mm/cache-l2x0.c +4 -0
index 8a5110deb657..96a1374127de 100644
@@ -1378,6 +1378,10 @@ int __init l2x0_of_init(u32 aux_val, u32 aux_mask)
 
 	data = of_match_node(l2x0_ids, np)->data;
 
+	/* All L2 caches are unified, so this property should be specified */
+	if (!of_property_read_bool(np, "cache-unified"))
+		pr_err("L2C: device tree omits to specify unified cache\n");
+
 	/* L2 configuration can only be changed if the cache is disabled */
 	if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN))
 		if (data->of_parse)
Signature · 1 line
1.8.3.1
#70 lore raw

PATCH 70/222 ARM: l2c: add warnings for stuff modifying aux_ctrl register values

In reply to Russell King - ARM Linux
---
 arch/arm/mm/cache-l2x0.c | 27 +++++++++++++++++++++++----
 1 file changed, 23 insertions(+), 4 deletions(-)
arch/arm/mm/cache-l2x0.c +23 -4
index 96a1374127de..5de7f6c56306 100644
@@ -715,7 +715,15 @@ static void __init __l2c_init(const struct l2c_init_data *data,
 {
 	struct outer_cache_fns fns;
 	unsigned way_size_bits, ways;
-	u32 aux;
+	u32 aux, old_aux;
+
+	/*
+	 * Sanity check the aux values.  aux_mask is the bits we preserve
+	 * from reading the hardware register, and aux_val is the bits we
+	 * set.
+	 */
+	if (aux_val & aux_mask)
+		pr_alert("L2C: platform provided aux values permit register corruption.\n");
 
 	/*
 	 * It is strange to save the register state before initialisation,
@@ -724,11 +732,14 @@ static void __init __l2c_init(const struct l2c_init_data *data,
 	if (data->save)
 		data->save(l2x0_base);
 
-	aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
-
+	old_aux = aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
 	aux &= aux_mask;
 	aux |= aux_val;
 
+	if (old_aux != aux)
+		pr_warn("L2C: DT/platform modifies aux control register: 0x%08x -> 0x%08x\n",
+		        old_aux, aux);
+
 	/* Determine the number of ways */
 	switch (cache_id & L2X0_CACHE_ID_PART_MASK) {
 	case L2X0_CACHE_ID_PART_L310:
@@ -1361,7 +1372,7 @@ int __init l2x0_of_init(u32 aux_val, u32 aux_mask)
 	const struct l2c_init_data *data;
 	struct device_node *np;
 	struct resource res;
-	u32 cache_id;
+	u32 cache_id, old_aux;
 
 	np = of_find_matching_node(NULL, l2x0_ids);
 	if (!np)
@@ -1378,6 +1389,14 @@ int __init l2x0_of_init(u32 aux_val, u32 aux_mask)
 
 	data = of_match_node(l2x0_ids, np)->data;
 
+	old_aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
+	if (old_aux != ((old_aux & aux_mask) | aux_val)) {
+		pr_warn("L2C: platform modifies aux control register: 0x%08x -> 0x%08x\n",
+		        old_aux, (old_aux & aux_mask) | aux_val);
+	} else if (aux_mask != ~0U && aux_val != 0) {
+		pr_alert("L2C: platform provided aux values match the hardware, so have no effect.  Please remove them.\n");
+	}
+
 	/* All L2 caches are unified, so this property should be specified */
 	if (!of_property_read_bool(np, "cache-unified"))
 		pr_err("L2C: device tree omits to specify unified cache\n");
Signature · 1 line
1.8.3.1
#71 lore raw

PATCH 71/222 net:fec: remove unnecessary ip stack includes

In reply to Russell King - ARM Linux

Commit 4c09eed9dc42 (net: fec: Enable imx6 enet checksum acceleration.) added support for hardware checksum support, adding six new includes for things like tcp, udp, ip, icmp, but never made use of anything provided by those header files. Let's remove this bloat.

---
 drivers/net/ethernet/freescale/fec_main.c | 6 ------
 1 file changed, 6 deletions(-)
drivers/net/ethernet/freescale/fec_main.c +0 -6
index 8d69e439f0c5..b151eaf3b500 100644
@@ -33,12 +33,6 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
-#include <linux/in.h>
-#include <linux/ip.h>
-#include <net/ip.h>
-#include <linux/tcp.h>
-#include <linux/udp.h>
-#include <linux/icmp.h>
 #include <linux/spinlock.h>
 #include <linux/workqueue.h>
 #include <linux/bitops.h>
Signature · 1 line
1.8.3.1
#72 lore raw

PATCH 71/222 net:fec: remove unnecessary ip stack includes

Quoted 6 lines · Russell King

Commit 4c09eed9dc42 (net: fec: Enable imx6 enet checksum acceleration.) added support for hardware checksum support, adding six new includes for things like tcp, udp, ip, icmp, but never made use of anything provided by those header files. Let's remove this bloat.

Russell

I see patches from 71 to 126 are related with fec driver. But maintainer David S. Miller is not in to list.

Do you need someone follow up this patches and resent to David
S Miller till accepted by david? Or you will do it by yourself?

Best regards
Frank Li

Quoted 29 lines
---
 drivers/net/ethernet/freescale/fec_main.c | 6 ------
 1 file changed, 6 deletions(-)
drivers/net/ethernet/freescale/fec_main.c +0 -7
index 8d69e439f0c5..b151eaf3b500 100644
@@ -33,12 +33,6 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
-#include <linux/in.h>
-#include <linux/ip.h>
-#include <net/ip.h>
-#include <linux/tcp.h>
-#include <linux/udp.h>
-#include <linux/icmp.h>
 #include <linux/spinlock.h>
 #include <linux/workqueue.h>
 #include <linux/bitops.h>
--

1.8.3.1

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

#73 lore raw

PATCH 71/222 net:fec: remove unnecessary ip stack includes

Quoted 9 lines · Frank Li
Quoted 6 lines · Russell King

Commit 4c09eed9dc42 (net: fec: Enable imx6 enet checksum acceleration.) added support for hardware checksum support, adding six new includes for things like tcp, udp, ip, icmp, but never made use of anything provided by those header files. Let's remove this bloat.

Quoted 7 lines

Russell

I see patches from 71 to 126 are related with fec driver. But maintainer David S. Miller is not in to list.

Do you need someone follow up this patches and resent to David
S Miller till accepted by david? Or you will do it by yourself?

I know they need to re-worked to convert unsigned -> unsigned int before David sees them, otherwise it'd be an instant review failure. Doing the conversion is the easy bit, doing it without hitting the 80 column limit is much harder...

Signature · 2 lines
FTTC broadband for 0.8mile line: now at 9.7Mbps down 460kbps up... slowly
improving, and getting towards what was expected from it.
#74 lore raw

PATCH 72/222 net:fec: reorder ethtool ops to match order in struct declaration

In reply to Russell King - ARM Linux

This allows us to merge two separate preprocessor conditionals together.

---
 drivers/net/ethernet/freescale/fec_main.c | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)
drivers/net/ethernet/freescale/fec_main.c +5 -7
index b151eaf3b500..875c0ca55c5a 100644
@@ -1639,21 +1639,19 @@ static int fec_enet_nway_reset(struct net_device *dev)
 }
 
 static const struct ethtool_ops fec_enet_ethtool_ops = {
-#if !defined(CONFIG_M5272)
-	.get_pauseparam		= fec_enet_get_pauseparam,
-	.set_pauseparam		= fec_enet_set_pauseparam,
-#endif
 	.get_settings		= fec_enet_get_settings,
 	.set_settings		= fec_enet_set_settings,
 	.get_drvinfo		= fec_enet_get_drvinfo,
-	.get_link		= ethtool_op_get_link,
-	.get_ts_info		= fec_enet_get_ts_info,
 	.nway_reset		= fec_enet_nway_reset,
+	.get_link		= ethtool_op_get_link,
 #ifndef CONFIG_M5272
-	.get_ethtool_stats	= fec_enet_get_ethtool_stats,
+	.get_pauseparam		= fec_enet_get_pauseparam,
+	.set_pauseparam		= fec_enet_set_pauseparam,
 	.get_strings		= fec_enet_get_strings,
+	.get_ethtool_stats	= fec_enet_get_ethtool_stats,
 	.get_sset_count		= fec_enet_get_sset_count,
 #endif
+	.get_ts_info		= fec_enet_get_ts_info,
 };
 
 static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
Signature · 1 line
1.8.3.1
#75 lore raw

PATCH 73/222 net:fec: remove checking for NULL phy_dev in fec_enet_close()

In reply to Russell King - ARM Linux

fep->phy_dev can not be NULL here for two reasons:
- fec_enet_open() will have successfully connected the phy, or will have failed.
- fec_enet_open() will have called phy_start(fep->phy_dev), which unconditionally dereferences this pointer.

If it were to be NULL here, then fec_enet_open() will have already oopsed.

---
 drivers/net/ethernet/freescale/fec_main.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)
drivers/net/ethernet/freescale/fec_main.c +2 -4
index 875c0ca55c5a..b7a5614b874d 100644
@@ -1798,10 +1798,8 @@ fec_enet_close(struct net_device *ndev)
 	netif_stop_queue(ndev);
 	fec_stop(ndev);
 
-	if (fep->phy_dev) {
-		phy_stop(fep->phy_dev);
-		phy_disconnect(fep->phy_dev);
-	}
+	phy_stop(fep->phy_dev);
+	phy_disconnect(fep->phy_dev);
 
 	fec_enet_free_buffers(ndev);
 
Signature · 1 line
1.8.3.1
#76 lore raw

PATCH 74/222 net:fec: ensure that a disconnected phy isn't configured

In reply to Russell King - ARM Linux

When we disconnect from a phy, we should forget our pointer to it so we don't accidentally try to configure it. We handle a NULL phy pointer correctly in most places, except fec_enet_set_pauseparam(). Fix this too.

---
 drivers/net/ethernet/freescale/fec_main.c | 4 ++++
 1 file changed, 4 insertions(+)
drivers/net/ethernet/freescale/fec_main.c +4 -0
index b7a5614b874d..9de899eb9f8e 100644
@@ -1496,6 +1496,9 @@ static int fec_enet_set_pauseparam(struct net_device *ndev,
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
 
+	if (!fep->phy_dev)
+		return -ENODEV;
+
 	if (pause->tx_pause != pause->rx_pause) {
 		netdev_info(ndev,
 			"hardware only support enable/disable both tx and rx");
@@ -1800,6 +1803,7 @@ fec_enet_close(struct net_device *ndev)
 
 	phy_stop(fep->phy_dev);
 	phy_disconnect(fep->phy_dev);
+	fep->phy_dev = NULL;
 
 	fec_enet_free_buffers(ndev);
 
Signature · 1 line
1.8.3.1
#77 lore raw

PATCH 75/222 net:fec: add support for dumping transmit ring on timeout

In reply to Russell King - ARM Linux

When we timeout on transmit, it would be useful to dump the transmit ring, so we can see the ring state. This can be helpful to diagnose the cause of transmit timeouts.

---
 drivers/net/ethernet/freescale/fec_main.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)
drivers/net/ethernet/freescale/fec_main.c +23 -0
index 9de899eb9f8e..7d506ae2dbba 100644
@@ -291,6 +291,27 @@ static void *swap_buffer(void *bufaddr, int len)
 	return bufaddr;
 }
 
+static void fec_dump(struct net_device *ndev)
+{
+	struct fec_enet_private *fep = netdev_priv(ndev);
+	struct bufdesc *bdp = fep->tx_bd_base;
+	unsigned index = 0;
+
+	netdev_info(ndev, "TX ring dump\n");
+	pr_info("Nr     SC     addr       len  SKB\n");
+
+	do {
+		pr_info("%3u %c%c 0x%04x 0x%08lx %4u %p\n",
+			index,
+			bdp == fep->cur_tx ? 'S' : ' ',
+			bdp == fep->dirty_tx ? 'H' : ' ',
+			bdp->cbd_sc, bdp->cbd_bufaddr, bdp->cbd_datlen,
+			fep->tx_skbuff[index]);
+		bdp = fec_enet_get_nextdesc(bdp, fep);
+		index++;
+	} while (bdp != fep->tx_bd_base);
+}
+
 static int
 fec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev)
 {
@@ -717,6 +738,8 @@ fec_timeout(struct net_device *ndev)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
 
+	fec_dump(ndev);
+
 	ndev->stats.tx_errors++;
 
 	fep->delay_work.timeout = true;
Signature · 1 line
1.8.3.1
#78 lore raw

PATCH 76/222 net:fec: use netif_tx_disable() rather than netif_stop_queue()

In reply to Russell King - ARM Linux

We use netif_stop_queue() in several places where we want to ensure that the start_xmit function is not running. netif_stop_queue() is not sufficient to achieve that - it merely sets a flag to indicate that the transmit queue(s) should not be run.

netif_tx_disable() gives this guarantee, since it takes the transmit queue lock while marking the queue stopped. This will wait for the transmit function to complete before returning.

---
 drivers/net/ethernet/freescale/fec_main.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/ethernet/freescale/fec_main.c +2 -2
index 7d506ae2dbba..1c84f3a6f3a7 100644
@@ -522,7 +522,7 @@ fec_restart(struct net_device *ndev, int duplex)
 	if (netif_running(ndev)) {
 		netif_device_detach(ndev);
 		napi_disable(&fep->napi);
-		netif_stop_queue(ndev);
+		netif_tx_disable(ndev);
 		netif_tx_lock_bh(ndev);
 	}
 
@@ -1821,7 +1821,7 @@ fec_enet_close(struct net_device *ndev)
 	/* Don't know what to do yet. */
 	napi_disable(&fep->napi);
 	fep->opened = 0;
-	netif_stop_queue(ndev);
+	netif_tx_disable(ndev);
 	fec_stop(ndev);
 
 	phy_stop(fep->phy_dev);
Signature · 1 line
1.8.3.1
#79 lore raw

PATCH 77/222 net:fec: iMX6 FEC does not support half-duplex gigabit

In reply to Russell King - ARM Linux

The iMX6 gigabit FEC does not support half-duplex gigabit operation. Phys attacked to the FEC may support this, and we currently do nothing to disable this feature. This may result in an invalid configuration. Mask out phy support for gigabit half-duplex operation.

---
 drivers/net/ethernet/freescale/fec_main.c | 1 +
 1 file changed, 1 insertion(+)
drivers/net/ethernet/freescale/fec_main.c +1 -0
index 1c84f3a6f3a7..d9109acc934d 100644
@@ -1316,6 +1316,7 @@ static int fec_enet_mii_probe(struct net_device *ndev)
 	/* mask with MAC supported features */
 	if (id_entry->driver_data & FEC_QUIRK_HAS_GBIT) {
 		phy_dev->supported &= PHY_GBIT_FEATURES;
+		phy_dev->supported &= ~SUPPORTED_1000baseT_Half;
 #if !defined(CONFIG_M5272)
 		phy_dev->supported |= SUPPORTED_Pause;
 #endif
Signature · 1 line
1.8.3.1
#80 lore raw

PATCH 78/222 net:fec: clean up transmit descriptor setup

In reply to Russell King - ARM Linux

Avoid writing any state until we're certain we can proceed with the
transmission: this avoids writing mapping error address values to the descriptors, or setting the skbuff pointer until we have successfully mapped the skb.

---
 drivers/net/ethernet/freescale/fec_main.c | 32 +++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)
drivers/net/ethernet/freescale/fec_main.c +16 -16
index d9109acc934d..45cb558bb227 100644
@@ -337,6 +337,8 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 	void *bufaddr;
 	unsigned short	status;
 	unsigned int index;
+	unsigned length;
+	dma_addr_t addr;
 
 	/* Fill in a Tx ring entry */
 	bdp = fep->cur_tx;
@@ -362,7 +364,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 
 	/* Set buffer length and buffer pointer */
 	bufaddr = skb->data;
-	bdp->cbd_datlen = skb->len;
+	length = skb->len;
 
 	/*
 	 * On some FEC implementations data must be aligned on
@@ -376,7 +378,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 		index = bdp - fep->tx_bd_base;
 
 	if (((unsigned long) bufaddr) & FEC_ALIGNMENT) {
-		memcpy(fep->tx_bounce[index], skb->data, skb->len);
+		memcpy(fep->tx_bounce[index], skb->data, length);
 		bufaddr = fep->tx_bounce[index];
 	}
 
@@ -386,25 +388,23 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 	 * swap every frame going to and coming from the controller.
 	 */
 	if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
-		swap_buffer(bufaddr, skb->len);
+		swap_buffer(bufaddr, length);
 
-	/* Save skb pointer */
-	fep->tx_skbuff[index] = skb;
-
-	/* Push the data cache so the CPM does not get stale memory
-	 * data.
-	 */
-	bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, bufaddr,
-			skb->len, DMA_TO_DEVICE);
-	if (dma_mapping_error(&fep->pdev->dev, bdp->cbd_bufaddr)) {
-		bdp->cbd_bufaddr = 0;
-		fep->tx_skbuff[index] = NULL;
+	/* Push the data cache so the CPM does not get stale memory data. */
+	addr = dma_map_single(&fep->pdev->dev, bufaddr, length, DMA_TO_DEVICE);
+	if (dma_mapping_error(&fep->pdev->dev, addr)) {
 		dev_kfree_skb_any(skb);
 		if (net_ratelimit())
 			netdev_err(ndev, "Tx DMA memory map failed\n");
 		return NETDEV_TX_OK;
 	}
 
+	/* Save skb pointer */
+	fep->tx_skbuff[index] = skb;
+
+	bdp->cbd_datlen = length;
+	bdp->cbd_bufaddr = addr;
+
 	if (fep->bufdesc_ex) {
 
 		struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
@@ -489,7 +489,7 @@ static void fec_enet_bd_init(struct net_device *dev)
 
 		/* Initialize the BD for every fragment in the page. */
 		bdp->cbd_sc = 0;
-		if (bdp->cbd_bufaddr && fep->tx_skbuff[i]) {
+		if (fep->tx_skbuff[i]) {
 			dev_kfree_skb_any(fep->tx_skbuff[i]);
 			fep->tx_skbuff[i] = NULL;
 		}
@@ -793,6 +793,7 @@ fec_enet_tx(struct net_device *ndev)
 			index = bdp - fep->tx_bd_base;
 
 		skb = fep->tx_skbuff[index];
+		fep->tx_skbuff[index] = NULL;
 		dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr, skb->len,
 				DMA_TO_DEVICE);
 		bdp->cbd_bufaddr = 0;
@@ -842,7 +843,6 @@ fec_enet_tx(struct net_device *ndev)
 
 		/* Free the sk buffer associated with this last transmit */
 		dev_kfree_skb_any(skb);
-		fep->tx_skbuff[index] = NULL;
 
 		fep->dirty_tx = bdp;
 
Signature · 1 line
1.8.3.1
#81 lore raw

PATCH 79/222 net:fec: make rx skb allocation/map more robust

In reply to Russell King - ARM Linux

Allocate, and then map the receive skb before writing any data to the ring descriptor or storing the skb. This means we have ring data in one of two states: either fully setup, or not setup. This simplifies the cleanup, which could end up calling dma_unmap_single() on a DMA error value.

---
 drivers/net/ethernet/freescale/fec_main.c | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)
drivers/net/ethernet/freescale/fec_main.c +10 -6
index 45cb558bb227..f98ddb0923a3 100644
@@ -1712,12 +1712,11 @@ static void fec_enet_free_buffers(struct net_device *ndev)
 	bdp = fep->rx_bd_base;
 	for (i = 0; i < fep->rx_ring_size; i++) {
 		skb = fep->rx_skbuff[i];
-
-		if (bdp->cbd_bufaddr)
+		if (skb) {
 			dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
 					FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
-		if (skb)
 			dev_kfree_skb(skb);
+		}
 		bdp = fec_enet_get_nextdesc(bdp, fep);
 	}
 
@@ -1735,21 +1734,26 @@ static int fec_enet_alloc_buffers(struct net_device *ndev)
 
 	bdp = fep->rx_bd_base;
 	for (i = 0; i < fep->rx_ring_size; i++) {
+		dma_addr_t addr;
+
 		skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE);
 		if (!skb) {
 			fec_enet_free_buffers(ndev);
 			return -ENOMEM;
 		}
-		fep->rx_skbuff[i] = skb;
 
-		bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, skb->data,
+		addr = dma_map_single(&fep->pdev->dev, skb->data,
 				FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
-		if (dma_mapping_error(&fep->pdev->dev, bdp->cbd_bufaddr)) {
+		if (dma_mapping_error(&fep->pdev->dev, addr)) {
+			dev_kfree_skb(skb);
 			fec_enet_free_buffers(ndev);
 			if (net_ratelimit())
 				netdev_err(ndev, "Rx DMA memory map failed\n");
 			return -ENOMEM;
 		}
+
+		fep->rx_skbuff[i] = skb;
+		bdp->cbd_bufaddr = addr;
 		bdp->cbd_sc = BD_ENET_RX_EMPTY;
 
 		if (fep->bufdesc_ex) {
Signature · 1 line
1.8.3.1
#82 lore raw

PATCH 80/222 net:fec: ensure fec_enet_free_buffers() properly cleans the rings

In reply to Russell King - ARM Linux

Ensure that we do not double-free any allocations, and that any transmit skbuffs are properly freed when we clean up the rings.

---
 drivers/net/ethernet/freescale/fec_main.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
drivers/net/ethernet/freescale/fec_main.c +8 -1
index f98ddb0923a3..08a5004970c9 100644
@@ -1712,6 +1712,7 @@ static void fec_enet_free_buffers(struct net_device *ndev)
 	bdp = fep->rx_bd_base;
 	for (i = 0; i < fep->rx_ring_size; i++) {
 		skb = fep->rx_skbuff[i];
+		fep->rx_skbuff[i] = NULL;
 		if (skb) {
 			dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
 					FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
@@ -1721,8 +1722,14 @@ static void fec_enet_free_buffers(struct net_device *ndev)
 	}
 
 	bdp = fep->tx_bd_base;
-	for (i = 0; i < fep->tx_ring_size; i++)
+	for (i = 0; i < fep->tx_ring_size; i++) {
 		kfree(fep->tx_bounce[i]);
+		fep->tx_bounce[i] = NULL;
+		skb = fep->tx_skbuff[i];
+		fep->tx_skbuff[i] = NULL;
+		if (skb)
+			dev_kfree_skb(skb);
+	}
 }
 
 static int fec_enet_alloc_buffers(struct net_device *ndev)
Signature · 1 line
1.8.3.1
#83 lore raw

PATCH 81/222 net:fec: fix missing kmalloc() failure check in fec_enet_alloc_buffers()

In reply to Russell King - ARM Linux

fec_enet_alloc_buffers() appears to assume that kmalloc() will never fail. This is an unreasonable assumption. Add a proper check and failure cleanup path.

---
 drivers/net/ethernet/freescale/fec_main.c | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)
drivers/net/ethernet/freescale/fec_main.c +9 -6
index 08a5004970c9..7f7d9dcb485e 100644
@@ -1744,19 +1744,16 @@ static int fec_enet_alloc_buffers(struct net_device *ndev)
 		dma_addr_t addr;
 
 		skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE);
-		if (!skb) {
-			fec_enet_free_buffers(ndev);
-			return -ENOMEM;
-		}
+		if (!skb)
+			goto err_alloc;
 
 		addr = dma_map_single(&fep->pdev->dev, skb->data,
 				FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
 		if (dma_mapping_error(&fep->pdev->dev, addr)) {
 			dev_kfree_skb(skb);
-			fec_enet_free_buffers(ndev);
 			if (net_ratelimit())
 				netdev_err(ndev, "Rx DMA memory map failed\n");
-			return -ENOMEM;
+			goto err_alloc;
 		}
 
 		fep->rx_skbuff[i] = skb;
@@ -1778,6 +1775,8 @@ static int fec_enet_alloc_buffers(struct net_device *ndev)
 	bdp = fep->tx_bd_base;
 	for (i = 0; i < fep->tx_ring_size; i++) {
 		fep->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL);
+		if (!fep->tx_bounce[i])
+			goto err_alloc;
 
 		bdp->cbd_sc = 0;
 		bdp->cbd_bufaddr = 0;
@@ -1795,6 +1794,10 @@ static int fec_enet_alloc_buffers(struct net_device *ndev)
 	bdp->cbd_sc |= BD_SC_WRAP;
 
 	return 0;
+
+ err_alloc:
+	fec_enet_free_buffers(ndev);
+	return -ENOMEM;
 }
 
 static int
Signature · 1 line
1.8.3.1
#84 lore raw

PATCH 82/222 net:fec: ensure that we dma unmap the transmit descriptors

In reply to Russell King - ARM Linux

On transmit timeout or close, dirty transmit descriptors were not being correctly cleaned: the only time that DMA mappings are cleaned is when scanning the TX ring after a transmit interrupt. Fix this.

---
 drivers/net/ethernet/freescale/fec_main.c | 21 +++++++++++++++++----
 1 file changed, 17 insertions(+), 4 deletions(-)
drivers/net/ethernet/freescale/fec_main.c +17 -4
index 7f7d9dcb485e..94861870e741 100644
@@ -327,6 +327,17 @@ fec_enet_clear_csum(struct sk_buff *skb, struct net_device *ndev)
 	return 0;
 }
 
+static void
+fec_enet_tx_unmap(struct bufdesc *bdp, struct fec_enet_private *fep)
+{
+	dma_addr_t addr = bdp->cbd_bufaddr;
+	unsigned length = bdp->cbd_datlen;
+
+	bdp->cbd_bufaddr = 0;
+
+	dma_unmap_single(&fep->pdev->dev, addr, length, DMA_TO_DEVICE);
+}
+
 static netdev_tx_t
 fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 {
@@ -489,11 +500,12 @@ static void fec_enet_bd_init(struct net_device *dev)
 
 		/* Initialize the BD for every fragment in the page. */
 		bdp->cbd_sc = 0;
+		if (bdp->cbd_bufaddr)
+			fec_enet_tx_unmap(bdp, fep);
 		if (fep->tx_skbuff[i]) {
 			dev_kfree_skb_any(fep->tx_skbuff[i]);
 			fep->tx_skbuff[i] = NULL;
 		}
-		bdp->cbd_bufaddr = 0;
 		bdp = fec_enet_get_nextdesc(bdp, fep);
 	}
 
@@ -792,11 +804,10 @@ fec_enet_tx(struct net_device *ndev)
 		else
 			index = bdp - fep->tx_bd_base;
 
+		fec_enet_tx_unmap(bdp, fep);
+
 		skb = fep->tx_skbuff[index];
 		fep->tx_skbuff[index] = NULL;
-		dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr, skb->len,
-				DMA_TO_DEVICE);
-		bdp->cbd_bufaddr = 0;
 
 		/* Check for errors. */
 		if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC |
@@ -1723,6 +1734,8 @@ static void fec_enet_free_buffers(struct net_device *ndev)
 
 	bdp = fep->tx_bd_base;
 	for (i = 0; i < fep->tx_ring_size; i++) {
+		if (bdp->cbd_bufaddr)
+			fec_enet_tx_unmap(bdp, fep);
 		kfree(fep->tx_bounce[i]);
 		fep->tx_bounce[i] = NULL;
 		skb = fep->tx_skbuff[i];
Signature · 1 line
1.8.3.1
#85 lore raw

PATCH 83/222 net:fec: remove useless fep->opened

In reply to Russell King - ARM Linux

napi_disable() waits until the NAPI processing has completed, and then prevents any further polls. At this point, the driver then clears fep->opened. The NAPI poll function uses this to stop processing in the receive path. Hence, it will never see this variable cleared, because the NAPI poll has to complete before it will be cleared.

Therefore, this variable serves no purpose, so let's remove it.

---
 drivers/net/ethernet/freescale/fec.h      | 1 -
 drivers/net/ethernet/freescale/fec_main.c | 5 -----
 2 files changed, 6 deletions(-)
drivers/net/ethernet/freescale/fec.h +0 -1
index 3b8d6d19ff05..88ef53b18ae7 100644
@@ -301,7 +301,6 @@ struct fec_enet_private {
 
 	struct	platform_device *pdev;
 
-	int	opened;
 	int	dev_id;
 
 	/* Phylib and MDIO interface */
drivers/net/ethernet/freescale/fec_main.c +0 -5
index 94861870e741..634046c73734 100644
@@ -914,9 +914,6 @@ fec_enet_rx(struct net_device *ndev, int budget)
 		if ((status & BD_ENET_RX_LAST) == 0)
 			netdev_err(ndev, "rcv is not +last\n");
 
-		if (!fep->opened)
-			goto rx_processing_done;
-
 		/* Check for errors. */
 		if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO |
 			   BD_ENET_RX_CR | BD_ENET_RX_OV)) {
@@ -1837,7 +1834,6 @@ fec_enet_open(struct net_device *ndev)
 	napi_enable(&fep->napi);
 	phy_start(fep->phy_dev);
 	netif_start_queue(ndev);
-	fep->opened = 1;
 	return 0;
 }
 
@@ -1848,7 +1844,6 @@ fec_enet_close(struct net_device *ndev)
 
 	/* Don't know what to do yet. */
 	napi_disable(&fep->napi);
-	fep->opened = 0;
 	netif_tx_disable(ndev);
 	fec_stop(ndev);
 
Signature · 1 line
1.8.3.1
#86 lore raw

PATCH 84/222 net:fec: stop the phy before shutting down the MAC

In reply to Russell King - ARM Linux

When the network interface goes down, stop the phy to prevent further link up status changes before taking the MAC or netif sections down. This prevents further reception of link up events which could potentially call fec_restart().

Since phy_stop() takes the mutex which adjust_link() runs under, we also ensure that adjust_link() will not already be processing a link up event.

We also need to do this when suspending as well - we don't want a mis-timed phy state change to restart the MAC after we have stopped it for suspend, and thus need to restart the phy when resuming.

---
 drivers/net/ethernet/freescale/fec_main.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/net/ethernet/freescale/fec_main.c +4 -1
index 634046c73734..765ea90b99ed 100644
@@ -1842,12 +1842,13 @@ fec_enet_close(struct net_device *ndev)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
 
+	phy_stop(fep->phy_dev);
+
 	/* Don't know what to do yet. */
 	napi_disable(&fep->napi);
 	netif_tx_disable(ndev);
 	fec_stop(ndev);
 
-	phy_stop(fep->phy_dev);
 	phy_disconnect(fep->phy_dev);
 	fep->phy_dev = NULL;
 
@@ -2333,6 +2334,7 @@ fec_suspend(struct device *dev)
 	struct fec_enet_private *fep = netdev_priv(ndev);
 
 	if (netif_running(ndev)) {
+		phy_stop(fep->phy_dev);
 		fec_stop(ndev);
 		netif_device_detach(ndev);
 	}
@@ -2385,6 +2387,7 @@ fec_resume(struct device *dev)
 	if (netif_running(ndev)) {
 		fec_restart(ndev, fep->full_duplex);
 		netif_device_attach(ndev);
+		phy_start(fep->phy_dev);
 	}
 
 	return 0;
Signature · 1 line
1.8.3.1
#87 lore raw

PATCH 85/222 net:fec: improve safety of suspend/resume/transmit timeout paths

In reply to Russell King - ARM Linux

We need to hold the rtnl lock while suspending, resuming or processing the transmit timeout to ensure that netif_running() will not change state beneath us while we bring up, take down or restart the hardware.

---
 drivers/net/ethernet/freescale/fec_main.c | 7 +++++++
 1 file changed, 7 insertions(+)
drivers/net/ethernet/freescale/fec_main.c +7 -0
index 765ea90b99ed..843721aa44bd 100644
@@ -767,8 +767,10 @@ static void fec_enet_work(struct work_struct *work)
 
 	if (fep->delay_work.timeout) {
 		fep->delay_work.timeout = false;
+		rtnl_lock();
 		fec_restart(fep->netdev, fep->full_duplex);
 		netif_wake_queue(fep->netdev);
+		rtnl_unlock();
 	}
 
 	if (fep->delay_work.trig_tx) {
@@ -2333,11 +2335,14 @@ fec_suspend(struct device *dev)
 	struct net_device *ndev = dev_get_drvdata(dev);
 	struct fec_enet_private *fep = netdev_priv(ndev);
 
+	rtnl_lock();
 	if (netif_running(ndev)) {
 		phy_stop(fep->phy_dev);
 		fec_stop(ndev);
 		netif_device_detach(ndev);
 	}
+	rtnl_unlock();
+
 	if (fep->clk_ptp)
 		clk_disable_unprepare(fep->clk_ptp);
 	if (fep->clk_enet_out)
@@ -2384,11 +2389,13 @@ fec_resume(struct device *dev)
 			goto failed_clk_ptp;
 	}
 
+	rtnl_lock();
 	if (netif_running(ndev)) {
 		fec_restart(ndev, fep->full_duplex);
 		netif_device_attach(ndev);
 		phy_start(fep->phy_dev);
 	}
+	rtnl_unlock();
 
 	return 0;
 
Signature · 1 line
1.8.3.1
#88 lore raw

PATCH 86/222 net:fec: ensure fec_enet_close() copes with resume failure

In reply to Russell King - ARM Linux

When the FEC is suspended, the device is detached. Upon resume failure, the device is left in detached mode, possibly with some of the required clocks not running. We don't want to be poking the device in that state because as it may cause bus errors.

If the device is marked detached, avoid calling fec_stop().

This depends upon: "net:fec: improve safety of suspend/resume paths"

---
 drivers/net/ethernet/freescale/fec_main.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/ethernet/freescale/fec_main.c +2 -2
index 843721aa44bd..f65c1f1d8770 100644
@@ -1846,10 +1846,10 @@ fec_enet_close(struct net_device *ndev)
 
 	phy_stop(fep->phy_dev);
 
-	/* Don't know what to do yet. */
 	napi_disable(&fep->napi);
 	netif_tx_disable(ndev);
-	fec_stop(ndev);
+	if (netif_device_present(ndev))
+		fec_stop(ndev);
 
 	phy_disconnect(fep->phy_dev);
 	fep->phy_dev = NULL;
Signature · 1 line
1.8.3.1
#89 lore raw

PATCH 87/222 net:fec: only run a restart or stop if the device is present and running

In reply to Russell King - ARM Linux

Avoid calling fec_restart() or fec_stop() while the device is down or not present (iow suspended.)

Although the ndo_timeout method will only be called if the device is present and running, we defer this to a work queue. The work queue can run independently, and so needs to repeat these checks to ensure that a restart doesn't occur after the device has been taken down or detached for suspend. In this case, we call fec_restart() in the resume path, so nothing is lost.

For fec_set_features, we add a call to fec_restart() in fec_enet_open() to ensure that the hardware is appropriate programmed when the interface is opened. fec_set_features() call should not occur while we're suspended, so we don't have to worry about that case.

The adjust_link needs similar treatment - this also is called from a work queue, which may be run independently after we have taken the device down and detached it. In this case, we just mark the link down and take no further action. We will reset things appropriately once the device is up and running again, at which point we will receive another adjust_link callback.

---
 drivers/net/ethernet/freescale/fec_main.c | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)
drivers/net/ethernet/freescale/fec_main.c +14 -5
index f65c1f1d8770..723a645dfeb3 100644
@@ -764,12 +764,15 @@ static void fec_enet_work(struct work_struct *work)
 		container_of(work,
 			     struct fec_enet_private,
 			     delay_work.delay_work.work);
+	struct net_device *ndev = fep->netdev;
 
 	if (fep->delay_work.timeout) {
 		fep->delay_work.timeout = false;
 		rtnl_lock();
-		fec_restart(fep->netdev, fep->full_duplex);
-		netif_wake_queue(fep->netdev);
+		if (netif_device_present(ndev) || netif_running(ndev)) {
+			fec_restart(ndev, fep->full_duplex);
+			netif_wake_queue(ndev);
+		}
 		rtnl_unlock();
 	}
 
@@ -1200,7 +1203,14 @@ static void fec_enet_adjust_link(struct net_device *ndev)
 		return;
 	}
 
-	if (phy_dev->link) {
+	/*
+	 * If the netdev is down, or is going down, we're not interested
+	 * in link state events, so just mark our idea of the link as down
+	 * and ignore the event.
+	 */
+	if (!netif_running(ndev) || !netif_device_present(ndev)) {
+		fep->link = 0;
+	} else if (phy_dev->link) {
 		if (!fep->link) {
 			fep->link = phy_dev->link;
 			status_change = 1;
@@ -1833,6 +1843,7 @@ fec_enet_open(struct net_device *ndev)
 		return ret;
 	}
 
+	fec_restart(ndev, fep->full_duplex);
 	napi_enable(&fep->napi);
 	phy_start(fep->phy_dev);
 	netif_start_queue(ndev);
@@ -1997,8 +2008,6 @@ static int fec_set_features(struct net_device *netdev,
 			fec_stop(netdev);
 			fec_restart(netdev, fep->phy_dev->duplex);
 			netif_wake_queue(netdev);
-		} else {
-			fec_restart(netdev, fep->phy_dev->duplex);
 		}
 	}
 
Signature · 1 line
1.8.3.1
#90 lore raw

PATCH 88/222 net:fec: move calls to quiesce/resume packet processing out of fec_restart()

In reply to Russell King - ARM Linux

Move the calls to quiesce and resume packet processing out of fec_restart() to its call sites. This is the first step in a two stage clean up of this code, where we just move the calls out of fec_restart() without changing them. Not everywhere needs to issue these calls, and not everywhere needs all of these calls to be issued.

---
 drivers/net/ethernet/freescale/fec_main.c | 64 ++++++++++++++++++++++---------
 1 file changed, 45 insertions(+), 19 deletions(-)
drivers/net/ethernet/freescale/fec_main.c +45 -19
index 723a645dfeb3..1da67d4fdd0a 100644
@@ -515,9 +515,10 @@ static void fec_enet_bd_init(struct net_device *dev)
 	fep->dirty_tx = bdp;
 }
 
-/* This function is called to start or restart the FEC during a link
- * change.  This only happens when switching between half and full
- * duplex.
+/*
+ * This function is called to start or restart the FEC during a link
+ * change, transmit timeout, or to reconfigure the FEC.  The network
+ * packet processing for this device must be stopped before this call.
  */
 static void
 fec_restart(struct net_device *ndev, int duplex)
@@ -531,13 +532,6 @@ fec_restart(struct net_device *ndev, int duplex)
 	u32 rcntl = OPT_FRAME_SIZE | 0x04;
 	u32 ecntl = 0x2; /* ETHEREN */
 
-	if (netif_running(ndev)) {
-		netif_device_detach(ndev);
-		napi_disable(&fep->napi);
-		netif_tx_disable(ndev);
-		netif_tx_lock_bh(ndev);
-	}
-
 	/* Whack a reset.  We should wait for this. */
 	writel(1, fep->hwp + FEC_ECNTRL);
 	udelay(10);
@@ -706,13 +700,6 @@ fec_restart(struct net_device *ndev, int duplex)
 
 	/* Enable interrupts we wish to service */
 	writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
-
-	if (netif_running(ndev)) {
-		netif_tx_unlock_bh(ndev);
-		netif_wake_queue(ndev);
-		napi_enable(&fep->napi);
-		netif_device_attach(ndev);
-	}
 }
 
 static void
@@ -770,8 +757,15 @@ static void fec_enet_work(struct work_struct *work)
 		fep->delay_work.timeout = false;
 		rtnl_lock();
 		if (netif_device_present(ndev) || netif_running(ndev)) {
+			netif_device_detach(ndev);
+			napi_disable(&fep->napi);
+			netif_tx_disable(ndev);
+			netif_tx_lock_bh(ndev);
 			fec_restart(ndev, fep->full_duplex);
+			netif_tx_unlock_bh(ndev);
 			netif_wake_queue(ndev);
+			napi_enable(&fep->napi);
+			netif_device_attach(ndev);
 		}
 		rtnl_unlock();
 	}
@@ -1225,8 +1219,17 @@ static void fec_enet_adjust_link(struct net_device *ndev)
 		}
 
 		/* if any of the above changed restart the FEC */
-		if (status_change)
+		if (status_change) {
+			netif_device_detach(ndev);
+			napi_disable(&fep->napi);
+			netif_tx_disable(ndev);
+			netif_tx_lock_bh(ndev);
 			fec_restart(ndev, phy_dev->duplex);
+			netif_tx_unlock_bh(ndev);
+			netif_wake_queue(ndev);
+			napi_enable(&fep->napi);
+			netif_device_attach(ndev);
+		}
 	} else {
 		if (fep->link) {
 			fec_stop(ndev);
@@ -1568,8 +1571,17 @@ static int fec_enet_set_pauseparam(struct net_device *ndev,
 			fec_stop(ndev);
 		phy_start_aneg(fep->phy_dev);
 	}
-	if (netif_running(ndev))
+	if (netif_running(ndev)) {
+		netif_device_detach(ndev);
+		napi_disable(&fep->napi);
+		netif_tx_disable(ndev);
+		netif_tx_lock_bh(ndev);
 		fec_restart(ndev, 0);
+		netif_tx_unlock_bh(ndev);
+		netif_wake_queue(ndev);
+		napi_enable(&fep->napi);
+		netif_device_attach(ndev);
+	}
 
 	return 0;
 }
@@ -2006,8 +2018,15 @@ static int fec_set_features(struct net_device *netdev,
 
 		if (netif_running(netdev)) {
 			fec_stop(netdev);
+			netif_device_detach(netdev);
+			napi_disable(&fep->napi);
+			netif_tx_disable(netdev);
+			netif_tx_lock_bh(netdev);
 			fec_restart(netdev, fep->phy_dev->duplex);
+			netif_tx_unlock_bh(netdev);
 			netif_wake_queue(netdev);
+			napi_enable(&fep->napi);
+			netif_device_attach(netdev);
 		}
 	}
 
@@ -2400,7 +2419,14 @@ fec_resume(struct device *dev)
 
 	rtnl_lock();
 	if (netif_running(ndev)) {
+		netif_device_detach(ndev);
+		napi_disable(&fep->napi);
+		netif_tx_disable(ndev);
+		netif_tx_lock_bh(ndev);
 		fec_restart(ndev, fep->full_duplex);
+		netif_tx_unlock_bh(ndev);
+		netif_wake_queue(ndev);
+		napi_enable(&fep->napi);
 		netif_device_attach(ndev);
 		phy_start(fep->phy_dev);
 	}
Signature · 1 line
1.8.3.1
#91 lore raw

PATCH 89/222 net:fec: remove inappropriate calls around fec_restart()

In reply to Russell King - ARM Linux

This is the second stage to "move calls to quiesce/resume packet processing out of fec_restart()", where we remove calls which are not appropriate to the call site.

In the majority of cases, there is no need to detach and reattach the interface as we are holding the queue xmit lock across the reset. The exception to that is in fec_resume(), where we are already detached by the suspend function. Here, we can remove the call to detach the interface.

We also do not need to stop the transmit queue. Holding the xmit lock is enough to ensure that the transmit packet processing is not running while we perform our task. However, since fec_restart() always cleans the rings, we call netif_wake_queue() (or netif_device_attach() in the case of resume) just before dropping the xmit lock. This prevents the watchdog firing.

Lastly, always call napi_enable() after the device has been reattached in the resume path so that we know that the transmit packet processing is already in an enabled state, so we don't call netif_wake_queue() while detached.

---
 drivers/net/ethernet/freescale/fec_main.c | 26 ++++++--------------------
 1 file changed, 6 insertions(+), 20 deletions(-)
drivers/net/ethernet/freescale/fec_main.c +6 -20
index 1da67d4fdd0a..02e121a19d87 100644
@@ -757,15 +757,12 @@ static void fec_enet_work(struct work_struct *work)
 		fep->delay_work.timeout = false;
 		rtnl_lock();
 		if (netif_device_present(ndev) || netif_running(ndev)) {
-			netif_device_detach(ndev);
 			napi_disable(&fep->napi);
-			netif_tx_disable(ndev);
 			netif_tx_lock_bh(ndev);
 			fec_restart(ndev, fep->full_duplex);
-			netif_tx_unlock_bh(ndev);
 			netif_wake_queue(ndev);
+			netif_tx_unlock_bh(ndev);
 			napi_enable(&fep->napi);
-			netif_device_attach(ndev);
 		}
 		rtnl_unlock();
 	}
@@ -1220,15 +1217,12 @@ static void fec_enet_adjust_link(struct net_device *ndev)
 
 		/* if any of the above changed restart the FEC */
 		if (status_change) {
-			netif_device_detach(ndev);
 			napi_disable(&fep->napi);
-			netif_tx_disable(ndev);
 			netif_tx_lock_bh(ndev);
 			fec_restart(ndev, phy_dev->duplex);
-			netif_tx_unlock_bh(ndev);
 			netif_wake_queue(ndev);
+			netif_tx_unlock_bh(ndev);
 			napi_enable(&fep->napi);
-			netif_device_attach(ndev);
 		}
 	} else {
 		if (fep->link) {
@@ -1572,15 +1566,12 @@ static int fec_enet_set_pauseparam(struct net_device *ndev,
 		phy_start_aneg(fep->phy_dev);
 	}
 	if (netif_running(ndev)) {
-		netif_device_detach(ndev);
 		napi_disable(&fep->napi);
-		netif_tx_disable(ndev);
 		netif_tx_lock_bh(ndev);
 		fec_restart(ndev, 0);
-		netif_tx_unlock_bh(ndev);
 		netif_wake_queue(ndev);
+		netif_tx_unlock_bh(ndev);
 		napi_enable(&fep->napi);
-		netif_device_attach(ndev);
 	}
 
 	return 0;
@@ -2018,15 +2009,12 @@ static int fec_set_features(struct net_device *netdev,
 
 		if (netif_running(netdev)) {
 			fec_stop(netdev);
-			netif_device_detach(netdev);
 			napi_disable(&fep->napi);
-			netif_tx_disable(netdev);
 			netif_tx_lock_bh(netdev);
 			fec_restart(netdev, fep->phy_dev->duplex);
-			netif_tx_unlock_bh(netdev);
 			netif_wake_queue(netdev);
+			netif_tx_unlock_bh(netdev);
 			napi_enable(&fep->napi);
-			netif_device_attach(netdev);
 		}
 	}
 
@@ -2419,15 +2407,13 @@ fec_resume(struct device *dev)
 
 	rtnl_lock();
 	if (netif_running(ndev)) {
-		netif_device_detach(ndev);
 		napi_disable(&fep->napi);
-		netif_tx_disable(ndev);
 		netif_tx_lock_bh(ndev);
 		fec_restart(ndev, fep->full_duplex);
+		netif_device_attach(ndev);
 		netif_tx_unlock_bh(ndev);
-		netif_wake_queue(ndev);
-		napi_enable(&fep->napi);
 		netif_device_attach(ndev);
+		napi_enable(&fep->napi);
 		phy_start(fep->phy_dev);
 	}
 	rtnl_unlock();
Signature · 1 line
1.8.3.1
#92 lore raw

PATCH 90/222 net:fec: quiesce packet processing before stopping device in fec_suspend()

In reply to Russell King - ARM Linux

fec_suspend() calls fec_stop() to stop the transmit ring while the transmit packet processing is still active. This can lead to the transmit queue being restarted by an intervening packet queued for transmission, or by the tx quirk timer expiring. Fix this by moving the call to fec_enet_netif_quiesce() before fec_stop(). We must remove the corresponding call to fec_enet_netif_quiesce() from the resume path.

We also need to avoid calling fec_enet_netif_quiesce() inside fec_enet_close() to cover the failed resume scenario otherwise fec_enet_close() will hang in napi_disable().

---
 drivers/net/ethernet/freescale/fec_main.c | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)
drivers/net/ethernet/freescale/fec_main.c +9 -7
index 02e121a19d87..927c283e43b1 100644
@@ -1860,10 +1860,11 @@ fec_enet_close(struct net_device *ndev)
 
 	phy_stop(fep->phy_dev);
 
-	napi_disable(&fep->napi);
-	netif_tx_disable(ndev);
-	if (netif_device_present(ndev))
+	if (netif_device_present(ndev)) {
+		napi_disable(&fep->napi);
+		netif_tx_disable(ndev);
 		fec_stop(ndev);
+	}
 
 	phy_disconnect(fep->phy_dev);
 	fep->phy_dev = NULL;
@@ -2354,8 +2355,11 @@ fec_suspend(struct device *dev)
 	rtnl_lock();
 	if (netif_running(ndev)) {
 		phy_stop(fep->phy_dev);
-		fec_stop(ndev);
+		napi_disable(&fep->napi);
+		netif_tx_lock_bh(ndev);
 		netif_device_detach(ndev);
+		netif_tx_unlock_bh(ndev);
+		fec_stop(ndev);
 	}
 	rtnl_unlock();
 
@@ -2407,12 +2411,10 @@ fec_resume(struct device *dev)
 
 	rtnl_lock();
 	if (netif_running(ndev)) {
-		napi_disable(&fep->napi);
-		netif_tx_lock_bh(ndev);
 		fec_restart(ndev, fep->full_duplex);
+		netif_tx_lock_bh(ndev);
 		netif_device_attach(ndev);
 		netif_tx_unlock_bh(ndev);
-		netif_device_attach(ndev);
 		napi_enable(&fep->napi);
 		phy_start(fep->phy_dev);
 	}
Signature · 1 line
1.8.3.1
#93 lore raw

PATCH 91/222 net:fec: quiesce packet processing before stopping device in fec_set_features()

In reply to Russell King - ARM Linux

fec_set_features() calls fec_stop() to stop the transmit ring while the transmit queue is still active. This can lead to the transmit ring being restarted by an intervening packet queued for transmission, or by the tx quirk timer expiring. Fix this by moving the call to fec_enet_netif_quiesce() before fec_stop().

---
 drivers/net/ethernet/freescale/fec_main.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/freescale/fec_main.c +1 -1
index 927c283e43b1..789678c6f6d4 100644
@@ -2009,9 +2009,9 @@ static int fec_set_features(struct net_device *netdev,
 			fep->csum_flags &= ~FLAG_RX_CSUM_ENABLED;
 
 		if (netif_running(netdev)) {
-			fec_stop(netdev);
 			napi_disable(&fep->napi);
 			netif_tx_lock_bh(netdev);
+			fec_stop(netdev);
 			fec_restart(netdev, fep->phy_dev->duplex);
 			netif_wake_queue(netdev);
 			netif_tx_unlock_bh(netdev);
Signature · 1 line
1.8.3.1
#94 lore raw

PATCH 92/222 net:fec: quiesce packet processing before changing features

In reply to Russell King - ARM Linux

Changing the features (receive checksumming) requires the hardware to be reprogrammed, and also changes the checks in the receive packet processing.

The current implementation has a race - fec_set_features() changes the flags which alter the receive packet processing while we are active. Only afterwards do we shutdown and reconfigure the hardware.

This can lead to packets being received and marked with a valid checksum (via CHECKSUM_UNNECESSARY) when the hardware checksum validation has not yet been enabled.

We must quiesce the device, then change the software configuration for this feature, and then resume the device if it was previously running.

The resulting code structure also allows us to add other configuration features in this path without having to quiesce and resume the network interface and device.

---
 drivers/net/ethernet/freescale/fec_main.c | 25 ++++++++++++++++---------
 1 file changed, 16 insertions(+), 9 deletions(-)
drivers/net/ethernet/freescale/fec_main.c +16 -9
index 789678c6f6d4..c5a5a0aedf21 100644
@@ -1993,12 +1993,21 @@ static void fec_poll_controller(struct net_device *dev)
 }
 #endif
 
+#define FEATURES_NEED_QUIESCE NETIF_F_RXCSUM
+
 static int fec_set_features(struct net_device *netdev,
 	netdev_features_t features)
 {
 	struct fec_enet_private *fep = netdev_priv(netdev);
 	netdev_features_t changed = features ^ netdev->features;
 
+	/* Quiesce the device if necessary */
+	if (netif_running(netdev) && changed & FEATURES_NEED_QUIESCE) {
+		napi_disable(&fep->napi);
+		netif_tx_lock_bh(netdev);
+		fec_stop(netdev);
+	}
+
 	netdev->features = features;
 
 	/* Receive checksum has been changed */
@@ -2007,16 +2016,14 @@ static int fec_set_features(struct net_device *netdev,
 			fep->csum_flags |= FLAG_RX_CSUM_ENABLED;
 		else
 			fep->csum_flags &= ~FLAG_RX_CSUM_ENABLED;
+	}
 
-		if (netif_running(netdev)) {
-			napi_disable(&fep->napi);
-			netif_tx_lock_bh(netdev);
-			fec_stop(netdev);
-			fec_restart(netdev, fep->phy_dev->duplex);
-			netif_wake_queue(netdev);
-			netif_tx_unlock_bh(netdev);
-			napi_enable(&fep->napi);
-		}
+	/* Resume the device after updates */
+	if (netif_running(netdev) && changed & FEATURES_NEED_QUIESCE) {
+		fec_restart(netdev, fep->phy_dev->duplex);
+		netif_wake_queue(netdev);
+		netif_tx_unlock_bh(netdev);
+		napi_enable(&fep->napi);
 	}
 
 	return 0;
Signature · 1 line
1.8.3.1
#95 lore raw

PATCH 93/222 net:fec: quiesce packet processing when taking link down in fec_enet_adjust_link()

In reply to Russell King - ARM Linux

When the link goes down, the adjust_link method will be called, but there is no synchronisation to ensure that we won't be processing some last remaining packets while performing a reset of the device.

Add the necessary synchronisation to ensure that packet processing is complete before we stop and reset the FEC.

---
 drivers/net/ethernet/freescale/fec_main.c | 4 ++++
 1 file changed, 4 insertions(+)
drivers/net/ethernet/freescale/fec_main.c +4 -0
index c5a5a0aedf21..6156d423a6a9 100644
@@ -1226,7 +1226,11 @@ static void fec_enet_adjust_link(struct net_device *ndev)
 		}
 	} else {
 		if (fep->link) {
+			napi_disable(&fep->napi);
+			netif_tx_lock_bh(ndev);
 			fec_stop(ndev);
+			netif_tx_unlock_bh(ndev);
+			napi_enable(&fep->napi);
 			fep->link = phy_dev->link;
 			status_change = 1;
 		}
Signature · 1 line
1.8.3.1
#96 lore raw

PATCH 94/222 net:fec: fix ethtool set_pauseparam duplex bug

In reply to Russell King - ARM Linux

Setting the pause parameters causes a running network interface to be restarted. However, the restart forces the FEC into half-duplex mode, whether or not the remote end is in half-duplex mode. Misconfigured duplex mode is a known source of problems on a link.

Fix this by always preserving the duplex mode on configuration changes.

---
 drivers/net/ethernet/freescale/fec_main.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/freescale/fec_main.c +1 -1
index 6156d423a6a9..c79d604372a4 100644
@@ -1572,7 +1572,7 @@ static int fec_enet_set_pauseparam(struct net_device *ndev,
 	if (netif_running(ndev)) {
 		napi_disable(&fep->napi);
 		netif_tx_lock_bh(ndev);
-		fec_restart(ndev, 0);
+		fec_restart(ndev, fep->full_duplex);
 		netif_wake_queue(ndev);
 		netif_tx_unlock_bh(ndev);
 		napi_enable(&fep->napi);
Signature · 1 line
1.8.3.1
#97 lore raw

PATCH 95/222 net:fec: clean up duplex mode handling

In reply to Russell King - ARM Linux

Many places call fec_restart() with the second parameter being some kind of previously saved duplex value, but only two places call it with some other setting. This is at odds with how the other link settings are handled, and used to be racy before the rtnl locks were added to fec_restart()'s various call paths.

Clean this up so all link capabilities are handled in the same way - saved into the fec_enet_private structure, and then fec_restart() acts on those settings.

---
 drivers/net/ethernet/freescale/fec_main.c | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)
drivers/net/ethernet/freescale/fec_main.c +12 -12
index c79d604372a4..d5ad0c70e6a0 100644
@@ -521,7 +521,7 @@ static void fec_enet_bd_init(struct net_device *dev)
  * packet processing for this device must be stopped before this call.
  */
 static void
-fec_restart(struct net_device *ndev, int duplex)
+fec_restart(struct net_device *ndev)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
 	const struct platform_device_id *id_entry =
@@ -572,7 +572,7 @@ fec_restart(struct net_device *ndev, int duplex)
 	}
 
 	/* Enable MII mode */
-	if (duplex) {
+	if (fep->full_duplex == DUPLEX_FULL) {
 		/* FD enable */
 		writel(0x04, fep->hwp + FEC_X_CNTRL);
 	} else {
@@ -581,8 +581,6 @@ fec_restart(struct net_device *ndev, int duplex)
 		writel(0x0, fep->hwp + FEC_X_CNTRL);
 	}
 
-	fep->full_duplex = duplex;
-
 	/* Set MII speed */
 	writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
 
@@ -759,7 +757,7 @@ static void fec_enet_work(struct work_struct *work)
 		if (netif_device_present(ndev) || netif_running(ndev)) {
 			napi_disable(&fep->napi);
 			netif_tx_lock_bh(ndev);
-			fec_restart(ndev, fep->full_duplex);
+			fec_restart(ndev);
 			netif_wake_queue(ndev);
 			netif_tx_unlock_bh(ndev);
 			napi_enable(&fep->napi);
@@ -1207,8 +1205,10 @@ static void fec_enet_adjust_link(struct net_device *ndev)
 			status_change = 1;
 		}
 
-		if (fep->full_duplex != phy_dev->duplex)
+		if (fep->full_duplex != phy_dev->duplex) {
+			fep->full_duplex = phy_dev->duplex;
 			status_change = 1;
+		}
 
 		if (phy_dev->speed != fep->speed) {
 			fep->speed = phy_dev->speed;
@@ -1219,7 +1219,7 @@ static void fec_enet_adjust_link(struct net_device *ndev)
 		if (status_change) {
 			napi_disable(&fep->napi);
 			netif_tx_lock_bh(ndev);
-			fec_restart(ndev, phy_dev->duplex);
+			fec_restart(ndev);
 			netif_wake_queue(ndev);
 			netif_tx_unlock_bh(ndev);
 			napi_enable(&fep->napi);
@@ -1572,7 +1572,7 @@ static int fec_enet_set_pauseparam(struct net_device *ndev,
 	if (netif_running(ndev)) {
 		napi_disable(&fep->napi);
 		netif_tx_lock_bh(ndev);
-		fec_restart(ndev, fep->full_duplex);
+		fec_restart(ndev);
 		netif_wake_queue(ndev);
 		netif_tx_unlock_bh(ndev);
 		napi_enable(&fep->napi);
@@ -1850,7 +1850,7 @@ fec_enet_open(struct net_device *ndev)
 		return ret;
 	}
 
-	fec_restart(ndev, fep->full_duplex);
+	fec_restart(ndev);
 	napi_enable(&fep->napi);
 	phy_start(fep->phy_dev);
 	netif_start_queue(ndev);
@@ -2024,7 +2024,7 @@ static int fec_set_features(struct net_device *netdev,
 
 	/* Resume the device after updates */
 	if (netif_running(netdev) && changed & FEATURES_NEED_QUIESCE) {
-		fec_restart(netdev, fep->phy_dev->duplex);
+		fec_restart(netdev);
 		netif_wake_queue(netdev);
 		netif_tx_unlock_bh(netdev);
 		napi_enable(&fep->napi);
@@ -2110,7 +2110,7 @@ static int fec_enet_init(struct net_device *ndev)
 		fep->csum_flags |= FLAG_RX_CSUM_ENABLED;
 	}
 
-	fec_restart(ndev, 0);
+	fec_restart(ndev);
 
 	return 0;
 }
@@ -2422,7 +2422,7 @@ fec_resume(struct device *dev)
 
 	rtnl_lock();
 	if (netif_running(ndev)) {
-		fec_restart(ndev, fep->full_duplex);
+		fec_restart(ndev);
 		netif_tx_lock_bh(ndev);
 		netif_device_attach(ndev);
 		netif_tx_unlock_bh(ndev);
Signature · 1 line
1.8.3.1
#98 lore raw

PATCH 96/222 net:fec: add barrier to transmit path to ensure proper ordering

In reply to Russell King - ARM Linux

Ensure that the writes to the descriptor data is visible to the hardware before the descriptor is handed over to the hardware.

Having discussed this with Will Deacon, we need a wmb() between writing the descriptor data and handing the descriptor over to the hardware. The corresponding rmb() is in the ethernet hardware.

---
 drivers/net/ethernet/freescale/fec_main.c | 7 +++++++
 1 file changed, 7 insertions(+)
drivers/net/ethernet/freescale/fec_main.c +7 -0
index d5ad0c70e6a0..2d9a6493b33b 100644
@@ -436,6 +436,13 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 		}
 	}
 
+	/*
+	 * We need the preceding stores to the descriptor to complete
+	 * before updating the status field, which hands it over to the
+	 * hardware.  The corresponding rmb() is "in the hardware".
+	 */
+	wmb();
+
 	/* Send it on its way.  Tell FEC it's ready, interrupt when done,
 	 * it's the last BD of the frame, and to put the CRC on the end.
 	 */
Signature · 1 line
1.8.3.1
#99 lore raw

PATCH 97/222 net:fec: add barrier to receive path to ensure proper ordering

In reply to Russell King - ARM Linux

Just as we need a barrier in the transmit path, we also need a barrier in the receive path to ensure that we don't modify a handed over descriptor.

---
 drivers/net/ethernet/freescale/fec_main.c | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)
drivers/net/ethernet/freescale/fec_main.c +13 -7
index 2d9a6493b33b..9d2023b3e5ce 100644
@@ -1036,13 +1036,6 @@ fec_enet_rx(struct net_device *ndev, int budget)
 		dma_sync_single_for_device(&fep->pdev->dev, bdp->cbd_bufaddr,
 					FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
 rx_processing_done:
-		/* Clear the status flags for this buffer */
-		status &= ~BD_ENET_RX_STATS;
-
-		/* Mark the buffer empty */
-		status |= BD_ENET_RX_EMPTY;
-		bdp->cbd_sc = status;
-
 		if (fep->bufdesc_ex) {
 			struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
 
@@ -1051,6 +1044,19 @@ fec_enet_rx(struct net_device *ndev, int budget)
 			ebdp->cbd_bdu = 0;
 		}
 
+		/*
+		 * Ensure that the previous writes have completed before
+		 * the status update becomes visible.
+		 */
+		wmb();
+
+		/* Clear the status flags for this buffer */
+		status &= ~BD_ENET_RX_STATS;
+
+		/* Mark the buffer empty */
+		status |= BD_ENET_RX_EMPTY;
+		bdp->cbd_sc = status;
+
 		/* Update BD pointer to next entry */
 		bdp = fec_enet_get_nextdesc(bdp, fep);
 
Signature · 1 line
1.8.3.1
#100 lore raw

PATCH 98/222 net:fec: better implementation of iMX6 ERR006358 quirk

In reply to Russell King - ARM Linux

Using a (delayed) workqueue for ERR006358 is not correct - a work queue is a single-trigger device. Once the work queue has been scheduled, it can't be re-scheduled until it has been run. This can cause problems - with an appropriate packet timing, we can end up with packets queued, but not sent by the hardware, resulting in the transmit timeout firing.

Re-implement this as per the workaround detailed in the ERR006358 documentation - if there are packets waiting to be sent when we service the transmit ring, and we see that the transmitter is not running, kick the transmitter to run the pending entries in the ring.

Testing here with a 10Mbit half duplex link sees the resulting iperf TCP bandwidth increase from between 1 to 2Mbps to between 8 to 9Mbps.

---
 drivers/net/ethernet/freescale/fec.h      |  1 -
 drivers/net/ethernet/freescale/fec_main.c | 20 +++++++-------------
 2 files changed, 7 insertions(+), 14 deletions(-)
drivers/net/ethernet/freescale/fec.h +0 -1
index 88ef53b18ae7..d124a19e3fe8 100644
@@ -259,7 +259,6 @@ struct bufdesc_ex {
 struct fec_enet_delayed_work {
 	struct delayed_work delay_work;
 	bool timeout;
-	bool trig_tx;
 };
 
 /* The FEC buffer descriptors track the ring buffers.  The rx_bd_base and
drivers/net/ethernet/freescale/fec_main.c +7 -13
index 9d2023b3e5ce..7e48d1960bf0 100644
@@ -344,7 +344,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 	struct fec_enet_private *fep = netdev_priv(ndev);
 	const struct platform_device_id *id_entry =
 				platform_get_device_id(fep->pdev);
-	struct bufdesc *bdp, *bdp_pre;
+	struct bufdesc *bdp;
 	void *bufaddr;
 	unsigned short	status;
 	unsigned int index;
@@ -450,13 +450,6 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 			| BD_ENET_TX_LAST | BD_ENET_TX_TC);
 	bdp->cbd_sc = status;
 
-	bdp_pre = fec_enet_get_prevdesc(bdp, fep);
-	if ((id_entry->driver_data & FEC_QUIRK_ERR006358) &&
-	    !(bdp_pre->cbd_sc & BD_ENET_TX_READY)) {
-		fep->delay_work.trig_tx = true;
-		schedule_delayed_work(&(fep->delay_work.delay_work),
-					msecs_to_jiffies(1));
-	}
 
 	/* If this was the last BD in the ring, start at the beginning again. */
 	bdp = fec_enet_get_nextdesc(bdp, fep);
@@ -771,11 +764,6 @@ static void fec_enet_work(struct work_struct *work)
 		}
 		rtnl_unlock();
 	}
-
-	if (fep->delay_work.trig_tx) {
-		fep->delay_work.trig_tx = false;
-		writel(0, fep->hwp + FEC_X_DES_ACTIVE);
-	}
 }
 
 static void
@@ -868,6 +856,12 @@ fec_enet_tx(struct net_device *ndev)
 				netif_wake_queue(ndev);
 		}
 	}
+
+	/* ERR006538: Keep the transmitter going */
+	if (fep->dirty_tx != fep->cur_tx &&
+	    readl(fep->hwp + FEC_X_DES_ACTIVE) == 0)
+		writel(0, fep->hwp + FEC_X_DES_ACTIVE);
+
 	return;
 }
 
Signature · 1 line
1.8.3.1
#101 lore raw

PATCH 99/222 net:fec: avoid hitting transmitter if it is still running

In reply to Russell King - ARM Linux
---
 drivers/net/ethernet/freescale/fec_main.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/ethernet/freescale/fec_main.c +2 -1
index 7e48d1960bf0..bf186cc54891 100644
@@ -462,7 +462,8 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 		netif_stop_queue(ndev);
 
 	/* Trigger transmission start */
-	writel(0, fep->hwp + FEC_X_DES_ACTIVE);
+	if (readl(fep->hwp + FEC_X_DES_ACTIVE) == 0)
+		writel(0, fep->hwp + FEC_X_DES_ACTIVE);
 
 	return NETDEV_TX_OK;
 }
Signature · 1 line
1.8.3.1
#102 lore raw

PATCH 100/222 net:fec: remove delayed work

In reply to Russell King - ARM Linux

We no longer need a delayed work in this driver, since we no longer have the requirement to delay any work - instead, use a work_struct directly.

---
 drivers/net/ethernet/freescale/fec.h      |  8 ++------
 drivers/net/ethernet/freescale/fec_main.c | 34 +++++++++++++------------------
 2 files changed, 16 insertions(+), 26 deletions(-)
drivers/net/ethernet/freescale/fec.h +2 -6
index d124a19e3fe8..aecc46c33a82 100644
@@ -256,11 +256,6 @@ struct bufdesc_ex {
 #define FLAG_RX_CSUM_ENABLED	(BD_ENET_RX_ICE | BD_ENET_RX_PCR)
 #define FLAG_RX_CSUM_ERROR	(BD_ENET_RX_ICE | BD_ENET_RX_PCR)
 
-struct fec_enet_delayed_work {
-	struct delayed_work delay_work;
-	bool timeout;
-};
-
 /* The FEC buffer descriptors track the ring buffers.  The rx_bd_base and
  * tx_bd_base always point to the base of the buffer descriptors.  The
  * cur_rx and cur_tx point to the currently available buffer.
@@ -319,6 +314,8 @@ struct fec_enet_private {
 	struct	napi_struct napi;
 	int	csum_flags;
 
+	struct work_struct tx_timeout_work;
+
 	struct ptp_clock *ptp_clock;
 	struct ptp_clock_info ptp_caps;
 	unsigned long last_overflow_check;
@@ -331,7 +328,6 @@ struct fec_enet_private {
 	int hwts_rx_en;
 	int hwts_tx_en;
 	struct timer_list time_keep;
-	struct fec_enet_delayed_work delay_work;
 	struct regulator *reg_phy;
 };
 
drivers/net/ethernet/freescale/fec_main.c +14 -20
index bf186cc54891..3de6634867a6 100644
@@ -740,31 +740,25 @@ fec_timeout(struct net_device *ndev)
 
 	ndev->stats.tx_errors++;
 
-	fep->delay_work.timeout = true;
-	schedule_delayed_work(&(fep->delay_work.delay_work), 0);
+	schedule_work(&fep->tx_timeout_work);
 }
 
-static void fec_enet_work(struct work_struct *work)
+static void fec_enet_timeout_work(struct work_struct *work)
 {
 	struct fec_enet_private *fep =
-		container_of(work,
-			     struct fec_enet_private,
-			     delay_work.delay_work.work);
+		container_of(work, struct fec_enet_private, tx_timeout_work);
 	struct net_device *ndev = fep->netdev;
 
-	if (fep->delay_work.timeout) {
-		fep->delay_work.timeout = false;
-		rtnl_lock();
-		if (netif_device_present(ndev) || netif_running(ndev)) {
-			napi_disable(&fep->napi);
-			netif_tx_lock_bh(ndev);
-			fec_restart(ndev);
-			netif_wake_queue(ndev);
-			netif_tx_unlock_bh(ndev);
-			napi_enable(&fep->napi);
-		}
-		rtnl_unlock();
+	rtnl_lock();
+	if (netif_device_present(ndev) || netif_running(ndev)) {
+		napi_disable(&fep->napi);
+		netif_tx_lock_bh(ndev);
+		fec_restart(ndev);
+		netif_wake_queue(ndev);
+		netif_tx_unlock_bh(ndev);
+		napi_enable(&fep->napi);
 	}
+	rtnl_unlock();
 }
 
 static void
@@ -2312,7 +2306,7 @@ fec_probe(struct platform_device *pdev)
 	if (fep->bufdesc_ex && fep->ptp_clock)
 		netdev_info(ndev, "registered PHC device %d\n", fep->dev_id);
 
-	INIT_DELAYED_WORK(&(fep->delay_work.delay_work), fec_enet_work);
+	INIT_WORK(&fep->tx_timeout_work, fec_enet_timeout_work);
 	return 0;
 
 failed_register:
@@ -2345,7 +2339,7 @@ fec_drv_remove(struct platform_device *pdev)
 	struct net_device *ndev = platform_get_drvdata(pdev);
 	struct fec_enet_private *fep = netdev_priv(ndev);
 
-	cancel_delayed_work_sync(&(fep->delay_work.delay_work));
+	cancel_work_sync(&fep->tx_timeout_work);
 	unregister_netdev(ndev);
 	fec_enet_mii_remove(fep);
 	del_timer_sync(&fep->time_keep);
Signature · 1 line
1.8.3.1
#103 lore raw

PATCH 101/222 net:fec: fix interrupt handling races

In reply to Russell King - ARM Linux

While running: while :; do iperf -c <HOST> -P 4; done, transmit timeouts are regularly reported. With the tx ring dumping in place, we can see that all entries are in use, and the hardware has finished transmitting these packets. However, the driver has not reclaimed these ring entries.

This can occur if the interrupt handler is invoked at the wrong moment - eg:

	CPU0				CPU1
	fec_enet_tx()
					interrupt, IEVENT = FEC_ENET_TXF
					FEC_ENET_TXF cleared
					napi_schedule_prep()
	napi_complete()

The result is that we clear the transmit interrupt, but we don't trigger any cleaning of the transmit ring. Instead, use a different strategy:

- When receiving a transmit or receive interrupt, disable both tx and rx interrupts, but do not acknowledge them. Schedule a napi poll. Don't loop.

- When we are polled, read IEVENT, acknowledging the pending transmit and receive interrupts, before then going on to process the appropriate rings.

This allows us to avoid the race, and has a number of other advantages:
- we cut down on the number of transmit interrupts we have to process.
- we only look at the rings which have pending events.
- we gain additional throughput: the iperf total bandwidth increases from about 180Mbps to 240Mbps:

[  3]  0.0-10.0 sec  68.1 MBytes  57.0 Mbits/sec
[  5]  0.0-10.0 sec  72.4 MBytes  60.5 Mbits/sec
[  4]  0.0-10.1 sec  76.1 MBytes  63.5 Mbits/sec
[  6]  0.0-10.1 sec  71.9 MBytes  59.9 Mbits/sec
[SUM]  0.0-10.1 sec   288 MBytes   241 Mbits/sec
---
 drivers/net/ethernet/freescale/fec_main.c | 40 +++++++++++++++++--------------
 1 file changed, 22 insertions(+), 18 deletions(-)
drivers/net/ethernet/freescale/fec_main.c +22 -18
index 3de6634867a6..0d1580ebaede 100644
@@ -1065,29 +1065,25 @@ fec_enet_interrupt(int irq, void *dev_id)
 {
 	struct net_device *ndev = dev_id;
 	struct fec_enet_private *fep = netdev_priv(ndev);
+	const unsigned napi_mask = FEC_ENET_RXF | FEC_ENET_TXF;
 	uint int_events;
 	irqreturn_t ret = IRQ_NONE;
 
-	do {
-		int_events = readl(fep->hwp + FEC_IEVENT);
-		writel(int_events, fep->hwp + FEC_IEVENT);
+	int_events = readl(fep->hwp + FEC_IEVENT);
+	writel(int_events & ~napi_mask, fep->hwp + FEC_IEVENT);
 
-		if (int_events & (FEC_ENET_RXF | FEC_ENET_TXF)) {
-			ret = IRQ_HANDLED;
+	if (int_events & napi_mask) {
+		ret = IRQ_HANDLED;
 
-			/* Disable the RX interrupt */
-			if (napi_schedule_prep(&fep->napi)) {
-				writel(FEC_RX_DISABLED_IMASK,
-					fep->hwp + FEC_IMASK);
-				__napi_schedule(&fep->napi);
-			}
-		}
+		/* Disable the NAPI interrupts */
+		writel(FEC_ENET_MII, fep->hwp + FEC_IMASK);
+		napi_schedule(&fep->napi);
+	}
 
-		if (int_events & FEC_ENET_MII) {
-			ret = IRQ_HANDLED;
-			complete(&fep->mdio_done);
-		}
-	} while (int_events);
+	if (int_events & FEC_ENET_MII) {
+		ret = IRQ_HANDLED;
+		complete(&fep->mdio_done);
+	}
 
 	return ret;
 }
@@ -1095,8 +1091,16 @@ fec_enet_interrupt(int irq, void *dev_id)
 static int fec_enet_rx_napi(struct napi_struct *napi, int budget)
 {
 	struct net_device *ndev = napi->dev;
-	int pkts = fec_enet_rx(ndev, budget);
 	struct fec_enet_private *fep = netdev_priv(ndev);
+	int pkts;
+
+	/*
+	 * Clear any pending transmit or receive interrupts before
+	 * processing the rings to avoid racing with the hardware.
+	 */
+	writel(FEC_ENET_RXF | FEC_ENET_TXF, fep->hwp + FEC_IEVENT);
+
+	pkts = fec_enet_rx(ndev, budget);
 
 	fec_enet_tx(ndev);
 
Signature · 1 line
1.8.3.1
#104 lore raw

PATCH 102/222 net:fec: clear receive interrupts before processing a packet

In reply to Russell King - ARM Linux

Clear any pending receive interrupt before we process a pending packet. This helps to avoid any spurious interrupts being raised after we have fully cleaned the receive ring, while still allowing an interrupt to be raised if we receive another packet.

The timing of this is critical: we must do this prior to reading the next packet status to avoid delaying a pending packet.

---
 drivers/net/ethernet/freescale/fec_main.c | 2 ++
 1 file changed, 2 insertions(+)
drivers/net/ethernet/freescale/fec_main.c +2 -0
index 0d1580ebaede..51b00c77bf00 100644
@@ -904,6 +904,8 @@ fec_enet_rx(struct net_device *ndev, int budget)
 		if ((status & BD_ENET_RX_LAST) == 0)
 			netdev_err(ndev, "rcv is not +last\n");
 
+		writel(FEC_ENET_RXF, fep->hwp + FEC_IEVENT);
+
 		/* Check for errors. */
 		if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO |
 			   BD_ENET_RX_CR | BD_ENET_RX_OV)) {
Signature · 1 line
1.8.3.1
#105 lore raw

PATCH 103/222 net:fec: remove useless status check in tx reap path

In reply to Russell King - ARM Linux

Remove a useless status check in the transmit reap path - we have already checked that the DB_ENET_TX_READY bit is clear, so there is no way this test can ever be true.

---
 drivers/net/ethernet/freescale/fec_main.c | 3 ---
 1 file changed, 3 deletions(-)
drivers/net/ethernet/freescale/fec_main.c +0 -3
index 51b00c77bf00..c7fe6eb8be12 100644
@@ -827,9 +827,6 @@ fec_enet_tx(struct net_device *ndev)
 			skb_tstamp_tx(skb, &shhwtstamps);
 		}
 
-		if (status & BD_ENET_TX_READY)
-			netdev_err(ndev, "HEY! Enet xmit interrupt and TX_READY\n");
-
 		/* Deferred means some collisions occurred during transmit,
 		 * but we eventually sent the packet OK.
 		 */
Signature · 1 line
1.8.3.1
#106 lore raw

PATCH 104/222 net:fec: use a union for the buffer descriptors

In reply to Russell King - ARM Linux

Using a union gives clearer C code than the existing solution, and allows the removal of some odd code from the receive path whose purpose was to merely store the enhanced buffer descriptor.

---
 drivers/net/ethernet/freescale/fec.h      |  13 +-
 drivers/net/ethernet/freescale/fec_main.c | 194 ++++++++++++++----------------
 2 files changed, 96 insertions(+), 111 deletions(-)
drivers/net/ethernet/freescale/fec.h +9 -4
index aecc46c33a82..30c683c0b480 100644
@@ -170,6 +170,11 @@ struct bufdesc_ex {
 	unsigned short res0[4];
 };
 
+union bufdesc_u {
+	struct bufdesc bd;
+	struct bufdesc_ex ebd;
+};
+
 /*
  *	The following definitions courtesy of commproc.h, which where
  *	Copyright (c) 1997 Dan Malek (dmalek at jlc.net).
@@ -283,12 +288,12 @@ struct fec_enet_private {
 	/* CPM dual port RAM relative addresses */
 	dma_addr_t	bd_dma;
 	/* Address of Rx and Tx buffers */
-	struct bufdesc	*rx_bd_base;
-	struct bufdesc	*tx_bd_base;
+	union bufdesc_u	*rx_bd_base;
+	union bufdesc_u	*tx_bd_base;
 	/* The next free ring entry */
-	struct bufdesc	*cur_rx, *cur_tx;
+	union bufdesc_u	*cur_rx, *cur_tx;
 	/* The ring entries to be free()ed */
-	struct bufdesc	*dirty_tx;
+	union bufdesc_u	*dirty_tx;
 
 	unsigned short tx_ring_size;
 	unsigned short rx_ring_size;
drivers/net/ethernet/freescale/fec_main.c +87 -107

478 lines not on this page · raw message

Signature · 1 line
1.8.3.1
#107 lore raw

PATCH 105/222 net:fec: consolidate hwtstamp implementation

In reply to Russell King - ARM Linux

Both transmit and receive use the same infrastructure for calculating the packet timestamp. Rather than duplicating the code, provide a function to do this common work. Model this function in the Intel e1000e version which avoids calling ns_to_ktime() within the spinlock; the spinlock is critical for timecounter_cyc2time() but not ns_to_ktime().

---
 drivers/net/ethernet/freescale/fec_main.c | 37 ++++++++++++++++---------------
 1 file changed, 19 insertions(+), 18 deletions(-)
drivers/net/ethernet/freescale/fec_main.c +19 -18
index b9d0f0c8cae1..01e43a75089d 100644
@@ -756,6 +756,21 @@ static void fec_enet_timeout_work(struct work_struct *work)
 }
 
 static void
+fec_enet_hwtstamp(struct fec_enet_private *fep, unsigned ts,
+	struct skb_shared_hwtstamps *hwtstamps)
+{
+	unsigned long flags;
+	u64 ns;
+
+	spin_lock_irqsave(&fep->tmreg_lock, flags);
+	ns = timecounter_cyc2time(&fep->tc, ts);
+	spin_unlock_irqrestore(&fep->tmreg_lock, flags);
+
+	memset(hwtstamps, 0, sizeof(*hwtstamps));
+	hwtstamps->hwtstamp = ns_to_ktime(ns);
+}
+
+static void
 fec_enet_tx(struct net_device *ndev)
 {
 	struct	fec_enet_private *fep;
@@ -809,13 +824,8 @@ fec_enet_tx(struct net_device *ndev)
 		if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) &&
 			fep->bufdesc_ex) {
 			struct skb_shared_hwtstamps shhwtstamps;
-			unsigned long flags;
 
-			memset(&shhwtstamps, 0, sizeof(shhwtstamps));
-			spin_lock_irqsave(&fep->tmreg_lock, flags);
-			shhwtstamps.hwtstamp = ns_to_ktime(
-				timecounter_cyc2time(&fep->tc, bdp->ebd.ts));
-			spin_unlock_irqrestore(&fep->tmreg_lock, flags);
+			fec_enet_hwtstamp(fep, bdp->ebd.ts, &shhwtstamps);
 			skb_tstamp_tx(skb, &shhwtstamps);
 		}
 
@@ -974,18 +984,9 @@ fec_enet_rx(struct net_device *ndev, int budget)
 			skb->protocol = eth_type_trans(skb, ndev);
 
 			/* Get receive timestamp from the skb */
-			if (fep->hwts_rx_en && fep->bufdesc_ex) {
-				struct skb_shared_hwtstamps *shhwtstamps =
-							    skb_hwtstamps(skb);
-				unsigned long flags;
-
-				memset(shhwtstamps, 0, sizeof(*shhwtstamps));
-
-				spin_lock_irqsave(&fep->tmreg_lock, flags);
-				shhwtstamps->hwtstamp = ns_to_ktime(
-				    timecounter_cyc2time(&fep->tc, bdp->ebd.ts));
-				spin_unlock_irqrestore(&fep->tmreg_lock, flags);
-			}
+			if (fep->hwts_rx_en && fep->bufdesc_ex)
+				fec_enet_hwtstamp(fep, bdp->ebd.ts,
+						  skb_hwtstamps(skb));
 
 			if (fep->bufdesc_ex &&
 			    (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) {
Signature · 1 line
1.8.3.1
#108 lore raw

PATCH 106/222 net:fec: better indexing for transmit descriptor ring

In reply to Russell King - ARM Linux

Maintaining the transmit ring position via pointers is inefficient, especially when it involves complex pointer manipulation, and overlap with the receive descriptor logic.

Re-implement this using indexes, and a single function which returns the descriptor (appropriate to the descriptor type). As an additional benefit, using a union allows cleaner access to the descriptor.

---
 drivers/net/ethernet/freescale/fec.h      |   6 +-
 drivers/net/ethernet/freescale/fec_main.c | 142 ++++++++++++++----------------
 2 files changed, 67 insertions(+), 81 deletions(-)
drivers/net/ethernet/freescale/fec.h +3 -3
index 30c683c0b480..c5a72c4bd07d 100644
@@ -291,9 +291,9 @@ struct fec_enet_private {
 	union bufdesc_u	*rx_bd_base;
 	union bufdesc_u	*tx_bd_base;
 	/* The next free ring entry */
-	union bufdesc_u	*cur_rx, *cur_tx;
-	/* The ring entries to be free()ed */
-	union bufdesc_u	*dirty_tx;
+	unsigned short tx_next;
+	unsigned short tx_dirty;
+	union bufdesc_u *cur_rx;
 
 	unsigned short tx_ring_size;
 	unsigned short rx_ring_size;
drivers/net/ethernet/freescale/fec_main.c +64 -78

267 lines not on this page · raw message

Signature · 1 line
1.8.3.1
#109 lore raw

PATCH 107/222 net:fec: better indexing for receive descriptor ring

In reply to Russell King - ARM Linux

Extend the previous commit to the receive descriptor ring as well. This gets rid of the two nextdesc/prevdesc functions, since we now just need to get the descriptor for an index instead.

---
 drivers/net/ethernet/freescale/fec.h      |  2 +-
 drivers/net/ethernet/freescale/fec_main.c | 98 +++++++++++--------------------
 2 files changed, 35 insertions(+), 65 deletions(-)
drivers/net/ethernet/freescale/fec.h +1 -1
index c5a72c4bd07d..4b92758b51b8 100644
@@ -293,7 +293,7 @@ struct fec_enet_private {
 	/* The next free ring entry */
 	unsigned short tx_next;
 	unsigned short tx_dirty;
-	union bufdesc_u *cur_rx;
+	unsigned short rx_next;
 
 	unsigned short tx_ring_size;
 	unsigned short rx_ring_size;
drivers/net/ethernet/freescale/fec_main.c +34 -64

200 lines not on this page · raw message

Signature · 1 line
1.8.3.1
#110 lore raw

PATCH 108/222 net:fec: calculate ring space rather than relying on comparing indices

In reply to Russell King - ARM Linux

Calculate the space in the transmit ring rather than comparing indices. This allows us to positively calculate the number of entries remaining, which will be required when we handle fragmented skbs.

---
 drivers/net/ethernet/freescale/fec_main.c | 32 +++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)
drivers/net/ethernet/freescale/fec_main.c +16 -16
index dba2e315f937..47d4aa2842a4 100644
@@ -316,6 +316,12 @@ fec_enet_tx_unmap(union bufdesc_u *bdp, struct fec_enet_private *fep)
 	dma_unmap_single(&fep->pdev->dev, addr, length, DMA_TO_DEVICE);
 }
 
+static unsigned ring_free(unsigned ins, unsigned rem, unsigned size)
+{
+	int num = rem - ins;
+	return num < 0 ? num + size : num;
+}
+
 static netdev_tx_t
 fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 {
@@ -331,11 +337,8 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 
 	/* Fill in a Tx ring entry */
 	index = fep->tx_next;
-	bdp = fec_enet_tx_get(index, fep);
-
-	status = bdp->bd.cbd_sc;
 
-	if (status & BD_ENET_TX_READY) {
+	if (ring_free(index, fep->tx_dirty, fep->tx_ring_size) < 1) {
 		/* Ooops.  All transmit buffers are full.  Bail out.
 		 * This should not happen, since ndev->tbusy should be set.
 		 */
@@ -349,9 +352,6 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 		return NETDEV_TX_OK;
 	}
 
-	/* Clear all of the status flags */
-	status &= ~BD_ENET_TX_STATS;
-
 	/* Set buffer length and buffer pointer */
 	bufaddr = skb->data;
 	length = skb->len;
@@ -386,6 +386,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 	/* Save skb pointer */
 	fep->tx_skbuff[index] = skb;
 
+	bdp = fec_enet_tx_get(index, fep);
 	bdp->bd.cbd_datlen = length;
 	bdp->bd.cbd_bufaddr = addr;
 
@@ -417,9 +418,9 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 	/* Send it on its way.  Tell FEC it's ready, interrupt when done,
 	 * it's the last BD of the frame, and to put the CRC on the end.
 	 */
-	status |= (BD_ENET_TX_READY | BD_ENET_TX_INTR
-			| BD_ENET_TX_LAST | BD_ENET_TX_TC);
-	bdp->bd.cbd_sc = status;
+	status = bdp->bd.cbd_sc & ~BD_ENET_TX_STATS;
+	bdp->bd.cbd_sc = status | BD_ENET_TX_READY | BD_ENET_TX_INTR |
+			 BD_ENET_TX_LAST | BD_ENET_TX_TC;
 
 	skb_tx_timestamp(skb);
 
@@ -428,7 +429,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 
 	fep->tx_next = index;
 
-	if (fep->tx_next == fep->tx_dirty)
+	if (ring_free(index, fep->tx_dirty, fep->tx_ring_size) < 1)
 		netif_stop_queue(ndev);
 
 	/* Trigger transmission start */
@@ -808,17 +809,16 @@ fec_enet_tx(struct net_device *ndev)
 		/* Free the sk buffer associated with this last transmit */
 		dev_kfree_skb_any(skb);
 
-		/* Since we have freed up a buffer, the ring is no longer full
-		 */
-		if (netif_queue_stopped(ndev))
-			netif_wake_queue(ndev);
-
 		fep->tx_dirty = index;
 	} while (1);
 
 	/* ERR006538: Keep the transmitter going */
 	if (index != fep->tx_next && readl(fep->hwp + FEC_X_DES_ACTIVE) == 0)
 		writel(0, fep->hwp + FEC_X_DES_ACTIVE);
+
+	if (netif_queue_stopped(ndev) &&
+	    ring_free(fep->tx_next, fep->tx_dirty, fep->tx_ring_size))
+		netif_wake_queue(ndev);
 }
 
 
Signature · 1 line
1.8.3.1
#111 lore raw

PATCH 109/222 net:fec: increase transmit ring size

In reply to Russell King - ARM Linux

Increasing the transmit ring size allows us to queue more packets before needing to stop the transmit queue.

---
 drivers/net/ethernet/freescale/fec.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/ethernet/freescale/fec.h +2 -2
index 4b92758b51b8..62fe1217b781 100644
@@ -251,8 +251,8 @@ union bufdesc_u {
 #define RX_RING_SIZE		(FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES)
 #define FEC_ENET_TX_FRSIZE	2048
 #define FEC_ENET_TX_FRPPG	(PAGE_SIZE / FEC_ENET_TX_FRSIZE)
-#define TX_RING_SIZE		16	/* Must be power of two */
-#define TX_RING_MOD_MASK	15	/*   for this to work */
+#define TX_RING_SIZE		64	/* Must be power of two */
+#define TX_RING_MOD_MASK	63	/*   for this to work */
 
 #define BD_ENET_RX_INT          0x00800000
 #define BD_ENET_RX_PTP          ((ushort)0x0400)
Signature · 1 line
1.8.3.1
#112 lore raw

PATCH 110/222 net:fec: increase receive ring size

In reply to Russell King - ARM Linux

Increasing the receive ring size allows more packets to be received before we run out of ring entries to store the packets. With 16 ring entries, this corresponds with a maximum of 16 * 1514 bytes, or just 23K. At gigabit speeds, this would take around 200us to fill. Increasing this to 64 entries gives us more scope when we have busy workloads, since it increases us to 95K, and around 700us.

Going above this requires the ring buffer allocation to be changed.

---
 drivers/net/ethernet/freescale/fec.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/freescale/fec.h +1 -1
index 62fe1217b781..1650c8440f5f 100644
@@ -245,7 +245,7 @@ union bufdesc_u {
  * the skbuffer directly.
  */
 
-#define FEC_ENET_RX_PAGES	8
+#define FEC_ENET_RX_PAGES	32
 #define FEC_ENET_RX_FRSIZE	2048
 #define FEC_ENET_RX_FRPPG	(PAGE_SIZE / FEC_ENET_RX_FRSIZE)
 #define RX_RING_SIZE		(FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES)
Signature · 1 line
1.8.3.1
#113 lore raw

PATCH 111/222 net:fec: add scatter-gather support

In reply to Russell King - ARM Linux

Add scatter-gather support for SKB transmission. This allows the driver to make use of GSO, which when enabled allows the iMX6Q to increase TCP transmission throughput from about 320 to 420Mbps, measured with iperf 2.0.5

We adjust the minimum transmit ring space according to whether SG support is enabled or not. This allows non-SG configurations to avoid the tx ring reservation necessary for SG, thereby making full use of their available ring (since non-SG requires just one tx ring entry per packet.)

---
 drivers/net/ethernet/freescale/fec.h      |   2 +
 drivers/net/ethernet/freescale/fec_main.c | 204 ++++++++++++++++++++++++------
 2 files changed, 164 insertions(+), 42 deletions(-)
drivers/net/ethernet/freescale/fec.h +2 -0
index 1650c8440f5f..aca92660d2be 100644
@@ -280,6 +280,7 @@ struct fec_enet_private {
 	struct clk *clk_enet_out;
 	struct clk *clk_ptp;
 
+	unsigned char tx_page_map[TX_RING_SIZE];
 	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
 	unsigned char *tx_bounce[TX_RING_SIZE];
 	struct	sk_buff *tx_skbuff[TX_RING_SIZE];
@@ -293,6 +294,7 @@ struct fec_enet_private {
 	/* The next free ring entry */
 	unsigned short tx_next;
 	unsigned short tx_dirty;
+	unsigned short tx_min;
 	unsigned short rx_next;
 
 	unsigned short tx_ring_size;
drivers/net/ethernet/freescale/fec_main.c +162 -42

353 lines not on this page · raw message

Signature · 1 line
1.8.3.1
#114 lore raw

PATCH 112/222 net:fec: add byte queue limits support

In reply to Russell King - ARM Linux

Add support for byte queue limits. Information on this feature can be found here: https://lwn.net/Articles/469652/

---
 drivers/net/ethernet/freescale/fec_main.c | 9 +++++++++
 1 file changed, 9 insertions(+)
drivers/net/ethernet/freescale/fec_main.c +9 -0
index 45733f9c3c85..c697b6ae9039 100644
@@ -499,6 +499,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 	}
 
 	skb_tx_timestamp(skb);
+	netdev_sent_queue(ndev, skb->len);
 
 	if (++last >= fep->tx_ring_size)
 		last = 0;
@@ -606,6 +607,7 @@ fec_restart(struct net_device *ndev)
 	writel(PKT_MAXBLR_SIZE, fep->hwp + FEC_R_BUFF_SIZE);
 
 	fec_enet_bd_init(ndev);
+	netdev_reset_queue(ndev);
 
 	/* Set receive and transmit descriptor base. */
 	writel(fep->bd_dma, fep->hwp + FEC_R_DES_START);
@@ -836,7 +838,9 @@ fec_enet_tx(struct net_device *ndev)
 	unsigned short status;
 	struct	sk_buff	*skb;
 	unsigned index = fep->tx_dirty;
+	unsigned pkts_compl, bytes_compl;
 
+	pkts_compl = bytes_compl = 0;
 	do {
 		if (++index >= fep->tx_ring_size)
 			index = 0;
@@ -891,6 +895,9 @@ fec_enet_tx(struct net_device *ndev)
 				skb_tstamp_tx(skb, &shhwtstamps);
 			}
 
+			pkts_compl++;
+			bytes_compl += skb->len;
+
 			/* Free the sk buffer associated with this last transmit */
 			dev_kfree_skb_any(skb);
 		}
@@ -898,6 +905,8 @@ fec_enet_tx(struct net_device *ndev)
 		fep->tx_dirty = index;
 	} while (1);
 
+	netdev_completed_queue(ndev, pkts_compl, bytes_compl);
+
 	/* ERR006538: Keep the transmitter going */
 	if (index != fep->tx_next && readl(fep->hwp + FEC_X_DES_ACTIVE) == 0)
 		writel(0, fep->hwp + FEC_X_DES_ACTIVE);
Signature · 1 line
1.8.3.1
#115 lore raw

PATCH 113/222 net:fec: add better flow control support

In reply to Russell King - ARM Linux

The FEC hardware in iMX6 is capable of separate control of each flow control path: the receiver can be programmed via the receive control register to detect flow control frames, and the transmitter can be programmed via the receive FIFO thresholds to enable generation of pause frames.

This means we can implement the full range of flow control: both symmetric and asymmetric flow control. We support ethtool configuring all options: forced manual mode, where each path can be controlled individually, and autonegotiation mode.

In autonegotiation mode, the tx/rx enable bits can be used to influence the outcome, though they don't precisely define which paths will be enabled. One combination we don't support is "symmetric only" since we can always configure each path independently, a case which Linux seems to often get wrong.

---
 drivers/net/ethernet/freescale/fec.h      |   3 +-
 drivers/net/ethernet/freescale/fec_main.c | 196 ++++++++++++++++++++++--------
 2 files changed, 146 insertions(+), 53 deletions(-)
drivers/net/ethernet/freescale/fec.h +2 -1
index aca92660d2be..af81d0f62dae 100644
@@ -316,7 +316,8 @@ struct fec_enet_private {
 	struct	completion mdio_done;
 	int	irq[FEC_IRQ_NUM];
 	int	bufdesc_ex;
-	int	pause_flag;
+	unsigned short pause_flag;
+	unsigned short pause_mode;
 
 	struct	napi_struct napi;
 	int	csum_flags;
drivers/net/ethernet/freescale/fec_main.c +144 -52

263 lines not on this page · raw message

Signature · 1 line
1.8.3.1
#116 lore raw

PATCH 114/222 net:fec: move transmit dma ring address calculation to fec_enet_init()

In reply to Russell King - ARM Linux

Move the calculation of the transmit DMA ring address to fec_enet_init() so the CPU and DMA ring address calculations are localised.

---
 drivers/net/ethernet/freescale/fec.h      |  3 ++-
 drivers/net/ethernet/freescale/fec_main.c | 24 ++++++++++++------------
 2 files changed, 14 insertions(+), 13 deletions(-)
drivers/net/ethernet/freescale/fec.h +2 -1
index af81d0f62dae..29f607a2d9b0 100644
@@ -287,7 +287,8 @@ struct fec_enet_private {
 	struct	sk_buff *rx_skbuff[RX_RING_SIZE];
 
 	/* CPM dual port RAM relative addresses */
-	dma_addr_t	bd_dma;
+	dma_addr_t	rx_bd_dma;
+	dma_addr_t	tx_bd_dma;
 	/* Address of Rx and Tx buffers */
 	union bufdesc_u	*rx_bd_base;
 	union bufdesc_u	*tx_bd_base;
drivers/net/ethernet/freescale/fec_main.c +12 -12
index fb01d7891b67..96e74ef0c4d6 100644
@@ -611,14 +611,8 @@ fec_restart(struct net_device *ndev)
 	netdev_reset_queue(ndev);
 
 	/* Set receive and transmit descriptor base. */
-	writel(fep->bd_dma, fep->hwp + FEC_R_DES_START);
-	if (fep->bufdesc_ex)
-		writel((unsigned long)fep->bd_dma + sizeof(struct bufdesc_ex)
-			* fep->rx_ring_size, fep->hwp + FEC_X_DES_START);
-	else
-		writel((unsigned long)fep->bd_dma + sizeof(struct bufdesc)
-			* fep->rx_ring_size,	fep->hwp + FEC_X_DES_START);
-
+	writel(fep->rx_bd_dma, fep->hwp + FEC_R_DES_START);
+	writel(fep->tx_bd_dma, fep->hwp + FEC_X_DES_START);
 
 	for (i = 0; i <= TX_RING_MOD_MASK; i++) {
 		if (fep->tx_skbuff[i]) {
@@ -2208,10 +2202,10 @@ static int fec_enet_init(struct net_device *ndev)
 	const struct platform_device_id *id_entry =
 				platform_get_device_id(fep->pdev);
 	union bufdesc_u *cbd_base;
+	dma_addr_t cbd_dma;
 
 	/* Allocate memory for buffer descriptors. */
-	cbd_base = dma_alloc_coherent(NULL, PAGE_SIZE, &fep->bd_dma,
-				      GFP_KERNEL);
+	cbd_base = dma_alloc_coherent(NULL, PAGE_SIZE, &cbd_dma, GFP_KERNEL);
 	if (!cbd_base)
 		return -ENOMEM;
 
@@ -2230,12 +2224,18 @@ static int fec_enet_init(struct net_device *ndev)
 
 	/* Set receive and transmit descriptor base. */
 	fep->rx_bd_base = cbd_base;
-	if (fep->bufdesc_ex)
+	fep->rx_bd_dma = cbd_dma;
+	if (fep->bufdesc_ex) {
 		fep->tx_bd_base = (union bufdesc_u *)
 			(&cbd_base->ebd + fep->rx_ring_size);
-	else
+		fep->tx_bd_dma = cbd_dma + sizeof(struct bufdesc_ex) *
+			fep->rx_ring_size;
+	} else {
 		fep->tx_bd_base = (union bufdesc_u *)
 			(&cbd_base->bd + fep->rx_ring_size);
+		fep->tx_bd_dma = cbd_dma + sizeof(struct bufdesc) *
+			fep->rx_ring_size;
+	}
 
 	/* The FEC Ethernet specific entries in the device structure */
 	ndev->watchdog_timeo = TX_TIMEOUT;
Signature · 1 line
1.8.3.1
#117 lore raw

PATCH 115/222 net:fec: allocate descriptor rings at device open, and free on device close

In reply to Russell King - ARM Linux

The driver has had a memory leak for quite some time: when the device is probed, we allocate a page for the descriptor rings, but we never free this.

Rather than trying to free it on the various probe failure paths, move its allocation to device open time - we have to restart the FEC for this to take effect. This also means we can free the descriptor rings when we clean up in the device close function, which gives this a nice symmetry.

---
 drivers/net/ethernet/freescale/fec_main.c | 67 +++++++++++++++----------------
 1 file changed, 32 insertions(+), 35 deletions(-)
drivers/net/ethernet/freescale/fec_main.c +32 -35
index 96e74ef0c4d6..9471abb189bc 100644
@@ -607,7 +607,8 @@ fec_restart(struct net_device *ndev)
 	/* Set maximum receive buffer size. */
 	writel(PKT_MAXBLR_SIZE, fep->hwp + FEC_R_BUFF_SIZE);
 
-	fec_enet_bd_init(ndev);
+	if (fep->rx_bd_base)
+		fec_enet_bd_init(ndev);
 	netdev_reset_queue(ndev);
 
 	/* Set receive and transmit descriptor base. */
@@ -1889,6 +1890,8 @@ static void fec_enet_free_buffers(struct net_device *ndev)
 		if (skb)
 			dev_kfree_skb(skb);
 	}
+
+	dma_free_coherent(NULL, PAGE_SIZE, fep->rx_bd_base, fep->rx_bd_dma);
 }
 
 static int fec_enet_alloc_buffers(struct net_device *ndev)
@@ -1897,6 +1900,30 @@ static int fec_enet_alloc_buffers(struct net_device *ndev)
 	unsigned int i;
 	struct sk_buff *skb;
 	union bufdesc_u *bdp;
+	union bufdesc_u *cbd_base;
+	dma_addr_t cbd_dma;
+
+	/* Allocate memory for buffer descriptors. */
+	cbd_base = dma_alloc_coherent(NULL, PAGE_SIZE, &cbd_dma, GFP_KERNEL);
+	if (!cbd_base)
+		return -ENOMEM;
+
+	memset(cbd_base, 0, PAGE_SIZE);
+
+	/* Set receive and transmit descriptor base. */
+	fep->rx_bd_base = cbd_base;
+	fep->rx_bd_dma = cbd_dma;
+	if (fep->bufdesc_ex) {
+		fep->tx_bd_base = (union bufdesc_u *)
+			(&cbd_base->ebd + fep->rx_ring_size);
+		fep->tx_bd_dma = cbd_dma + sizeof(struct bufdesc_ex) *
+			fep->rx_ring_size;
+	} else {
+		fep->tx_bd_base = (union bufdesc_u *)
+			(&cbd_base->bd + fep->rx_ring_size);
+		fep->tx_bd_dma = cbd_dma + sizeof(struct bufdesc) *
+			fep->rx_ring_size;
+	}
 
 	for (i = 0; i < fep->rx_ring_size; i++) {
 		dma_addr_t addr;
@@ -2192,24 +2219,11 @@ static const struct net_device_ops fec_netdev_ops = {
 	.ndo_set_features	= fec_set_features,
 };
 
- /*
-  * XXX:  We need to clean up on failure exits here.
-  *
-  */
-static int fec_enet_init(struct net_device *ndev)
+static void fec_enet_init(struct net_device *ndev)
 {
 	struct fec_enet_private *fep = netdev_priv(ndev);
 	const struct platform_device_id *id_entry =
 				platform_get_device_id(fep->pdev);
-	union bufdesc_u *cbd_base;
-	dma_addr_t cbd_dma;
-
-	/* Allocate memory for buffer descriptors. */
-	cbd_base = dma_alloc_coherent(NULL, PAGE_SIZE, &cbd_dma, GFP_KERNEL);
-	if (!cbd_base)
-		return -ENOMEM;
-
-	memset(cbd_base, 0, PAGE_SIZE);
 
 	fep->netdev = ndev;
 
@@ -2222,20 +2236,8 @@ static int fec_enet_init(struct net_device *ndev)
 	fep->tx_ring_size = TX_RING_SIZE;
 	fep->rx_ring_size = RX_RING_SIZE;
 
-	/* Set receive and transmit descriptor base. */
-	fep->rx_bd_base = cbd_base;
-	fep->rx_bd_dma = cbd_dma;
-	if (fep->bufdesc_ex) {
-		fep->tx_bd_base = (union bufdesc_u *)
-			(&cbd_base->ebd + fep->rx_ring_size);
-		fep->tx_bd_dma = cbd_dma + sizeof(struct bufdesc_ex) *
-			fep->rx_ring_size;
-	} else {
-		fep->tx_bd_base = (union bufdesc_u *)
-			(&cbd_base->bd + fep->rx_ring_size);
-		fep->tx_bd_dma = cbd_dma + sizeof(struct bufdesc) *
-			fep->rx_ring_size;
-	}
+	fep->rx_bd_base = fep->tx_bd_base = NULL;
+	fep->rx_bd_dma = fep->tx_bd_dma = 0;
 
 	/* The FEC Ethernet specific entries in the device structure */
 	ndev->watchdog_timeo = TX_TIMEOUT;
@@ -2272,8 +2274,6 @@ static int fec_enet_init(struct net_device *ndev)
 		fep->tx_min = 1;
 
 	fec_restart(ndev);
-
-	return 0;
 }
 
 #ifdef CONFIG_OF
@@ -2435,9 +2435,7 @@ fec_probe(struct platform_device *pdev)
 	if (fep->bufdesc_ex)
 		fec_ptp_init(pdev);
 
-	ret = fec_enet_init(ndev);
-	if (ret)
-		goto failed_init;
+	fec_enet_init(ndev);
 
 	for (i = 0; i < FEC_IRQ_NUM; i++) {
 		irq = platform_get_irq(pdev, i);
@@ -2474,7 +2472,6 @@ fec_probe(struct platform_device *pdev)
 	fec_enet_mii_remove(fep);
 failed_mii_init:
 failed_irq:
-failed_init:
 	if (fep->reg_phy)
 		regulator_disable(fep->reg_phy);
 failed_regulator:
Signature · 1 line
1.8.3.1
#118 lore raw

PATCH 116/222 net:fec: remove unnecessary code

In reply to Russell King - ARM Linux

fec_enet_bd_init() already frees the transmit skbuffs, so there's no need for fec_restart() to do this again.

---
 drivers/net/ethernet/freescale/fec.h      | 1 -
 drivers/net/ethernet/freescale/fec_main.c | 8 --------
 2 files changed, 9 deletions(-)
drivers/net/ethernet/freescale/fec.h +0 -1
index 29f607a2d9b0..70ef32c965e5 100644
@@ -252,7 +252,6 @@ union bufdesc_u {
 #define FEC_ENET_TX_FRSIZE	2048
 #define FEC_ENET_TX_FRPPG	(PAGE_SIZE / FEC_ENET_TX_FRSIZE)
 #define TX_RING_SIZE		64	/* Must be power of two */
-#define TX_RING_MOD_MASK	63	/*   for this to work */
 
 #define BD_ENET_RX_INT          0x00800000
 #define BD_ENET_RX_PTP          ((ushort)0x0400)
drivers/net/ethernet/freescale/fec_main.c +0 -8
index 9471abb189bc..40ab7b7ccacf 100644
@@ -581,7 +581,6 @@ fec_restart(struct net_device *ndev)
 	struct fec_enet_private *fep = netdev_priv(ndev);
 	const struct platform_device_id *id_entry =
 				platform_get_device_id(fep->pdev);
-	int i;
 	u32 val;
 	u32 temp_mac[2];
 	u32 rcntl = OPT_FRAME_SIZE | 0x04;
@@ -615,13 +614,6 @@ fec_restart(struct net_device *ndev)
 	writel(fep->rx_bd_dma, fep->hwp + FEC_R_DES_START);
 	writel(fep->tx_bd_dma, fep->hwp + FEC_X_DES_START);
 
-	for (i = 0; i <= TX_RING_MOD_MASK; i++) {
-		if (fep->tx_skbuff[i]) {
-			dev_kfree_skb_any(fep->tx_skbuff[i]);
-			fep->tx_skbuff[i] = NULL;
-		}
-	}
-
 	/* Enable MII mode */
 	if (fep->full_duplex == DUPLEX_FULL) {
 		/* FD enable */
Signature · 1 line
1.8.3.1
#119 lore raw

PATCH 117/222 net:fec: consolidate common parts of mdio read/write

In reply to Russell King - ARM Linux

There is a commonality to fec_enet_mdio_read() and fec_enet_mdio_write() which can be factored out. Factor that commonality out, since we need to add some locking to prevent resets interfering with MDIO accesses.

---
 drivers/net/ethernet/freescale/fec_main.c | 41 +++++++++++++------------------
 1 file changed, 17 insertions(+), 24 deletions(-)
drivers/net/ethernet/freescale/fec_main.c +17 -24
index 40ab7b7ccacf..f6763686d5a9 100644
@@ -1305,23 +1305,27 @@ static void fec_enet_adjust_link(struct net_device *ndev)
 		phy_print_status(phy_dev);
 }
 
-static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+static unsigned long fec_enet_mdio_op(struct fec_enet_private *fep,
+	unsigned data)
 {
-	struct fec_enet_private *fep = bus->priv;
-	unsigned long time_left;
-
 	fep->mii_timeout = 0;
 	init_completion(&fep->mdio_done);
 
-	/* start a read op */
-	writel(FEC_MMFR_ST | FEC_MMFR_OP_READ |
-		FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
-		FEC_MMFR_TA, fep->hwp + FEC_MII_DATA);
+	/* start operation */
+	writel(data, fep->hwp + FEC_MII_DATA);
 
 	/* wait for end of transfer */
-	time_left = wait_for_completion_timeout(&fep->mdio_done,
+	return wait_for_completion_timeout(&fep->mdio_done,
 			usecs_to_jiffies(FEC_MII_TIMEOUT));
-	if (time_left == 0) {
+}
+
+static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+	struct fec_enet_private *fep = bus->priv;
+
+	if (fec_enet_mdio_op(fep, FEC_MMFR_ST | FEC_MMFR_OP_READ |
+				  FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
+				  FEC_MMFR_TA) == 0) {
 		fep->mii_timeout = 1;
 		netdev_err(fep->netdev, "MDIO read timeout\n");
 		return -ETIMEDOUT;
@@ -1335,21 +1339,10 @@ static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
 			   u16 value)
 {
 	struct fec_enet_private *fep = bus->priv;
-	unsigned long time_left;
-
-	fep->mii_timeout = 0;
-	init_completion(&fep->mdio_done);
 
-	/* start a write op */
-	writel(FEC_MMFR_ST | FEC_MMFR_OP_WRITE |
-		FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
-		FEC_MMFR_TA | FEC_MMFR_DATA(value),
-		fep->hwp + FEC_MII_DATA);
-
-	/* wait for end of transfer */
-	time_left = wait_for_completion_timeout(&fep->mdio_done,
-			usecs_to_jiffies(FEC_MII_TIMEOUT));
-	if (time_left == 0) {
+	if (fec_enet_mdio_op(fep, FEC_MMFR_ST | FEC_MMFR_OP_WRITE |
+				  FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
+				  FEC_MMFR_TA | FEC_MMFR_DATA(value)) == 0) {
 		fep->mii_timeout = 1;
 		netdev_err(fep->netdev, "MDIO write timeout\n");
 		return -ETIMEDOUT;
Signature · 1 line
1.8.3.1
#120 lore raw

PATCH 118/222 net:fec: add locking for MDIO bus access

In reply to Russell King - ARM Linux

Both fec_restart() and fec_stop() both perform a reset of the FEC. This reset can result in an in-process MDIO transfer being terminated, which can lead to the MDIO read/write functions timing out. Add some locking to prevent this occuring.

We can't use a spinlock for this as the MDIO accessor functions use wait_for_completion_timeout(), which sleeps.

---
 drivers/net/ethernet/freescale/fec.h      |  3 +++
 drivers/net/ethernet/freescale/fec_main.c | 30 +++++++++++++++++++++++++++++-
 2 files changed, 32 insertions(+), 1 deletion(-)
drivers/net/ethernet/freescale/fec.h +3 -0
index 70ef32c965e5..11717d2fd520 100644
@@ -14,6 +14,7 @@
 /****************************************************************************/
 
 #include <linux/clocksource.h>
+#include <linux/mutex.h>
 #include <linux/net_tstamp.h>
 #include <linux/ptp_clock_kernel.h>
 
@@ -300,6 +301,8 @@ struct fec_enet_private {
 	unsigned short tx_ring_size;
 	unsigned short rx_ring_size;
 
+	struct mutex mutex;
+
 	struct	platform_device *pdev;
 
 	int	dev_id;
drivers/net/ethernet/freescale/fec_main.c +29 -1
index f6763686d5a9..afbcd7f14951 100644
@@ -795,12 +795,14 @@ static void fec_enet_timeout_work(struct work_struct *work)
 
 	rtnl_lock();
 	if (netif_device_present(ndev) || netif_running(ndev)) {
+		mutex_lock(&fep->mutex);
 		napi_disable(&fep->napi);
 		netif_tx_lock_bh(ndev);
 		fec_restart(ndev);
 		netif_wake_queue(ndev);
 		netif_tx_unlock_bh(ndev);
 		napi_enable(&fep->napi);
+		mutex_unlock(&fep->mutex);
 	}
 	rtnl_unlock();
 }
@@ -1282,20 +1284,24 @@ static void fec_enet_adjust_link(struct net_device *ndev)
 
 		/* if any of the above changed restart the FEC */
 		if (status_change) {
+			mutex_lock(&fep->mutex);
 			napi_disable(&fep->napi);
 			netif_tx_lock_bh(ndev);
 			fec_restart(ndev);
 			netif_wake_queue(ndev);
 			netif_tx_unlock_bh(ndev);
 			napi_enable(&fep->napi);
+			mutex_unlock(&fep->mutex);
 		}
 	} else {
 		if (fep->link) {
+			mutex_lock(&fep->mutex);
 			napi_disable(&fep->napi);
 			netif_tx_lock_bh(ndev);
 			fec_stop(ndev);
 			netif_tx_unlock_bh(ndev);
 			napi_enable(&fep->napi);
+			mutex_unlock(&fep->mutex);
 			fep->link = phy_dev->link;
 			status_change = 1;
 		}
@@ -1308,15 +1314,23 @@ static void fec_enet_adjust_link(struct net_device *ndev)
 static unsigned long fec_enet_mdio_op(struct fec_enet_private *fep,
 	unsigned data)
 {
+	unsigned long time_left;
+
 	fep->mii_timeout = 0;
 	init_completion(&fep->mdio_done);
 
+	mutex_lock(&fep->mutex);
+
 	/* start operation */
 	writel(data, fep->hwp + FEC_MII_DATA);
 
 	/* wait for end of transfer */
-	return wait_for_completion_timeout(&fep->mdio_done,
+	time_left = wait_for_completion_timeout(&fep->mdio_done,
 			usecs_to_jiffies(FEC_MII_TIMEOUT));
+
+	mutex_unlock(&fep->mutex);
+
+	return time_left;
 }
 
 static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
@@ -1683,6 +1697,7 @@ static int fec_enet_set_pauseparam(struct net_device *ndev,
 			fep->pause_mode = fep->pause_flag;
 
 			if (netif_running(ndev)) {
+				mutex_lock(&fep->mutex);
 				napi_disable(&fep->napi);
 				netif_tx_lock_bh(ndev);
 				fec_stop(ndev);
@@ -1690,6 +1705,7 @@ static int fec_enet_set_pauseparam(struct net_device *ndev,
 				netif_wake_queue(ndev);
 				netif_tx_unlock_bh(ndev);
 				napi_enable(&fep->napi);
+				mutex_unlock(&fep->mutex);
 			}
 		}
 	}
@@ -1983,7 +1999,9 @@ fec_enet_open(struct net_device *ndev)
 		return ret;
 	}
 
+	mutex_lock(&fep->mutex);
 	fec_restart(ndev);
+	mutex_unlock(&fep->mutex);
 	napi_enable(&fep->napi);
 	phy_start(fep->phy_dev);
 	netif_start_queue(ndev);
@@ -2000,7 +2018,9 @@ fec_enet_close(struct net_device *ndev)
 	if (netif_device_present(ndev)) {
 		napi_disable(&fep->napi);
 		netif_tx_disable(ndev);
+		mutex_lock(&fep->mutex);
 		fec_stop(ndev);
+		mutex_unlock(&fep->mutex);
 	}
 
 	phy_disconnect(fep->phy_dev);
@@ -2155,6 +2175,7 @@ static int fec_set_features(struct net_device *netdev,
 
 	/* Quiesce the device if necessary */
 	if (netif_running(netdev) && changed & FEATURES_NEED_QUIESCE) {
+		mutex_lock(&fep->mutex);
 		napi_disable(&fep->napi);
 		netif_tx_lock_bh(netdev);
 		fec_stop(netdev);
@@ -2182,6 +2203,7 @@ static int fec_set_features(struct net_device *netdev,
 		netif_wake_queue(netdev);
 		netif_tx_unlock_bh(netdev);
 		napi_enable(&fep->napi);
+		mutex_unlock(&fep->mutex);
 	}
 
 	return 0;
@@ -2324,6 +2346,8 @@ fec_probe(struct platform_device *pdev)
 	/* setup board info structure */
 	fep = netdev_priv(ndev);
 
+	mutex_init(&fep->mutex);
+
 #if !defined(CONFIG_M5272)
 	/* default enable pause frame auto negotiation */
 	if (pdev->id_entry &&
@@ -2515,7 +2539,9 @@ fec_suspend(struct device *dev)
 		netif_tx_lock_bh(ndev);
 		netif_device_detach(ndev);
 		netif_tx_unlock_bh(ndev);
+		mutex_lock(&fep->mutex);
 		fec_stop(ndev);
+		mutex_unlock(&fep->mutex);
 	}
 	rtnl_unlock();
 
@@ -2567,7 +2593,9 @@ fec_resume(struct device *dev)
 
 	rtnl_lock();
 	if (netif_running(ndev)) {
+		mutex_lock(&fep->mutex);
 		fec_restart(ndev);
+		mutex_unlock(&fep->mutex);
 		netif_tx_lock_bh(ndev);
 		netif_device_attach(ndev);
 		netif_tx_unlock_bh(ndev);
Signature · 1 line
1.8.3.1
#121 lore raw

PATCH 119/222 net:fec: move bufdesc_ex flag into flags

In reply to Russell King - ARM Linux

Add a new flags field to contain the mostly static driver configuration, the first of which is the bufdesc_ex flag.

---
 drivers/net/ethernet/freescale/fec.h      |  3 +-
 drivers/net/ethernet/freescale/fec_main.c | 48 +++++++++++++++++--------------
 2 files changed, 28 insertions(+), 23 deletions(-)
drivers/net/ethernet/freescale/fec.h +2 -1
index 11717d2fd520..a5d38e4c770c 100644
@@ -301,6 +301,8 @@ struct fec_enet_private {
 	unsigned short tx_ring_size;
 	unsigned short rx_ring_size;
 
+	unsigned char flags;
+
 	struct mutex mutex;
 
 	struct	platform_device *pdev;
@@ -318,7 +320,6 @@ struct fec_enet_private {
 	int	speed;
 	struct	completion mdio_done;
 	int	irq[FEC_IRQ_NUM];
-	int	bufdesc_ex;
 	unsigned short pause_flag;
 	unsigned short pause_mode;
 
drivers/net/ethernet/freescale/fec_main.c +26 -22

177 lines not on this page · raw message

Signature · 1 line
1.8.3.1
#122 lore raw

PATCH 120/222 net:fec: move receive checksum flags into new flags member

In reply to Russell King - ARM Linux

The receive checksum flag is mostly static, so move it into the flags member. In any case, this is checked alongside the BUFDESC_EX flag.

---
 drivers/net/ethernet/freescale/fec.h      |  2 --
 drivers/net/ethernet/freescale/fec_main.c | 11 ++++++-----
 2 files changed, 6 insertions(+), 7 deletions(-)
drivers/net/ethernet/freescale/fec.h +0 -2
index a5d38e4c770c..aaf5aaaf9e56 100644
@@ -258,7 +258,6 @@ union bufdesc_u {
 #define BD_ENET_RX_PTP          ((ushort)0x0400)
 #define BD_ENET_RX_ICE		0x00000020
 #define BD_ENET_RX_PCR		0x00000010
-#define FLAG_RX_CSUM_ENABLED	(BD_ENET_RX_ICE | BD_ENET_RX_PCR)
 #define FLAG_RX_CSUM_ERROR	(BD_ENET_RX_ICE | BD_ENET_RX_PCR)
 
 /* The FEC buffer descriptors track the ring buffers.  The rx_bd_base and
@@ -324,7 +323,6 @@ struct fec_enet_private {
 	unsigned short pause_mode;
 
 	struct	napi_struct napi;
-	int	csum_flags;
 
 	struct work_struct tx_timeout_work;
 
drivers/net/ethernet/freescale/fec_main.c +6 -5
index c35a1e00129c..81ade95c06e0 100644
@@ -232,6 +232,7 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
 
 /* flags */
 #define FEC_FLAG_BUFDESC_EX	BIT(0)
+#define FEC_FLAG_RX_CSUM	BIT(1)
 
 static int mii_cnt;
 
@@ -634,7 +635,7 @@ fec_restart(struct net_device *ndev)
 #if !defined(CONFIG_M5272)
 	/* set RX checksum */
 	val = readl(fep->hwp + FEC_RACC);
-	if (fep->csum_flags & FLAG_RX_CSUM_ENABLED)
+	if (fep->flags & FEC_FLAG_RX_CSUM)
 		val |= FEC_RACC_OPTIONS;
 	else
 		val &= ~FEC_RACC_OPTIONS;
@@ -1042,7 +1043,7 @@ fec_enet_rx(struct net_device *ndev, int budget)
 						  skb_hwtstamps(skb));
 
 			if (fep->flags & FEC_FLAG_BUFDESC_EX &&
-			    (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) {
+			    fep->flags & FEC_FLAG_RX_CSUM) {
 				if (!(bdp->ebd.cbd_esc & FLAG_RX_CSUM_ERROR)) {
 					/* don't check it */
 					skb->ip_summed = CHECKSUM_UNNECESSARY;
@@ -2190,9 +2191,9 @@ static int fec_set_features(struct net_device *netdev,
 	/* Receive checksum has been changed */
 	if (changed & NETIF_F_RXCSUM) {
 		if (features & NETIF_F_RXCSUM)
-			fep->csum_flags |= FLAG_RX_CSUM_ENABLED;
+			fep->flags |= FEC_FLAG_RX_CSUM;
 		else
-			fep->csum_flags &= ~FLAG_RX_CSUM_ENABLED;
+			fep->flags &= ~FEC_FLAG_RX_CSUM;
 	}
 
 	/* Set the appropriate minimum transmit ring free threshold */
@@ -2270,7 +2271,7 @@ static void fec_enet_init(struct net_device *ndev)
 				| NETIF_F_RXCSUM);
 		ndev->hw_features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM
 				| NETIF_F_RXCSUM);
-		fep->csum_flags |= FLAG_RX_CSUM_ENABLED;
+		fep->flags |= FEC_FLAG_RX_CSUM;
 	}
 
 	if (!(id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)) {
Signature · 1 line
1.8.3.1
#123 lore raw

PATCH 121/222 net:fec: only enable CTAG_RX and IP checksumming if we have enhanced buffer descriptors

In reply to Russell King - ARM Linux

Only set the vlan tag and ip checksumming options if we have enhanced buffer descriptors - enhanced descriptors are required for these features.

---
 drivers/net/ethernet/freescale/fec_main.c | 28 +++++++++++++++-------------
 1 file changed, 15 insertions(+), 13 deletions(-)
drivers/net/ethernet/freescale/fec_main.c +15 -13
index 81ade95c06e0..7cb852991bf1 100644
@@ -2259,19 +2259,21 @@ static void fec_enet_init(struct net_device *ndev)
 	writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK);
 	netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, NAPI_POLL_WEIGHT);
 
-	if (id_entry->driver_data & FEC_QUIRK_HAS_VLAN) {
-		/* enable hw VLAN support */
-		ndev->features |= NETIF_F_HW_VLAN_CTAG_RX;
-		ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX;
-	}
-
-	if (id_entry->driver_data & FEC_QUIRK_HAS_CSUM) {
-		/* enable hw accelerator */
-		ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM
-				| NETIF_F_RXCSUM);
-		ndev->hw_features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM
-				| NETIF_F_RXCSUM);
-		fep->flags |= FEC_FLAG_RX_CSUM;
+	if (fep->flags & FEC_FLAG_BUFDESC_EX) {
+		if (id_entry->driver_data & FEC_QUIRK_HAS_VLAN) {
+			/* enable hw VLAN support */
+			ndev->features |= NETIF_F_HW_VLAN_CTAG_RX;
+			ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX;
+		}
+
+		if (id_entry->driver_data & FEC_QUIRK_HAS_CSUM) {
+			/* enable hw accelerator */
+			ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM
+					| NETIF_F_RXCSUM);
+			ndev->hw_features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM
+					| NETIF_F_RXCSUM);
+			fep->flags |= FEC_FLAG_RX_CSUM;
+		}
 	}
 
 	if (!(id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)) {
Signature · 1 line
1.8.3.1
#124 lore raw

PATCH 122/222 net:fec: avoid checking more flags than necessary in rx path

In reply to Russell King - ARM Linux

Avoid checking for both the receive checksumming flag and the enhanced buffer descriptor flags - we now only set receive checksumming if we know we have enhanced buffer descriptors.

---
 drivers/net/ethernet/freescale/fec_main.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/net/ethernet/freescale/fec_main.c +1 -2
index 7cb852991bf1..7f05866acee3 100644
@@ -1042,8 +1042,7 @@ fec_enet_rx(struct net_device *ndev, int budget)
 				fec_enet_hwtstamp(fep, bdp->ebd.ts,
 						  skb_hwtstamps(skb));
 
-			if (fep->flags & FEC_FLAG_BUFDESC_EX &&
-			    fep->flags & FEC_FLAG_RX_CSUM) {
+			if (fep->flags & FEC_FLAG_RX_CSUM) {
 				if (!(bdp->ebd.cbd_esc & FLAG_RX_CSUM_ERROR)) {
 					/* don't check it */
 					skb->ip_summed = CHECKSUM_UNNECESSARY;
Signature · 1 line
1.8.3.1
#125 lore raw

PATCH 123/222 net:fec: move vlan receive flag into our private flags

In reply to Russell King - ARM Linux
---
 drivers/net/ethernet/freescale/fec_main.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)
drivers/net/ethernet/freescale/fec_main.c +11 -2
index 7f05866acee3..11176d483db4 100644
@@ -233,6 +233,7 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
 /* flags */
 #define FEC_FLAG_BUFDESC_EX	BIT(0)
 #define FEC_FLAG_RX_CSUM	BIT(1)
+#define FEC_FLAG_RX_VLAN	BIT(2)
 
 static int mii_cnt;
 
@@ -1002,8 +1003,8 @@ fec_enet_rx(struct net_device *ndev, int budget)
 
 		/* If this is a VLAN packet remove the VLAN Tag */
 		vlan_packet_rcvd = false;
-		if ((ndev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
-		    fep->flags & FEC_FLAG_BUFDESC_EX && (bdp->ebd.cbd_esc & BD_ENET_RX_VLAN)) {
+		if (fep->flags & FEC_FLAG_RX_VLAN &&
+		    bdp->ebd.cbd_esc & BD_ENET_RX_VLAN) {
 			/* Push and remove the vlan tag */
 			struct vlan_hdr *vlan_header =
 					(struct vlan_hdr *) (data + ETH_HLEN);
@@ -2195,6 +2196,13 @@ static int fec_set_features(struct net_device *netdev,
 			fep->flags &= ~FEC_FLAG_RX_CSUM;
 	}
 
+	if (changed & NETIF_F_HW_VLAN_CTAG_RX) {
+		if (features & NETIF_F_HW_VLAN_CTAG_RX)
+			fep->flags |= FEC_FLAG_RX_VLAN;
+		else
+			fep->flags &= ~FEC_FLAG_RX_VLAN;
+	}
+
 	/* Set the appropriate minimum transmit ring free threshold */
 	if (features & NETIF_F_SG)
 		fep->tx_min = MAX_SKB_FRAGS + 1;
@@ -2263,6 +2271,7 @@ static void fec_enet_init(struct net_device *ndev)
 			/* enable hw VLAN support */
 			ndev->features |= NETIF_F_HW_VLAN_CTAG_RX;
 			ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX;
+			fep->flags |= FEC_FLAG_RX_VLAN;
 		}
 
 		if (id_entry->driver_data & FEC_QUIRK_HAS_CSUM) {
Signature · 1 line
1.8.3.1
#126 lore raw

PATCH 124/222 net:fec: add ethtool support for tx/rx ring sizes

In reply to Russell King - ARM Linux

Add ethtool support to retrieve and set the transmit and receive ring sizes. This allows runtime tuning of these parameters, which can result in better throughput with gigabit parts.

---
 drivers/net/ethernet/freescale/fec_main.c | 47 +++++++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)
drivers/net/ethernet/freescale/fec_main.c +47 -0
index 11176d483db4..12d48b997744 100644
@@ -1631,6 +1631,51 @@ static int fec_enet_get_ts_info(struct net_device *ndev,
 	}
 }
 
+static void fec_enet_get_ringparam(struct net_device *ndev,
+				   struct ethtool_ringparam *ring)
+{
+	struct fec_enet_private *fep = netdev_priv(ndev);
+
+	ring->rx_max_pending = RX_RING_SIZE;
+	ring->tx_max_pending = TX_RING_SIZE;
+	ring->rx_pending = fep->rx_ring_size;
+	ring->tx_pending = fep->tx_ring_size;
+}
+
+static int fec_enet_set_ringparam(struct net_device *ndev,
+				   struct ethtool_ringparam *ring)
+{
+	struct fec_enet_private *fep = netdev_priv(ndev);
+	unsigned rx, tx, tx_min;
+
+	tx_min = ndev->features & NETIF_F_SG ? TX_RING_SIZE_MIN_SG : 16;
+
+	rx = clamp_t(u32, ring->rx_pending, 16, RX_RING_SIZE);
+	tx = clamp_t(u32, ring->tx_pending, tx_min, TX_RING_SIZE);
+
+	if (tx == fep->tx_ring_size && rx == fep->rx_ring_size)
+		return 0;
+
+	/* Setting the ring size while the interface is down is easy */
+	if (!netif_running(ndev)) {
+		fep->tx_ring_size = tx;
+		fep->rx_ring_size = rx;
+	} else {
+		return -EINVAL;
+
+		napi_disable(&fep->napi);
+		netif_tx_lock_bh(ndev);
+		fec_stop(ndev);
+		/* reallocate ring */
+		fec_restart(ndev);
+		netif_wake_queue(ndev);
+		netif_tx_unlock_bh(ndev);
+		napi_enable(&fep->napi);
+	}
+
+	return 0;
+}
+
 #if !defined(CONFIG_M5272)
 
 static void fec_enet_get_pauseparam(struct net_device *ndev,
@@ -1835,6 +1880,8 @@ static const struct ethtool_ops fec_enet_ethtool_ops = {
 	.get_drvinfo		= fec_enet_get_drvinfo,
 	.nway_reset		= fec_enet_nway_reset,
 	.get_link		= ethtool_op_get_link,
+	.get_ringparam		= fec_enet_get_ringparam,
+	.set_ringparam		= fec_enet_set_ringparam,
 #ifndef CONFIG_M5272
 	.get_pauseparam		= fec_enet_get_pauseparam,
 	.set_pauseparam		= fec_enet_set_pauseparam,
Signature · 1 line
1.8.3.1
#127 lore raw

PATCH 125/222 net:fec: even larger rings

In reply to Russell King - ARM Linux
---
 drivers/net/ethernet/freescale/fec.h      |  4 +--
 drivers/net/ethernet/freescale/fec_main.c | 45 ++++++++++++++++---------------
 2 files changed, 26 insertions(+), 23 deletions(-)
drivers/net/ethernet/freescale/fec.h +2 -2
index aaf5aaaf9e56..bfcab01131cc 100644
@@ -246,13 +246,13 @@ union bufdesc_u {
  * the skbuffer directly.
  */
 
-#define FEC_ENET_RX_PAGES	32
+#define FEC_ENET_RX_PAGES	64
 #define FEC_ENET_RX_FRSIZE	2048
 #define FEC_ENET_RX_FRPPG	(PAGE_SIZE / FEC_ENET_RX_FRSIZE)
 #define RX_RING_SIZE		(FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES)
 #define FEC_ENET_TX_FRSIZE	2048
 #define FEC_ENET_TX_FRPPG	(PAGE_SIZE / FEC_ENET_TX_FRSIZE)
-#define TX_RING_SIZE		64	/* Must be power of two */
+#define TX_RING_SIZE		128	/* Must be power of two */
 
 #define BD_ENET_RX_INT          0x00800000
 #define BD_ENET_RX_PTP          ((ushort)0x0400)
drivers/net/ethernet/freescale/fec_main.c +24 -21

69 lines not on this page · raw message

Signature · 1 line
1.8.3.1
#128 lore raw

PATCH 126/222 net:fec: unsorted hacks

In reply to Russell King - ARM Linux
---
 drivers/net/ethernet/freescale/fec.h      |  16 +++-
 drivers/net/ethernet/freescale/fec_main.c | 126 +++++++++++++++++-------------
 2 files changed, 85 insertions(+), 57 deletions(-)
drivers/net/ethernet/freescale/fec.h +12 -4
index bfcab01131cc..09bdf6fea8ea 100644
@@ -208,6 +208,7 @@ union bufdesc_u {
 #define BD_ENET_RX_OV           ((ushort)0x0002)
 #define BD_ENET_RX_CL           ((ushort)0x0001)
 #define BD_ENET_RX_STATS        ((ushort)0x013f)        /* All status bits */
+#define BD_ENET_RX_ERROR	((ushort)0x003f)
 
 /* Enhanced buffer descriptor control/status used by Ethernet receive */
 #define BD_ENET_RX_VLAN         0x00000004
@@ -230,10 +231,17 @@ union bufdesc_u {
 #define BD_ENET_TX_STATS        ((ushort)0x03ff)        /* All status bits */
 
 /*enhanced buffer descriptor control/status used by Ethernet transmit*/
-#define BD_ENET_TX_INT          0x40000000
-#define BD_ENET_TX_TS           0x20000000
-#define BD_ENET_TX_PINS         0x10000000
-#define BD_ENET_TX_IINS         0x08000000
+#define BD_ENET_TX_INT          BIT(30)
+#define BD_ENET_TX_TS           BIT(29)
+#define BD_ENET_TX_PINS         BIT(28)
+#define BD_ENET_TX_IINS         BIT(27)
+#define BD_ENET_TX_TXE		BIT(15)
+#define BD_ENET_TX_UE		BIT(13)
+#define BD_ENET_TX_EE		BIT(12)
+#define BD_ENET_TX_FE		BIT(11)
+#define BD_ENET_TX_LCE		BIT(10)
+#define BD_ENET_TX_OE		BIT(9)
+#define BD_ENET_TX_TSE		BIT(8)
 
 
 /* This device has up to three irqs on some platforms */
drivers/net/ethernet/freescale/fec_main.c +73 -53

207 lines not on this page · raw message

Signature · 1 line
1.8.3.1
#129 lore raw

PATCH 127/222 leds: leds-pwm: provide a common function to setup a single led-pwm device

In reply to Russell King - ARM Linux

Provide a common function to setup a single led-pwm device, replacing the platform data initialisation path with this function. This allows us to have a common method of creating these devices in a consistent manner, which then allows us to place the probe failure cleanup in one place.

---
 drivers/leds/leds-pwm.c | 85 ++++++++++++++++++++++++++-----------------------
 1 file changed, 46 insertions(+), 39 deletions(-)
drivers/leds/leds-pwm.c +46 -39
index 7d0aaed1e23a..203e332967b2 100644
@@ -92,6 +92,44 @@ static void led_pwm_cleanup(struct led_pwm_priv *priv)
 	}
 }
 
+static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
+		       struct led_pwm *led)
+{
+	struct led_pwm_data *led_data = &priv->leds[priv->num_leds];
+	int ret;
+
+	led_data->active_low = led->active_low;
+	led_data->period = led->pwm_period_ns;
+	led_data->cdev.name = led->name;
+	led_data->cdev.default_trigger = led->default_trigger;
+	led_data->cdev.brightness_set = led_pwm_set;
+	led_data->cdev.brightness = LED_OFF;
+	led_data->cdev.max_brightness = led->max_brightness;
+	led_data->cdev.flags = LED_CORE_SUSPENDRESUME;
+
+	led_data->pwm = devm_pwm_get(dev, led->name);
+	if (IS_ERR(led_data->pwm)) {
+		ret = PTR_ERR(led_data->pwm);
+		dev_err(dev, "unable to request PWM for %s: %d\n",
+			led->name, ret);
+		return ret;
+	}
+
+	led_data->can_sleep = pwm_can_sleep(led_data->pwm);
+	if (led_data->can_sleep)
+		INIT_WORK(&led_data->work, led_pwm_work);
+
+	ret = led_classdev_register(dev, &led_data->cdev);
+	if (ret == 0) {
+		priv->num_leds++;
+	} else {
+		dev_err(dev, "failed to register PWM led for %s: %d\n",
+			led->name, ret);
+	}
+
+	return ret;
+}
+
 static int led_pwm_create_of(struct platform_device *pdev,
 			     struct led_pwm_priv *priv)
 {
@@ -139,8 +177,6 @@ static int led_pwm_create_of(struct platform_device *pdev,
 
 	return 0;
 err:
-	led_pwm_cleanup(priv);
-
 	return ret;
 }
 
@@ -166,51 +202,22 @@ static int led_pwm_probe(struct platform_device *pdev)
 
 	if (pdata) {
 		for (i = 0; i < count; i++) {
-			struct led_pwm *cur_led = &pdata->leds[i];
-			struct led_pwm_data *led_dat = &priv->leds[i];
-
-			led_dat->pwm = devm_pwm_get(&pdev->dev, cur_led->name);
-			if (IS_ERR(led_dat->pwm)) {
-				ret = PTR_ERR(led_dat->pwm);
-				dev_err(&pdev->dev,
-					"unable to request PWM for %s\n",
-					cur_led->name);
-				goto err;
-			}
-
-			led_dat->cdev.name = cur_led->name;
-			led_dat->cdev.default_trigger = cur_led->default_trigger;
-			led_dat->active_low = cur_led->active_low;
-			led_dat->period = cur_led->pwm_period_ns;
-			led_dat->cdev.brightness_set = led_pwm_set;
-			led_dat->cdev.brightness = LED_OFF;
-			led_dat->cdev.max_brightness = cur_led->max_brightness;
-			led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
-
-			led_dat->can_sleep = pwm_can_sleep(led_dat->pwm);
-			if (led_dat->can_sleep)
-				INIT_WORK(&led_dat->work, led_pwm_work);
-
-			ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
-			if (ret < 0)
-				goto err;
+			ret = led_pwm_add(&pdev->dev, priv, &pdata->leds[i]);
+			if (ret)
+				break;
 		}
-		priv->num_leds = count;
 	} else {
 		ret = led_pwm_create_of(pdev, priv);
-		if (ret)
-			return ret;
+	}
+
+	if (ret) {
+		led_pwm_cleanup(priv);
+		return ret;
 	}
 
 	platform_set_drvdata(pdev, priv);
 
 	return 0;
-
-err:
-	priv->num_leds = i;
-	led_pwm_cleanup(priv);
-
-	return ret;
 }
 
 static int led_pwm_remove(struct platform_device *pdev)
Signature · 1 line
1.8.3.1
#130 lore raw

PATCH 128/222 leds: leds-pwm: convert OF parsing code to use led_pwm_add()

In reply to Russell King - ARM Linux

Convert the OF parsing code to use the common PWM LED registration code, which means we have a consistent method, and single point where the registration happens for both paths.

---
 drivers/leds/leds-pwm.c | 62 ++++++++++++++++++-------------------------------
 1 file changed, 23 insertions(+), 39 deletions(-)
drivers/leds/leds-pwm.c +23 -39
index 203e332967b2..35c1c5f144dc 100644
@@ -93,7 +93,7 @@ static void led_pwm_cleanup(struct led_pwm_priv *priv)
 }
 
 static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
-		       struct led_pwm *led)
+		       struct led_pwm *led, struct device_node *child)
 {
 	struct led_pwm_data *led_data = &priv->leds[priv->num_leds];
 	int ret;
@@ -107,7 +107,10 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
 	led_data->cdev.max_brightness = led->max_brightness;
 	led_data->cdev.flags = LED_CORE_SUSPENDRESUME;
 
-	led_data->pwm = devm_pwm_get(dev, led->name);
+	if (child)
+		led_data->pwm = devm_of_pwm_get(dev, child, NULL);
+	else
+		led_data->pwm = devm_pwm_get(dev, led->name);
 	if (IS_ERR(led_data->pwm)) {
 		ret = PTR_ERR(led_data->pwm);
 		dev_err(dev, "unable to request PWM for %s: %d\n",
@@ -115,6 +118,9 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
 		return ret;
 	}
 
+	if (child)
+		led_data->period = pwm_get_period(led_data->pwm);
+
 	led_data->can_sleep = pwm_can_sleep(led_data->pwm);
 	if (led_data->can_sleep)
 		INIT_WORK(&led_data->work, led_pwm_work);
@@ -130,53 +136,30 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
 	return ret;
 }
 
-static int led_pwm_create_of(struct platform_device *pdev,
-			     struct led_pwm_priv *priv)
+static int led_pwm_create_of(struct device *dev, struct led_pwm_priv *priv)
 {
 	struct device_node *child;
-	int ret;
-
-	for_each_child_of_node(pdev->dev.of_node, child) {
-		struct led_pwm_data *led_dat = &priv->leds[priv->num_leds];
+	struct led_pwm led;
+	int ret = 0;
 
-		led_dat->cdev.name = of_get_property(child, "label",
-						     NULL) ? : child->name;
+	memset(&led, 0, sizeof(led));
 
-		led_dat->pwm = devm_of_pwm_get(&pdev->dev, child, NULL);
-		if (IS_ERR(led_dat->pwm)) {
-			dev_err(&pdev->dev, "unable to request PWM for %s\n",
-				led_dat->cdev.name);
-			ret = PTR_ERR(led_dat->pwm);
-			goto err;
-		}
-		/* Get the period from PWM core when n*/
-		led_dat->period = pwm_get_period(led_dat->pwm);
+	for_each_child_of_node(dev->of_node, child) {
+		led.name = of_get_property(child, "label", NULL) ? :
+			   child->name;
 
-		led_dat->cdev.default_trigger = of_get_property(child,
+		led.default_trigger = of_get_property(child,
 						"linux,default-trigger", NULL);
 		of_property_read_u32(child, "max-brightness",
-				     &led_dat->cdev.max_brightness);
-
-		led_dat->cdev.brightness_set = led_pwm_set;
-		led_dat->cdev.brightness = LED_OFF;
-		led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
-
-		led_dat->can_sleep = pwm_can_sleep(led_dat->pwm);
-		if (led_dat->can_sleep)
-			INIT_WORK(&led_dat->work, led_pwm_work);
+				     &led.max_brightness);
 
-		ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
-		if (ret < 0) {
-			dev_err(&pdev->dev, "failed to register for %s\n",
-				led_dat->cdev.name);
+		ret = led_pwm_add(dev, priv, &led, child);
+		if (ret) {
 			of_node_put(child);
-			goto err;
+			break;
 		}
-		priv->num_leds++;
 	}
 
-	return 0;
-err:
 	return ret;
 }
 
@@ -202,12 +185,13 @@ static int led_pwm_probe(struct platform_device *pdev)
 
 	if (pdata) {
 		for (i = 0; i < count; i++) {
-			ret = led_pwm_add(&pdev->dev, priv, &pdata->leds[i]);
+			ret = led_pwm_add(&pdev->dev, priv, &pdata->leds[i],
+					  NULL);
 			if (ret)
 				break;
 		}
 	} else {
-		ret = led_pwm_create_of(pdev, priv);
+		ret = led_pwm_create_of(&pdev->dev, priv);
 	}
 
 	if (ret) {
Signature · 1 line
1.8.3.1
#131 lore raw

PATCH 129/222 leds: leds-pwm: implement PWM inversion

In reply to Russell King - ARM Linux

Some PWM outputs are wired such that the LED they're controlling is connected to supply rather than ground. These PWMs may not support output inversion, or when they do, disabling the PWM may set the PWM output low, causing a "brightness" value of zero to turn the LED fully on.

The platform data for this driver already indicates that this was thought about, and we have the "active_low" property there already. However, the implementation for this is missing.

Add the trivial implementation for this feature.

---
 drivers/leds/leds-pwm.c | 4 ++++
 1 file changed, 4 insertions(+)
drivers/leds/leds-pwm.c +4 -0
index 35c1c5f144dc..8bd225df14de 100644
@@ -69,6 +69,10 @@ static void led_pwm_set(struct led_classdev *led_cdev,
 
 	duty *= brightness;
 	do_div(duty, max);
+
+	if (led_dat->active_low)
+		duty = led_dat->period - duty;
+
 	led_dat->duty = duty;
 
 	if (led_dat->can_sleep)
Signature · 1 line
1.8.3.1
#132 lore raw

PATCH 130/222 leds: leds-pwm: add DT support for LEDs wired to supply

In reply to Russell King - ARM Linux

The non-DT driver allowed an active low property to be specified, but DT is missing this in its description. Add the property to the DT binding document, making it optional. It defaults to active high, which retains compatibility with existing descriptions.

This should only be used for causes where the LED is wired to supply, and the PWM does not sensibly support its own inversion.

---
 Documentation/devicetree/bindings/leds/leds-pwm.txt | 2 ++
 drivers/leds/leds-pwm.c                             | 1 +
 2 files changed, 3 insertions(+)
Documentation/devicetree/bindings/leds/leds-pwm.txt +2 -0
index 7297107cf832..6c6583c35f2f 100644
@@ -13,6 +13,8 @@ node's name represents the name of the corresponding LED.
   For the pwms and pwm-names property please refer to:
   Documentation/devicetree/bindings/pwm/pwm.txt
 - max-brightness : Maximum brightness possible for the LED
+- active-low : (optional) For PWMs where the LED is wired to supply
+  rather than ground.
 - label :  (optional)
   see Documentation/devicetree/bindings/leds/common.txt
 - linux,default-trigger :  (optional)
drivers/leds/leds-pwm.c +1 -0
index 8bd225df14de..f5cf1b0f2748 100644
@@ -154,6 +154,7 @@ static int led_pwm_create_of(struct device *dev, struct led_pwm_priv *priv)
 
 		led.default_trigger = of_get_property(child,
 						"linux,default-trigger", NULL);
+		led.active_low = of_property_read_bool(child, "active-low");
 		of_property_read_u32(child, "max-brightness",
 				     &led.max_brightness);
 
Signature · 1 line
1.8.3.1
#133 lore raw

PATCH 131/222 ARM: add support for Cubox-i PWM-driven front panel LED

In reply to Russell King - ARM Linux

The front panel LED on the Cubox-i is driven by one of the iMX6 PWM channels, and is wired between the PWM output and supply.

---
 arch/arm/boot/dts/imx6qdl-cubox-i.dtsi | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)
arch/arm/boot/dts/imx6qdl-cubox-i.dtsi +17 -0
index c2a24888a276..c17602d4e4aa 100644
@@ -12,6 +12,19 @@
 		pinctrl-0 = <&pinctrl_cubox_i_ir>;
 	};
 
+	pwmleds {
+		compatible = "pwm-leds";
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_cubox_i_pwm1>;
+
+		front {
+			active-low;
+			label = "imx6:red:front";
+			max-brightness = <248>;
+			pwms = <&pwm1 0 50000>;
+		};
+	};
+
 	regulators {
 		compatible = "simple-bus";
 
@@ -82,6 +95,10 @@
 			>;
 		};
 
+		pinctrl_cubox_i_pwm1: cubox-i-pwm1-front-led {
+			fsl,pins = <MX6QDL_PAD_DISP0_DAT8__PWM1_OUT 0x1b0b0>;
+		};
+
 		pinctrl_cubox_i_spdif: cubox-i-spdif {
 			fsl,pins = <MX6QDL_PAD_GPIO_17__SPDIF_OUT 0x13091>;
 		};
Signature · 1 line
1.8.3.1
#134 lore raw

PATCH 132/222 ASoC: work around block transfer issues with imx-pcm-dma

In reply to Russell King - ARM Linux
---
 sound/soc/fsl/imx-pcm-dma.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
sound/soc/fsl/imx-pcm-dma.c +1 -1
index 2585ae44e634..a86d84854c7f 100644
@@ -44,7 +44,7 @@ static const struct snd_pcm_hardware imx_pcm_hardware = {
 	.buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
 	.period_bytes_min = 128,
 	.period_bytes_max = 65535, /* Limited by SDMA engine */
-	.periods_min = 2,
+	.periods_min = 4,
 	.periods_max = 255,
 	.fifo_size = 0,
 };
Signature · 1 line
1.8.3.1
#135 lore raw

PATCH 133/222 ata: ahci_imx: warn when disabling ahci link

In reply to Russell King - ARM Linux

When the AHCI link is disabled, it can't be re-enabled except by resetting the entire SoC. Rather than doing this silently print some kernel messages to inform the user, along with how to avoid this.

---
 drivers/ata/ahci_imx.c | 4 ++++
 1 file changed, 4 insertions(+)
drivers/ata/ahci_imx.c +4 -0
index 497c7abe1c7d..6a56a561a80b 100644
@@ -160,6 +160,10 @@ static void ahci_imx_error_handler(struct ata_port *ap)
 	writel(reg_val | PORT_PHY_CTL_PDDQ_LOC, mmio + PORT_PHY_CTL);
 	imx_sata_disable(hpriv);
 	imxpriv->no_device = true;
+
+	dev_info(ap->dev, "no device found, disabling link.\n");
+	dev_info(ap->dev, "pass " MODULE_PARAM_PREFIX
+		 ".hotplug=1 to enable hotplug\n");
 }
 
 static int ahci_imx_softreset(struct ata_link *link, unsigned int *class,
Signature · 1 line
1.8.3.1
#136 lore raw

PATCH 134/222 ARM: imx: keep PLLs in bypass while they're locking

In reply to Russell King - ARM Linux

Keep the PLL bypassed while we prepare a clock by powering up the PLL, otherwise we can end up with improper output/gitches which prevents further operation.

---
 arch/arm/mach-imx/clk-pllv3.c | 27 +++++++++++++++++++++------
 1 file changed, 21 insertions(+), 6 deletions(-)
arch/arm/mach-imx/clk-pllv3.c +21 -6
index 61364050fccd..3776f974d1dc 100644
@@ -273,9 +273,10 @@ static int clk_pllv3_av_set_rate(struct clk_hw *hw, unsigned long rate,
 	struct clk_pllv3 *pll = to_clk_pllv3(hw);
 	unsigned long min_rate = parent_rate * 27;
 	unsigned long max_rate = parent_rate * 54;
-	u32 val, div;
+	u32 val, newval, div;
 	u32 mfn, mfd = 1000000;
 	s64 temp64;
+	int ret;
 
 	if (rate < min_rate || rate > max_rate)
 		return -EINVAL;
@@ -287,13 +288,27 @@ static int clk_pllv3_av_set_rate(struct clk_hw *hw, unsigned long rate,
 	mfn = temp64;
 
 	val = readl_relaxed(pll->base);
-	val &= ~pll->div_mask;
-	val |= div;
-	writel_relaxed(val, pll->base);
+
+	/* set the PLL into bypass mode */
+	newval = val | BM_PLL_BYPASS;
+	writel_relaxed(newval, pll->base);
+
+	/* configure the new frequency */
+	newval &= ~pll->div_mask;
+	newval |= div;
+	writel_relaxed(newval, pll->base);
 	writel_relaxed(mfn, pll->base + PLL_NUM_OFFSET);
-	writel_relaxed(mfd, pll->base + PLL_DENOM_OFFSET);
+	writel(mfd, pll->base + PLL_DENOM_OFFSET);
 
-	return clk_pllv3_wait_lock(pll);
+	ret = clk_pllv3_wait_lock(pll);
+	if (ret == 0 && val & BM_PLL_POWER) {
+		/* only if it locked can we switch back to the PLL */
+		newval &= ~BM_PLL_BYPASS;
+		newval |= val & BM_PLL_BYPASS;
+		writel(newval, pll->base);
+	}
+
+	return ret;
 }
 
 static const struct clk_ops clk_pllv3_av_ops = {
Signature · 1 line
1.8.3.1
#137 lore raw

PATCH 134/222 ARM: imx: keep PLLs in bypass while they're locking

Quoted 38 lines · Russell King

Keep the PLL bypassed while we prepare a clock by powering up the PLL, otherwise we can end up with improper output/gitches which prevents further operation.

---
  arch/arm/mach-imx/clk-pllv3.c | 27 +++++++++++++++++++++------
  1 file changed, 21 insertions(+), 6 deletions(-)
arch/arm/mach-imx/clk-pllv3.c +8 -4
index 61364050fccd..3776f974d1dc 100644
@@ -273,9 +273,10 @@ static int clk_pllv3_av_set_rate(struct clk_hw *hw, unsigned long rate,
  	struct clk_pllv3 *pll = to_clk_pllv3(hw);
  	unsigned long min_rate = parent_rate * 27;
  	unsigned long max_rate = parent_rate * 54;
-	u32 val, div;
+	u32 val, newval, div;
  	u32 mfn, mfd = 1000000;
  	s64 temp64;
+	int ret;
 
  	if (rate < min_rate || rate > max_rate)
  		return -EINVAL;
@@ -287,13 +288,27 @@ static int clk_pllv3_av_set_rate(struct clk_hw *hw, unsigned long rate,
  	mfn = temp64;
 
  	val = readl_relaxed(pll->base);
-	val &= ~pll->div_mask;
-	val |= div;
-	writel_relaxed(val, pll->base);
+
+	/* set the PLL into bypass mode */
+	newval = val | BM_PLL_BYPASS;
+	writel_relaxed(newval, pll->base);
+
+	/* configure the new frequency */

arch/arm/mach-imx/clk-pllv3.c static int clk_pllv3_av_set_rate(struct clk_hw *hw, unsigned long rate, · line 304

Quoted 10 lines
arch/arm/mach-imx/clk-pllv3.c +6 -2
+	newval &= ~pll->div_mask;
+	newval |= div;
+	writel_relaxed(newval, pll->base);
  	writel_relaxed(mfn, pll->base + PLL_NUM_OFFSET);
-	writel_relaxed(mfd, pll->base + PLL_DENOM_OFFSET);
+	writel(mfd, pll->base + PLL_DENOM_OFFSET);
 
-	return clk_pllv3_wait_lock(pll);
+	ret = clk_pllv3_wait_lock(pll);
+	if (ret == 0 && val & BM_PLL_POWER) {

I'm no expert on this, so sorry if I'm wrong ;)

But regarding the check 'val & BM_PLL_POWER' here:

Reading the manual, it seems that both PLL4_AUDIO & PLL5_VIDEO don't have a POWER bit, instead they have a POWERDOWN at bit 12. So for PLL4_AUDIO & PLL5_VIDEO 'val & BM_PLL_POWER' would mean not powered up?

Maybe the check should be done anywhere else, e.g. in clk_pllv3_set_rate()?

Best regards

Dirk

#138 lore raw

PATCH 134/222 ARM: imx: keep PLLs in bypass while they're locking

Besides the question below regarding 'val & BM_PLL_POWER', three additional questions:

Quoted 4 lines · Dirk Behme
Quoted 3 lines · Russell King

Keep the PLL bypassed while we prepare a clock by powering up the PLL, otherwise we can end up with improper output/gitches which prevents further operation.

Have you seen a real world case to show this patch fixes an issue in real? Have you been seeing anything like "improper output/gitches"?

Quoted 11 lines

arch/arm/mach-imx/clk-pllv3.c static int clk_pllv3_av_set_rate(struct clk_hw

Quoted 11 lines
---
  arch/arm/mach-imx/clk-pllv3.c | 27 +++++++++++++++++++++------
  1 file changed, 21 insertions(+), 6 deletions(-)
arch/arm/mach-imx/clk-pllv3.c +0 -0
index 61364050fccd..3776f974d1dc 100644
@@ -273,9 +273,10 @@ static int clk_pllv3_av_set_rate(struct clk_hw

We touch here pllv3/av only, while the commit message generically says "keep PLLs in bypass while they're locking" nothing specific to audio/video PLL clock. So isn't similar modification needed for the other set_rate() functions as well?

Quoted 16 lines
Quoted 16 lines
arch/arm/mach-imx/clk-pllv3.c +2 -1
 *hw, unsigned long rate,
      struct clk_pllv3 *pll = to_clk_pllv3(hw);
      unsigned long min_rate = parent_rate * 27;
      unsigned long max_rate = parent_rate * 54;
-    u32 val, div;
+    u32 val, newval, div;
      u32 mfn, mfd = 1000000;
      s64 temp64;
+    int ret;
 
      if (rate < min_rate || rate > max_rate)
          return -EINVAL;
arch/arm/mach-imx/clk-pllv3.c +0 -0
@@ -287,13 +288,27 @@ static int clk_pllv3_av_set_rate(struct clk_hw
 *hw, unsigned long rate,
      mfn = temp64;
 
Quoted 10 lines

arch/arm/mach-imx/clk-pllv3.c static int clk_pllv3_av_set_rate(struct clk_hw *hw, unsigned long rate, · line 296

Quoted 10 lines
arch/arm/mach-imx/clk-pllv3.c +6 -3
      val = readl_relaxed(pll->base);
-    val &= ~pll->div_mask;
-    val |= div;
-    writel_relaxed(val, pll->base);
+
+    /* set the PLL into bypass mode */
+    newval = val | BM_PLL_BYPASS;
+    writel_relaxed(newval, pll->base);
+
+    /* configure the new frequency */

Reading the i.MX6 reference manual section '18.5.1.5.3 PLL clock change': 'Before changing the PLL setting, power it down. Power up the PLL after the change.' So we need to power down/up the PLL rather than just bypass for a rate change?

Quoted 13 lines

arch/arm/mach-imx/clk-pllv3.c static int clk_pllv3_av_set_rate(struct clk_hw *hw, unsigned long rate, · line 304

Quoted 10 lines
arch/arm/mach-imx/clk-pllv3.c +6 -2
+    newval &= ~pll->div_mask;
+    newval |= div;
+    writel_relaxed(newval, pll->base);
      writel_relaxed(mfn, pll->base + PLL_NUM_OFFSET);
-    writel_relaxed(mfd, pll->base + PLL_DENOM_OFFSET);
+    writel(mfd, pll->base + PLL_DENOM_OFFSET);
 
-    return clk_pllv3_wait_lock(pll);
+    ret = clk_pllv3_wait_lock(pll);
+    if (ret == 0 && val & BM_PLL_POWER) {

I'm no expert on this, so sorry if I'm wrong ;)

Quoted 7 lines

But regarding the check 'val & BM_PLL_POWER' here:

Reading the manual, it seems that both PLL4_AUDIO & PLL5_VIDEO don't have a POWER bit, instead they have a POWERDOWN at bit 12. So for PLL4_AUDIO & PLL5_VIDEO 'val & BM_PLL_POWER' would mean not powered up?

Maybe the check should be done anywhere else, e.g. in clk_pllv3_set_rate()?

Best regards

Dirk

#139 lore raw

PATCH 135/222 ARM: imx: set IPU to be derived from the 540MHz PLL3 PFD1 clock

In reply to Russell King - ARM Linux

Set the IPU muxer to derive the clock from the PLL3 FPD1 clock, which increases its clock rate from 198MHz to 270MHz.

---
 arch/arm/mach-imx/clk-imx6q.c | 4 ++++
 1 file changed, 4 insertions(+)
arch/arm/mach-imx/clk-imx6q.c +4 -0
index b0e7f9d2c245..f4d0b0e3e041 100644
@@ -445,6 +445,10 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
 		clk_set_parent(clk[ldb_di1_sel], clk[pll5_video_div]);
 	}
 
+	if (cpu_is_imx6dl()) {
+		clk_set_parent(clk[ipu1_sel], clk[pll3_pfd1_540m]);
+	}
+
 	/*
 	 * The gpmi needs 100MHz frequency in the EDO/Sync mode,
 	 * We can not get the 100MHz from the pll2_pfd0_352m.
Signature · 1 line
1.8.3.1
#140 lore raw

PATCH 136/222 regulator: allow GPIO 0 to be used for an enable signal

In reply to Russell King - ARM Linux

GPIO number 0 *is* legal and must be accepted.

---
 drivers/regulator/anatop-regulator.c | 1 +
 drivers/regulator/core.c             | 2 +-
 drivers/regulator/dummy.c            | 1 +
 drivers/regulator/fixed.c            | 4 +---
 4 files changed, 4 insertions(+), 4 deletions(-)
drivers/regulator/anatop-regulator.c +1 -0
index 7c397bb81e01..6da2fbbb9bd5 100644
@@ -267,6 +267,7 @@ static int anatop_regulator_probe(struct platform_device *pdev)
 	config.driver_data = sreg;
 	config.of_node = pdev->dev.of_node;
 	config.regmap = sreg->anatop;
+	config.ena_gpio = -EINVAL;
 
 	/* Only core regulators have the ramp up delay configuration. */
 	if (sreg->control_reg && sreg->delay_bit_width) {
drivers/regulator/core.c +1 -1
index 9a09f3cdbabb..19c4077374b6 100644
@@ -3459,7 +3459,7 @@ regulator_register(const struct regulator_desc *regulator_desc,
 
 	dev_set_drvdata(&rdev->dev, rdev);
 
-	if (config->ena_gpio && gpio_is_valid(config->ena_gpio)) {
+	if (gpio_is_valid(config->ena_gpio)) {
 		ret = regulator_ena_gpio_request(rdev, config);
 		if (ret != 0) {
 			rdev_err(rdev, "Failed to request enable GPIO%d: %d\n",
drivers/regulator/dummy.c +1 -0
index 2436db9e2ca3..acc4ea593c95 100644
@@ -48,6 +48,7 @@ static int dummy_regulator_probe(struct platform_device *pdev)
 
 	config.dev = &pdev->dev;
 	config.init_data = &dummy_initdata;
+	config.ena_gpio = -EINVAL;
 
 	dummy_regulator_rdev = regulator_register(&dummy_desc, &config);
 	if (IS_ERR(dummy_regulator_rdev)) {
drivers/regulator/fixed.c +1 -3
index c61f7e97e4f8..aabf10dc8a50 100644
@@ -161,9 +161,7 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev)
 		drvdata->desc.n_voltages = 1;
 
 	drvdata->desc.fixed_uV = config->microvolts;
-
-	if (config->gpio >= 0)
-		cfg.ena_gpio = config->gpio;
+	cfg.ena_gpio = config->gpio;
 	cfg.ena_gpio_invert = !config->enable_high;
 	if (config->enabled_at_boot) {
 		if (config->enable_high)
Signature · 1 line
1.8.3.1
#141 lore raw

PATCH 137/222 ARM: dt: microsom: don't set bit 7 for ethernet mux settings

In reply to Russell King - ARM Linux

Bit 6,7 are marked as reserved for the ethernet RGMII pins, so avoid setting these bits.

---
 arch/arm/boot/dts/imx6qdl-microsom-ar8035.dtsi | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)
arch/arm/boot/dts/imx6qdl-microsom-ar8035.dtsi +11 -11
index a3cb2fff8f61..d16066608e21 100644
@@ -26,25 +26,25 @@
 				/* GPIO16 -> AR8035 25MHz */
 				MX6QDL_PAD_GPIO_16__ENET_REF_CLK	0xc0000000
 				MX6QDL_PAD_RGMII_TXC__RGMII_TXC		0x80000000
-				MX6QDL_PAD_RGMII_TD0__RGMII_TD0		0x1b0b0
-				MX6QDL_PAD_RGMII_TD1__RGMII_TD1		0x1b0b0
-				MX6QDL_PAD_RGMII_TD2__RGMII_TD2		0x1b0b0
-				MX6QDL_PAD_RGMII_TD3__RGMII_TD3		0x1b0b0
-				MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL	0x1b0b0
+				MX6QDL_PAD_RGMII_TD0__RGMII_TD0		0x1b030
+				MX6QDL_PAD_RGMII_TD1__RGMII_TD1		0x1b030
+				MX6QDL_PAD_RGMII_TD2__RGMII_TD2		0x1b030
+				MX6QDL_PAD_RGMII_TD3__RGMII_TD3		0x1b030
+				MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL	0x1b030
 				/* AR8035 CLK_25M --> ENET_REF_CLK (V22) */
 				MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK	0x0a0b1
 				/* AR8035 pin strapping: IO voltage: pull up */
-				MX6QDL_PAD_RGMII_RXC__RGMII_RXC		0x1b0b0
+				MX6QDL_PAD_RGMII_RXC__RGMII_RXC		0x1b030
 				/* AR8035 pin strapping: PHYADDR#0: pull down */
-				MX6QDL_PAD_RGMII_RD0__RGMII_RD0		0x130b0
+				MX6QDL_PAD_RGMII_RD0__RGMII_RD0		0x13030
 				/* AR8035 pin strapping: PHYADDR#1: pull down */
-				MX6QDL_PAD_RGMII_RD1__RGMII_RD1		0x130b0
+				MX6QDL_PAD_RGMII_RD1__RGMII_RD1		0x13030
 				/* AR8035 pin strapping: MODE#1: pull up */
-				MX6QDL_PAD_RGMII_RD2__RGMII_RD2		0x1b0b0
+				MX6QDL_PAD_RGMII_RD2__RGMII_RD2		0x1b030
 				/* AR8035 pin strapping: MODE#3: pull up */
-				MX6QDL_PAD_RGMII_RD3__RGMII_RD3		0x1b0b0
+				MX6QDL_PAD_RGMII_RD3__RGMII_RD3		0x1b030
 				/* AR8035 pin strapping: MODE#0: pull down */
-				MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL	0x130b0
+				MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL	0x13030
 
 				/*
 				 * As the RMII pins are also connected to RGMII
Signature · 1 line
1.8.3.1
#142 lore raw

PATCH 138/222 ARM: imx6q: clk: Parent DI clocks to video PLL via di_pre_sel

In reply to Russell King - ARM Linux

From: Sascha Hauer <[email protected]>
To: linux-arm-kernel at lists.infradead.org

Route the video PLL to the display interface clocks via the di_pre_sel and di_sel muxes by default.

---
 arch/arm/mach-imx/clk-imx6q.c | 9 +++++++++
 1 file changed, 9 insertions(+)
arch/arm/mach-imx/clk-imx6q.c +9 -0
index f4d0b0e3e041..7d2b652497d2 100644
@@ -449,6 +449,15 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
 		clk_set_parent(clk[ipu1_sel], clk[pll3_pfd1_540m]);
 	}
 
+	clk_set_parent(clk[ipu1_di0_pre_sel], clk[pll5_video_div]);
+	clk_set_parent(clk[ipu1_di1_pre_sel], clk[pll5_video_div]);
+	clk_set_parent(clk[ipu2_di0_pre_sel], clk[pll5_video_div]);
+	clk_set_parent(clk[ipu2_di1_pre_sel], clk[pll5_video_div]);
+	clk_set_parent(clk[ipu1_di0_sel], clk[ipu1_di0_pre]);
+	clk_set_parent(clk[ipu1_di1_sel], clk[ipu1_di1_pre]);
+	clk_set_parent(clk[ipu2_di0_sel], clk[ipu2_di0_pre]);
+	clk_set_parent(clk[ipu2_di1_sel], clk[ipu2_di1_pre]);
+
 	/*
 	 * The gpmi needs 100MHz frequency in the EDO/Sync mode,
 	 * We can not get the 100MHz from the pll2_pfd0_352m.
Signature · 1 line
1.8.3.1
#143 lore raw

PATCH 139/222 ARM: i.MX6: ipu_di_sel clocks can set parent rates

In reply to Russell King - ARM Linux

From: Philipp Zabel <[email protected]>
To: linux-arm-kernel at lists.infradead.org

To obtain exact pixel clocks, allow the DI clock selectors to influence the PLLs that they are derived from.

---
 arch/arm/mach-imx/clk-imx6q.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)
arch/arm/mach-imx/clk-imx6q.c +8 -8
index 7d2b652497d2..e7888a1c0aa3 100644
@@ -258,14 +258,14 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
 	clk[ipu2_sel]         = imx_clk_mux("ipu2_sel",         base + 0x3c, 14, 2, ipu_sels,          ARRAY_SIZE(ipu_sels));
 	clk[ldb_di0_sel]      = imx_clk_mux_flags("ldb_di0_sel", base + 0x2c, 9,  3, ldb_di_sels,      ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT);
 	clk[ldb_di1_sel]      = imx_clk_mux_flags("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels,      ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT);
-	clk[ipu1_di0_pre_sel] = imx_clk_mux("ipu1_di0_pre_sel", base + 0x34, 6,  3, ipu_di_pre_sels,   ARRAY_SIZE(ipu_di_pre_sels));
-	clk[ipu1_di1_pre_sel] = imx_clk_mux("ipu1_di1_pre_sel", base + 0x34, 15, 3, ipu_di_pre_sels,   ARRAY_SIZE(ipu_di_pre_sels));
-	clk[ipu2_di0_pre_sel] = imx_clk_mux("ipu2_di0_pre_sel", base + 0x38, 6,  3, ipu_di_pre_sels,   ARRAY_SIZE(ipu_di_pre_sels));
-	clk[ipu2_di1_pre_sel] = imx_clk_mux("ipu2_di1_pre_sel", base + 0x38, 15, 3, ipu_di_pre_sels,   ARRAY_SIZE(ipu_di_pre_sels));
-	clk[ipu1_di0_sel]     = imx_clk_mux("ipu1_di0_sel",     base + 0x34, 0,  3, ipu1_di0_sels,     ARRAY_SIZE(ipu1_di0_sels));
-	clk[ipu1_di1_sel]     = imx_clk_mux("ipu1_di1_sel",     base + 0x34, 9,  3, ipu1_di1_sels,     ARRAY_SIZE(ipu1_di1_sels));
-	clk[ipu2_di0_sel]     = imx_clk_mux("ipu2_di0_sel",     base + 0x38, 0,  3, ipu2_di0_sels,     ARRAY_SIZE(ipu2_di0_sels));
-	clk[ipu2_di1_sel]     = imx_clk_mux("ipu2_di1_sel",     base + 0x38, 9,  3, ipu2_di1_sels,     ARRAY_SIZE(ipu2_di1_sels));
+	clk[ipu1_di0_pre_sel] = imx_clk_mux_flags("ipu1_di0_pre_sel", base + 0x34, 6,  3, ipu_di_pre_sels,   ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT);
+	clk[ipu1_di1_pre_sel] = imx_clk_mux_flags("ipu1_di1_pre_sel", base + 0x34, 15, 3, ipu_di_pre_sels,   ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT);
+	clk[ipu2_di0_pre_sel] = imx_clk_mux_flags("ipu2_di0_pre_sel", base + 0x38, 6,  3, ipu_di_pre_sels,   ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT);
+	clk[ipu2_di1_pre_sel] = imx_clk_mux_flags("ipu2_di1_pre_sel", base + 0x38, 15, 3, ipu_di_pre_sels,   ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT);
+	clk[ipu1_di0_sel]     = imx_clk_mux_flags("ipu1_di0_sel",     base + 0x34, 0,  3, ipu1_di0_sels,     ARRAY_SIZE(ipu1_di0_sels), CLK_SET_RATE_PARENT);
+	clk[ipu1_di1_sel]     = imx_clk_mux_flags("ipu1_di1_sel",     base + 0x34, 9,  3, ipu1_di1_sels,     ARRAY_SIZE(ipu1_di1_sels), CLK_SET_RATE_PARENT);
+	clk[ipu2_di0_sel]     = imx_clk_mux_flags("ipu2_di0_sel",     base + 0x38, 0,  3, ipu2_di0_sels,     ARRAY_SIZE(ipu2_di0_sels), CLK_SET_RATE_PARENT);
+	clk[ipu2_di1_sel]     = imx_clk_mux_flags("ipu2_di1_sel",     base + 0x38, 9,  3, ipu2_di1_sels,     ARRAY_SIZE(ipu2_di1_sels), CLK_SET_RATE_PARENT);
 	clk[hsi_tx_sel]       = imx_clk_mux("hsi_tx_sel",       base + 0x30, 28, 1, hsi_tx_sels,       ARRAY_SIZE(hsi_tx_sels));
 	clk[pcie_axi_sel]     = imx_clk_mux("pcie_axi_sel",     base + 0x18, 10, 1, pcie_axi_sels,     ARRAY_SIZE(pcie_axi_sels));
 	clk[ssi1_sel]         = imx_clk_fixup_mux("ssi1_sel",   base + 0x1c, 10, 2, ssi_sels,          ARRAY_SIZE(ssi_sels),          imx_cscmr1_fixup);
Signature · 1 line
1.8.3.1
#144 lore raw

PATCH 140/222 ata: ahci_imx: allow hardware parameters to be specified in DT

In reply to Russell King - ARM Linux

Various SATA phy parameters are board specific, and therefore need to be configured.

---
 drivers/ata/ahci_imx.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 160 insertions(+), 8 deletions(-)
drivers/ata/ahci_imx.c +160 -8
index 6a56a561a80b..fa5755f8dff2 100644
@@ -46,6 +46,7 @@ struct imx_ahci_priv {
 	struct regmap *gpr;
 	bool no_device;
 	bool first_time;
+	u32 phy_params;
 };
 
 static int ahci_imx_hotplug;
@@ -90,14 +91,7 @@ static int imx_sata_enable(struct ahci_host_priv *hpriv)
 				   IMX6Q_GPR13_SATA_TX_LVL_MASK |
 				   IMX6Q_GPR13_SATA_MPLL_CLK_EN |
 				   IMX6Q_GPR13_SATA_TX_EDGE_RATE,
-				   IMX6Q_GPR13_SATA_RX_EQ_VAL_3_0_DB |
-				   IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA2M |
-				   IMX6Q_GPR13_SATA_RX_DPLL_MODE_2P_4F |
-				   IMX6Q_GPR13_SATA_SPD_MODE_3P0G |
-				   IMX6Q_GPR13_SATA_MPLL_SS_EN |
-				   IMX6Q_GPR13_SATA_TX_ATTEN_9_16 |
-				   IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB |
-				   IMX6Q_GPR13_SATA_TX_LVL_1_025_V);
+				   imxpriv->phy_params);
 		regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13,
 				   IMX6Q_GPR13_SATA_MPLL_CLK_EN,
 				   IMX6Q_GPR13_SATA_MPLL_CLK_EN);
@@ -204,6 +198,152 @@ static const struct of_device_id imx_ahci_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, imx_ahci_of_match);
 
+struct reg_value {
+	u32 of_value;
+	u32 reg_value;
+};
+
+struct reg_property {
+	const char *name;
+	const struct reg_value *values;
+	size_t num_values;
+	u32 def_value;
+};
+
+static const struct reg_value gpr13_tx_level[] = {
+	{  937, IMX6Q_GPR13_SATA_TX_LVL_0_937_V },
+	{  947, IMX6Q_GPR13_SATA_TX_LVL_0_947_V },
+	{  957, IMX6Q_GPR13_SATA_TX_LVL_0_957_V },
+	{  966, IMX6Q_GPR13_SATA_TX_LVL_0_966_V },
+	{  976, IMX6Q_GPR13_SATA_TX_LVL_0_976_V },
+	{  986, IMX6Q_GPR13_SATA_TX_LVL_0_986_V },
+	{  996, IMX6Q_GPR13_SATA_TX_LVL_0_996_V },
+	{ 1005, IMX6Q_GPR13_SATA_TX_LVL_1_005_V },
+	{ 1015, IMX6Q_GPR13_SATA_TX_LVL_1_015_V },
+	{ 1025, IMX6Q_GPR13_SATA_TX_LVL_1_025_V },
+	{ 1035, IMX6Q_GPR13_SATA_TX_LVL_1_035_V },
+	{ 1045, IMX6Q_GPR13_SATA_TX_LVL_1_045_V },
+	{ 1054, IMX6Q_GPR13_SATA_TX_LVL_1_054_V },
+	{ 1064, IMX6Q_GPR13_SATA_TX_LVL_1_064_V },
+	{ 1074, IMX6Q_GPR13_SATA_TX_LVL_1_074_V },
+	{ 1084, IMX6Q_GPR13_SATA_TX_LVL_1_084_V },
+	{ 1094, IMX6Q_GPR13_SATA_TX_LVL_1_094_V },
+	{ 1104, IMX6Q_GPR13_SATA_TX_LVL_1_104_V },
+	{ 1113, IMX6Q_GPR13_SATA_TX_LVL_1_113_V },
+	{ 1123, IMX6Q_GPR13_SATA_TX_LVL_1_123_V },
+	{ 1133, IMX6Q_GPR13_SATA_TX_LVL_1_133_V },
+	{ 1143, IMX6Q_GPR13_SATA_TX_LVL_1_143_V },
+	{ 1152, IMX6Q_GPR13_SATA_TX_LVL_1_152_V },
+	{ 1162, IMX6Q_GPR13_SATA_TX_LVL_1_162_V },
+	{ 1172, IMX6Q_GPR13_SATA_TX_LVL_1_172_V },
+	{ 1182, IMX6Q_GPR13_SATA_TX_LVL_1_182_V },
+	{ 1191, IMX6Q_GPR13_SATA_TX_LVL_1_191_V },
+	{ 1201, IMX6Q_GPR13_SATA_TX_LVL_1_201_V },
+	{ 1211, IMX6Q_GPR13_SATA_TX_LVL_1_211_V },
+	{ 1221, IMX6Q_GPR13_SATA_TX_LVL_1_221_V },
+	{ 1230, IMX6Q_GPR13_SATA_TX_LVL_1_230_V },
+	{ 1240, IMX6Q_GPR13_SATA_TX_LVL_1_240_V }
+};
+
+static const struct reg_value gpr13_tx_boost[] = {
+	{    0, IMX6Q_GPR13_SATA_TX_BOOST_0_00_DB },
+	{  370, IMX6Q_GPR13_SATA_TX_BOOST_0_37_DB },
+	{  740, IMX6Q_GPR13_SATA_TX_BOOST_0_74_DB },
+	{  111, IMX6Q_GPR13_SATA_TX_BOOST_1_11_DB },
+	{  148, IMX6Q_GPR13_SATA_TX_BOOST_1_48_DB },
+	{  185, IMX6Q_GPR13_SATA_TX_BOOST_1_85_DB },
+	{  222, IMX6Q_GPR13_SATA_TX_BOOST_2_22_DB },
+	{  259, IMX6Q_GPR13_SATA_TX_BOOST_2_59_DB },
+	{  296, IMX6Q_GPR13_SATA_TX_BOOST_2_96_DB },
+	{  333, IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB },
+	{  370, IMX6Q_GPR13_SATA_TX_BOOST_3_70_DB },
+	{  407, IMX6Q_GPR13_SATA_TX_BOOST_4_07_DB },
+	{  444, IMX6Q_GPR13_SATA_TX_BOOST_4_44_DB },
+	{  481, IMX6Q_GPR13_SATA_TX_BOOST_4_81_DB },
+	{  528, IMX6Q_GPR13_SATA_TX_BOOST_5_28_DB },
+	{  575, IMX6Q_GPR13_SATA_TX_BOOST_5_75_DB }
+};
+
+static const struct reg_value gpr13_tx_atten[] = {
+	{  8, IMX6Q_GPR13_SATA_TX_ATTEN_8_16 },
+	{  9, IMX6Q_GPR13_SATA_TX_ATTEN_9_16 },
+	{ 10, IMX6Q_GPR13_SATA_TX_ATTEN_10_16 },
+	{ 12, IMX6Q_GPR13_SATA_TX_ATTEN_12_16 },
+	{ 14, IMX6Q_GPR13_SATA_TX_ATTEN_14_16 },
+	{ 16, IMX6Q_GPR13_SATA_TX_ATTEN_16_16 },
+};
+
+static const struct reg_value gpr13_rx_eq[] = {
+	{  500, IMX6Q_GPR13_SATA_RX_EQ_VAL_0_5_DB },
+	{ 1000, IMX6Q_GPR13_SATA_RX_EQ_VAL_1_0_DB },
+	{ 1500, IMX6Q_GPR13_SATA_RX_EQ_VAL_1_5_DB },
+	{ 2000, IMX6Q_GPR13_SATA_RX_EQ_VAL_2_0_DB },
+	{ 2500, IMX6Q_GPR13_SATA_RX_EQ_VAL_2_5_DB },
+	{ 3000, IMX6Q_GPR13_SATA_RX_EQ_VAL_3_0_DB },
+	{ 3500, IMX6Q_GPR13_SATA_RX_EQ_VAL_3_5_DB },
+	{ 4000, IMX6Q_GPR13_SATA_RX_EQ_VAL_4_0_DB },
+};
+
+static const struct reg_property gpr13_props[] = {
+	{
+		.name = "fsl,transmit-level-mV",
+		.values = gpr13_tx_level,
+		.num_values = ARRAY_SIZE(gpr13_tx_level),
+		.def_value = IMX6Q_GPR13_SATA_TX_LVL_1_025_V,
+	}, {
+		.name = "fsl,transmit-boost-mdB",
+		.values = gpr13_tx_boost,
+		.num_values = ARRAY_SIZE(gpr13_tx_boost),
+		.def_value = IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB,
+	}, {
+		.name = "fsl,transmit-atten-16ths",
+		.values = gpr13_tx_atten,
+		.num_values = ARRAY_SIZE(gpr13_tx_atten),
+		.def_value = IMX6Q_GPR13_SATA_TX_ATTEN_9_16,
+	}, {
+		.name = "fsl,receive-eq-mdB",
+		.values = gpr13_rx_eq,
+		.num_values = ARRAY_SIZE(gpr13_rx_eq),
+		.def_value = IMX6Q_GPR13_SATA_RX_EQ_VAL_3_0_DB,
+	},
+};
+
+static u32 imx_ahci_parse_props(struct device *dev,
+				const struct reg_property *prop, size_t num)
+{
+	struct device_node *np = dev->of_node;
+	u32 reg_value = 0;
+	int i, j;
+
+	for (i = 0; i < num; i++, prop++) {
+		u32 of_val;
+
+		if (of_property_read_u32(np, prop->name, &of_val)) {
+			dev_info(dev, "%s not specified, using %08x\n",
+				prop->name, prop->def_value);
+			reg_value |= prop->def_value;
+			continue;
+		}
+
+		for (j = 0; j < prop->num_values; j++) {
+			if (prop->values[j].of_value == of_val) {
+				dev_info(dev, "%s value %u, using %08x\n",
+					prop->name, of_val, prop->values[j].reg_value);
+				reg_value |= prop->values[j].reg_value;
+				break;
+			}
+		}
+
+		if (j == prop->num_values) {
+			dev_err(dev, "DT property %s is not a valid value\n",
+				prop->name);
+			reg_value |= prop->def_value;
+		}
+	}
+
+	return reg_value;
+}
+
 static int imx_ahci_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -231,6 +371,8 @@ static int imx_ahci_probe(struct platform_device *pdev)
 	}
 
 	if (imxpriv->type == AHCI_IMX6Q) {
+		u32 reg_value;
+
 		imxpriv->gpr = syscon_regmap_lookup_by_compatible(
 							"fsl,imx6q-iomuxc-gpr");
 		if (IS_ERR(imxpriv->gpr)) {
@@ -238,6 +380,16 @@ static int imx_ahci_probe(struct platform_device *pdev)
 				"failed to find fsl,imx6q-iomux-gpr regmap\n");
 			return PTR_ERR(imxpriv->gpr);
 		}
+
+		reg_value = imx_ahci_parse_props(dev, gpr13_props,
+						 ARRAY_SIZE(gpr13_props));
+
+		imxpriv->phy_params =
+				   IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA2M |
+				   IMX6Q_GPR13_SATA_RX_DPLL_MODE_2P_4F |
+				   IMX6Q_GPR13_SATA_SPD_MODE_3P0G |
+				   IMX6Q_GPR13_SATA_MPLL_SS_EN |
+				   reg_value;
 	}
 
 	hpriv = ahci_platform_get_resources(pdev);
Signature · 1 line
1.8.3.1
#145 lore raw

PATCH 141/222 ARM: cubox-i: add eSATA DT configuration

In reply to Russell King - ARM Linux
---
 arch/arm/boot/dts/imx6q-cubox-i.dts | 3 +++
 1 file changed, 3 insertions(+)
arch/arm/boot/dts/imx6q-cubox-i.dts +3 -0
index bc5f31e3e892..941365d7ee65 100644
@@ -13,4 +13,7 @@
 
 &sata {
 	status = "okay";
+	fsl,transmit-level-mV = <1104>;
+	fsl,transmit-boost-mdB = <0>;
+	fsl,transmit-atten-16ths = <9>;
 };
Signature · 1 line
1.8.3.1
#146 lore raw

PATCH 142/222 ahci_imx: add disable for spread-spectrum

In reply to Russell King - ARM Linux

Spread-spectrum doesn't work with Cubox-i hardware, so we have to disable this feature. Add a DT property so that platforms can indicate that this feature should not be enabled.

Having it as a negative property keeps existing DT files working.

---
 arch/arm/boot/dts/imx6q-cubox-i.dts |  1 +
 drivers/ata/ahci_imx.c              | 14 +++++++++++++-
 2 files changed, 14 insertions(+), 1 deletion(-)
arch/arm/boot/dts/imx6q-cubox-i.dts +1 -0
index 941365d7ee65..9efd8b0c8011 100644
@@ -16,4 +16,5 @@
 	fsl,transmit-level-mV = <1104>;
 	fsl,transmit-boost-mdB = <0>;
 	fsl,transmit-atten-16ths = <9>;
+	fsl,no-spread-spectrum;
 };
drivers/ata/ahci_imx.c +13 -1
index fa5755f8dff2..e4d5e0266fe0 100644
@@ -208,6 +208,7 @@ struct reg_property {
 	const struct reg_value *values;
 	size_t num_values;
 	u32 def_value;
+	u32 set_value;
 };
 
 static const struct reg_value gpr13_tx_level[] = {
@@ -305,6 +306,10 @@ static const struct reg_property gpr13_props[] = {
 		.values = gpr13_rx_eq,
 		.num_values = ARRAY_SIZE(gpr13_rx_eq),
 		.def_value = IMX6Q_GPR13_SATA_RX_EQ_VAL_3_0_DB,
+	}, {
+		.name = "fsl,no-spread-spectrum",
+		.def_value = IMX6Q_GPR13_SATA_MPLL_SS_EN,
+		.set_value = 0,
 	},
 };
 
@@ -318,6 +323,14 @@ static u32 imx_ahci_parse_props(struct device *dev,
 	for (i = 0; i < num; i++, prop++) {
 		u32 of_val;
 
+		if (prop->num_values == 0) {
+			if (of_property_read_bool(np, prop->name))
+				reg_value |= prop->set_value;
+			else
+				reg_value |= prop->def_value;
+			continue;
+		}
+
 		if (of_property_read_u32(np, prop->name, &of_val)) {
 			dev_info(dev, "%s not specified, using %08x\n",
 				prop->name, prop->def_value);
@@ -388,7 +401,6 @@ static int imx_ahci_probe(struct platform_device *pdev)
 				   IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA2M |
 				   IMX6Q_GPR13_SATA_RX_DPLL_MODE_2P_4F |
 				   IMX6Q_GPR13_SATA_SPD_MODE_3P0G |
-				   IMX6Q_GPR13_SATA_MPLL_SS_EN |
 				   reg_value;
 	}
 
Signature · 1 line
1.8.3.1
#147 lore raw

PATCH 143/222 imx-drm: imx-drm-core: fix imx_drm_encoder_get_mux_id

In reply to Russell King - ARM Linux

From: Philipp Zabel <[email protected]>
To: linux-arm-kernel at lists.infradead.org

The decoder mux id is equal to the port id of the encoder's input port that is connected to the given crtc, not to the endpoint id (which is arbitrary and usually zero).

---
 drivers/staging/imx-drm/imx-drm-core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/staging/imx-drm/imx-drm-core.c +1 -1
index 4144a75e5f71..bc7f8bd227c7 100644
@@ -517,7 +517,7 @@ int imx_drm_encoder_get_mux_id(struct device_node *node,
 		of_node_put(port);
 		if (port == imx_crtc->port) {
 			ret = of_graph_parse_endpoint(ep, &endpoint);
-			return ret ? ret : endpoint.id;
+			return ret ? ret : endpoint.port;
 		}
 	} while (ep);
 
Signature · 1 line
1.8.3.1
#148 lore raw

PATCH 144/222 imx-drm: imx-drm-core: skip components whose parent device is disabled

In reply to Russell King - ARM Linux

From: Shawn Guo <[email protected]>
To: linux-arm-kernel at lists.infradead.org

In a board setup which disables LDB device node completely by changing status to 'disabled', and only enables HDMI device, we're running into the problem that imx-drm master never succeeds in binding, and hence HDMI does not come up either.

&ldb {
	status = "disabled";

	lvds-channel at 1 {
		...
		status = "okay";
	};
};

The imx-drm-core should really skip the LVDS channels no matter what lvds-channel's status is, if LDB device is disabled. Let's consider such setup a misconfiguration, give a warning in there and not add the component.

---
 drivers/staging/imx-drm/imx-drm-core.c | 5 +++++
 1 file changed, 5 insertions(+)
drivers/staging/imx-drm/imx-drm-core.c +5 -0
index bc7f8bd227c7..c270c9ae6d27 100644
@@ -675,6 +675,11 @@ static int imx_drm_platform_probe(struct platform_device *pdev)
 			if (!remote || !of_device_is_available(remote)) {
 				of_node_put(remote);
 				continue;
+			} else if (!of_device_is_available(remote->parent)) {
+				dev_warn(&pdev->dev, "parent device of %s is not available\n",
+					 remote->full_name);
+				of_node_put(remote);
+				continue;
 			}
 
 			ret = imx_drm_add_component(&pdev->dev, remote);
Signature · 1 line
1.8.3.1
#149 lore raw

PATCH 145/222 imx-drm: imx-ldb: add drm_panel support

In reply to Russell King - ARM Linux

From: Philipp Zabel <[email protected]>
To: linux-arm-kernel at lists.infradead.org

This patch allows to optionally attach the lvds-channel to a panel supported by a drm_panel driver instead of supplying the modes via device tree.

Before:

	ldb {
		...

		lvds-channel at 0 {
			...

			display-timings {
				native-timing = <&timing1>;
				timing1: etm0700g0dh6 {
					hactive = <800>;
					vactive = <480>;
					clock-frequency = <33260000>;
					hsync-len = <128>;
					hback-porch = <88>;
					hfront-porch = <40>;
					vsync-len = <2>;
					vback-porch = <33>;
					vfront-porch = <10>;
					hsync-active = <0>;
					vsync-active = <0>;
					...
				};
			};
			...
		};
	};

After:

	ldb {
		...

		lvds-channel at 0 {
			fsl,panel = <&panel>;
			...
		};
	};

	panel: panel {
		compatible = "edt,etm0700g0dh6", "simple-panel";
	};
[Fixed build error due to missing select on DRM_PANEL --rmk]
---
 drivers/staging/imx-drm/Kconfig   |  1 +
 drivers/staging/imx-drm/imx-ldb.c | 21 +++++++++++++++++++++
 2 files changed, 22 insertions(+)
drivers/staging/imx-drm/Kconfig +1 -0
index c6e8ba7b3e4e..92fb52cbd3a2 100644
@@ -35,6 +35,7 @@ config DRM_IMX_TVE
 config DRM_IMX_LDB
 	tristate "Support for LVDS displays"
 	depends on DRM_IMX && MFD_SYSCON
+	select DRM_PANEL
 	help
 	  Choose this to enable the internal LVDS Display Bridge (LDB)
 	  found on i.MX53 and i.MX6 processors.
drivers/staging/imx-drm/imx-ldb.c +21 -0
index fe4c1ef4e7a5..3a3bacec2e11 100644
@@ -24,6 +24,7 @@
 #include <drm/drmP.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_panel.h>
 #include <linux/mfd/syscon.h>
 #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
 #include <linux/of_address.h>
@@ -60,6 +61,7 @@ struct imx_ldb_channel {
 	struct imx_ldb *ldb;
 	struct drm_connector connector;
 	struct drm_encoder encoder;
+	struct drm_panel *panel;
 	struct device_node *child;
 	int chno;
 	void *edid;
@@ -96,6 +98,13 @@ static int imx_ldb_connector_get_modes(struct drm_connector *connector)
 	struct imx_ldb_channel *imx_ldb_ch = con_to_imx_ldb_ch(connector);
 	int num_modes = 0;
 
+	if (imx_ldb_ch->panel && imx_ldb_ch->panel->funcs &&
+	    imx_ldb_ch->panel->funcs->get_modes) {
+		num_modes = imx_ldb_ch->panel->funcs->get_modes(imx_ldb_ch->panel);
+		if (num_modes > 0)
+			return num_modes;
+	}
+
 	if (imx_ldb_ch->edid) {
 		drm_mode_connector_update_edid_property(connector,
 							imx_ldb_ch->edid);
@@ -243,6 +252,8 @@ static void imx_ldb_encoder_commit(struct drm_encoder *encoder)
 	}
 
 	regmap_write(ldb->regmap, IOMUXC_GPR2, ldb->ldb_ctrl);
+
+	drm_panel_enable(imx_ldb_ch->panel);
 }
 
 static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder,
@@ -294,6 +305,8 @@ static void imx_ldb_encoder_disable(struct drm_encoder *encoder)
 		 (ldb->ldb_ctrl & LDB_CH1_MODE_EN_MASK) == 0)
 		return;
 
+	drm_panel_disable(imx_ldb_ch->panel);
+
 	if (imx_ldb_ch == &ldb->channel[0])
 		ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK;
 	else if (imx_ldb_ch == &ldb->channel[1])
@@ -379,6 +392,9 @@ static int imx_ldb_register(struct drm_device *drm,
 	drm_connector_init(drm, &imx_ldb_ch->connector,
 			   &imx_ldb_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
 
+	if (imx_ldb_ch->panel)
+		drm_panel_attach(imx_ldb_ch->panel, &imx_ldb_ch->connector);
+
 	drm_mode_connector_attach_encoder(&imx_ldb_ch->connector,
 			&imx_ldb_ch->encoder);
 
@@ -493,6 +509,7 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
 
 	for_each_child_of_node(np, child) {
 		struct imx_ldb_channel *channel;
+		struct device_node *panel_node;
 
 		ret = of_property_read_u32(child, "reg", &i);
 		if (ret || i < 0 || i > 1)
@@ -556,6 +573,10 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
 			return -EINVAL;
 		}
 
+		panel_node = of_parse_phandle(child, "fsl,panel", 0);
+		if (panel_node)
+			channel->panel = of_drm_find_panel(panel_node);
+
 		ret = imx_ldb_register(drm, channel);
 		if (ret)
 			return ret;
Signature · 1 line
1.8.3.1
#150 lore raw

PATCH 146/222 imx-drm: match ipu_di_signal_cfg's clk_pol with its description.

In reply to Russell King - ARM Linux

From: Denis Carikli <[email protected]>
To: linux-arm-kernel at lists.infradead.org

According to the datasheet, setting the di0_polarity_disp_clk field in the GENERAL di register sets the output clock polarity to active high.

---
 drivers/staging/imx-drm/ipu-v3/ipu-di.c | 2 +-
 drivers/staging/imx-drm/ipuv3-crtc.c    | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
drivers/staging/imx-drm/ipu-v3/ipu-di.c +1 -1
index 82a9ebad697c..849b3e120ef0 100644
@@ -595,7 +595,7 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
 		}
 	}
 
-	if (!sig->clk_pol)
+	if (sig->clk_pol)
 		di_gen |= DI_GEN_POLARITY_DISP_CLK;
 
 	ipu_di_write(di, di_gen, DI_GENERAL);
drivers/staging/imx-drm/ipuv3-crtc.c +1 -1
index c48f640db006..f2c9cd040043 100644
@@ -158,7 +158,7 @@ static int ipu_crtc_mode_set(struct drm_crtc *crtc,
 		sig_cfg.Vsync_pol = 1;
 
 	sig_cfg.enable_pol = 1;
-	sig_cfg.clk_pol = 1;
+	sig_cfg.clk_pol = 0;
 	sig_cfg.width = mode->hdisplay;
 	sig_cfg.height = mode->vdisplay;
 	sig_cfg.pixel_fmt = out_pixel_fmt;
Signature · 1 line
1.8.3.1
#151 lore raw

PATCH 147/222 v4l2: add new V4L2_PIX_FMT_RGB666 pixel format.

In reply to Russell King - ARM Linux

From: Denis Carikli <[email protected]>
To: linux-arm-kernel at lists.infradead.org

That new macro is needed by the imx_drm staging driver
for supporting the QVGA display of the eukrea-cpuimx51 board.

---
 .../DocBook/media/v4l/pixfmt-packed-rgb.xml        | 39 ++++++++++++++++++++++
 include/uapi/linux/videodev2.h                     |  1 +
 2 files changed, 40 insertions(+)
Documentation/DocBook/media/v4l/pixfmt-packed-rgb.xml +39 -0
index e1c4f8b4c0b3..88a7fe1ecaf1 100644
@@ -279,6 +279,45 @@ colorspace <constant>V4L2_COLORSPACE_SRGB</constant>.</para>
 	    <entry></entry>
 	    <entry></entry>
 	  </row>
+	  <row id="V4L2-PIX-FMT-RGB666">
+	    <entry><constant>V4L2_PIX_FMT_RGB666</constant></entry>
+	    <entry>'RGBH'</entry>
+	    <entry></entry>
+	    <entry>r<subscript>5</subscript></entry>
+	    <entry>r<subscript>4</subscript></entry>
+	    <entry>r<subscript>3</subscript></entry>
+	    <entry>r<subscript>2</subscript></entry>
+	    <entry>r<subscript>1</subscript></entry>
+	    <entry>r<subscript>0</subscript></entry>
+	    <entry>g<subscript>5</subscript></entry>
+	    <entry>g<subscript>4</subscript></entry>
+	    <entry></entry>
+	    <entry>g<subscript>3</subscript></entry>
+	    <entry>g<subscript>2</subscript></entry>
+	    <entry>g<subscript>1</subscript></entry>
+	    <entry>g<subscript>0</subscript></entry>
+	    <entry>b<subscript>5</subscript></entry>
+	    <entry>b<subscript>4</subscript></entry>
+	    <entry>b<subscript>3</subscript></entry>
+	    <entry>b<subscript>2</subscript></entry>
+	    <entry></entry>
+	    <entry>b<subscript>1</subscript></entry>
+	    <entry>b<subscript>0</subscript></entry>
+	    <entry></entry>
+	    <entry></entry>
+	    <entry></entry>
+	    <entry></entry>
+	    <entry></entry>
+	    <entry></entry>
+	    <entry></entry>
+	    <entry></entry>
+	    <entry></entry>
+	    <entry></entry>
+	    <entry></entry>
+	    <entry></entry>
+	    <entry></entry>
+	    <entry></entry>
+	  </row>
 	  <row id="V4L2-PIX-FMT-BGR24">
 	    <entry><constant>V4L2_PIX_FMT_BGR24</constant></entry>
 	    <entry>'BGR3'</entry>
include/uapi/linux/videodev2.h +1 -0
index ea468ee8fe21..d5d818a86676 100644
@@ -299,6 +299,7 @@ struct v4l2_pix_format {
 #define V4L2_PIX_FMT_RGB555X v4l2_fourcc('R', 'G', 'B', 'Q') /* 16  RGB-5-5-5 BE  */
 #define V4L2_PIX_FMT_RGB565X v4l2_fourcc('R', 'G', 'B', 'R') /* 16  RGB-5-6-5 BE  */
 #define V4L2_PIX_FMT_BGR666  v4l2_fourcc('B', 'G', 'R', 'H') /* 18  BGR-6-6-6	  */
+#define V4L2_PIX_FMT_RGB666  v4l2_fourcc('R', 'G', 'B', 'H') /* 18  RGB-6-6-6	  */
 #define V4L2_PIX_FMT_BGR24   v4l2_fourcc('B', 'G', 'R', '3') /* 24  BGR-8-8-8     */
 #define V4L2_PIX_FMT_RGB24   v4l2_fourcc('R', 'G', 'B', '3') /* 24  RGB-8-8-8     */
 #define V4L2_PIX_FMT_BGR32   v4l2_fourcc('B', 'G', 'R', '4') /* 32  BGR-8-8-8-8   */
Signature · 1 line
1.8.3.1
#152 lore raw

PATCH 148/222 imx-drm: add RGB666 support for parallel display.

In reply to Russell King - ARM Linux

From: Denis Carikli <[email protected]>
To: linux-arm-kernel at lists.infradead.org

---
 .../devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt          | 3 ++-
 drivers/staging/imx-drm/ipu-v3/ipu-dc.c                          | 9 +++++++++
 drivers/staging/imx-drm/parallel-display.c                       | 2 ++
 3 files changed, 13 insertions(+), 1 deletion(-)
Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt +2 -1
index 3be5ce7a9654..83137ef5a1ba 100644
@@ -60,7 +60,8 @@ Parallel display support
 - compatible: Should be "fsl,imx-parallel-display"
 Optional properties:
 - interface_pix_fmt: How this display is connected to the
-  display interface. Currently supported types: "rgb24", "rgb565", "bgr666"
+  display interface. Currently supported types: "rgb24", "rgb565", "bgr666",
+  "rgb666"
 - edid: verbatim EDID data block describing attached display.
 - ddc: phandle describing the i2c bus handling the display data
   channel
drivers/staging/imx-drm/ipu-v3/ipu-dc.c +9 -0
index d5de8bb5c803..6f9abe8a4575 100644
@@ -92,6 +92,7 @@ enum ipu_dc_map {
 	IPU_DC_MAP_GBR24, /* TVEv2 */
 	IPU_DC_MAP_BGR666,
 	IPU_DC_MAP_BGR24,
+	IPU_DC_MAP_RGB666,
 };
 
 struct ipu_dc {
@@ -155,6 +156,8 @@ static int ipu_pixfmt_to_map(u32 fmt)
 		return IPU_DC_MAP_BGR666;
 	case V4L2_PIX_FMT_BGR24:
 		return IPU_DC_MAP_BGR24;
+	case V4L2_PIX_FMT_RGB666:
+		return IPU_DC_MAP_RGB666;
 	default:
 		return -EINVAL;
 	}
@@ -404,6 +407,12 @@ int ipu_dc_init(struct ipu_soc *ipu, struct device *dev,
 	ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 1, 15, 0xff); /* green */
 	ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 0, 23, 0xff); /* blue */
 
+	/* rgb666 */
+	ipu_dc_map_clear(priv, IPU_DC_MAP_RGB666);
+	ipu_dc_map_config(priv, IPU_DC_MAP_RGB666, 0, 5, 0xfc); /* blue */
+	ipu_dc_map_config(priv, IPU_DC_MAP_RGB666, 1, 11, 0xfc); /* green */
+	ipu_dc_map_config(priv, IPU_DC_MAP_RGB666, 2, 17, 0xfc); /* red */
+
 	return 0;
 }
 
drivers/staging/imx-drm/parallel-display.c +2 -0
index c60b6c645f42..01b7ce50fc30 100644
@@ -219,6 +219,8 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data)
 			imxpd->interface_pix_fmt = V4L2_PIX_FMT_RGB565;
 		else if (!strcmp(fmt, "bgr666"))
 			imxpd->interface_pix_fmt = V4L2_PIX_FMT_BGR666;
+		else if (!strcmp(fmt, "rgb666"))
+			imxpd->interface_pix_fmt = V4L2_PIX_FMT_RGB666;
 	}
 
 	panel_node = of_parse_phandle(np, "fsl,panel", 0);
Signature · 1 line
1.8.3.1
#153 lore raw

PATCH 149/222 imx-drm: ipu-common: add ipu_map_irq to request non-IDMAC interrupts

In reply to Russell King - ARM Linux

From: Philipp Zabel <[email protected]>
To: linux-arm-kernel at lists.infradead.org

This allows to request the DC related interrupts.

---
 drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h |  1 +
 drivers/staging/imx-drm/ipu-v3/ipu-common.c | 19 +++++++++++++------
 2 files changed, 14 insertions(+), 6 deletions(-)
drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h +1 -0
index c4d14ead5837..2966e425990e 100644
@@ -76,6 +76,7 @@ enum ipu_channel_irq {
 	IPU_IRQ_EOS = 192,
 };
 
+int ipu_map_irq(struct ipu_soc *ipu, int irq);
 int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
 		enum ipu_channel_irq irq);
 
drivers/staging/imx-drm/ipu-v3/ipu-common.c +13 -6
index ca85d3d70ae3..058551795677 100644
@@ -933,15 +933,22 @@ static void ipu_err_irq_handler(unsigned int irq, struct irq_desc *desc)
 	chained_irq_exit(chip, desc);
 }
 
-int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
-		enum ipu_channel_irq irq_type)
+int ipu_map_irq(struct ipu_soc *ipu, int irq)
 {
-	int irq = irq_linear_revmap(ipu->domain, irq_type + channel->num);
+	int virq;
 
-	if (!irq)
-		irq = irq_create_mapping(ipu->domain, irq_type + channel->num);
+	virq = irq_linear_revmap(ipu->domain, irq);
+	if (!virq)
+		virq = irq_create_mapping(ipu->domain, irq);
+
+	return virq;
+}
+EXPORT_SYMBOL_GPL(ipu_map_irq);
 
-	return irq;
+int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
+		enum ipu_channel_irq irq_type)
+{
+	return ipu_map_irq(ipu, irq_type + channel->num);
 }
 EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq);
 
Signature · 1 line
1.8.3.1
#154 lore raw

PATCH 150/222 imx-drm: ipu-common: add helpers to check for a busy IDMAC channel and to busywait for an interrupt

In reply to Russell King - ARM Linux

From: Philipp Zabel <[email protected]>
To: linux-arm-kernel at lists.infradead.org

---
 drivers/staging/imx-drm/ipu-v3/ipu-common.c | 22 ++++++++++++++++++++++
 drivers/staging/imx-drm/ipu-v3/ipu-prv.h    |  3 +++
 2 files changed, 25 insertions(+)
drivers/staging/imx-drm/ipu-v3/ipu-common.c +22 -0
index 058551795677..8fb4c207f3f1 100644
@@ -697,6 +697,12 @@ int ipu_idmac_enable_channel(struct ipuv3_channel *channel)
 }
 EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel);
 
+bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno)
+{
+	return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno));
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy);
+
 int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms)
 {
 	struct ipu_soc *ipu = channel->ipu;
@@ -714,6 +720,22 @@ int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms)
 }
 EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy);
 
+int ipu_wait_interrupt(struct ipu_soc *ipu, int irq, int ms)
+{
+	unsigned long timeout;
+
+	timeout = jiffies + msecs_to_jiffies(ms);
+	ipu_cm_write(ipu, BIT(irq % 32), IPU_INT_STAT(irq / 32));
+	while (!(ipu_cm_read(ipu, IPU_INT_STAT(irq / 32) & BIT(irq % 32)))) {
+		if (time_after(jiffies, timeout))
+			return -ETIMEDOUT;
+		cpu_relax();
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_wait_interrupt);
+
 int ipu_idmac_disable_channel(struct ipuv3_channel *channel)
 {
 	struct ipu_soc *ipu = channel->ipu;
drivers/staging/imx-drm/ipu-v3/ipu-prv.h +3 -0
index 4df00501adc2..bfc1b3366488 100644
@@ -185,6 +185,9 @@ void ipu_srm_dp_sync_update(struct ipu_soc *ipu);
 int ipu_module_enable(struct ipu_soc *ipu, u32 mask);
 int ipu_module_disable(struct ipu_soc *ipu, u32 mask);
 
+bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno);
+int ipu_wait_interrupt(struct ipu_soc *ipu, int irq, int ms);
+
 int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id,
 		unsigned long base, u32 module, struct clk *ipu_clk);
 void ipu_di_exit(struct ipu_soc *ipu, int id);
Signature · 1 line
1.8.3.1
#155 lore raw

PATCH 151/222 imx-drm: ipu-dmfc: wait for FIFOs to run empty before disabling

In reply to Russell King - ARM Linux

From: Philipp Zabel <[email protected]>
To: linux-arm-kernel at lists.infradead.org

Disabling the DMFC module while there is still data in the FIFOs could cause the "new frame before end of frame" error state when the DMFC is enabled again.

---
 drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c | 25 +++++++++++++++++++++++--
 1 file changed, 23 insertions(+), 2 deletions(-)
drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c +23 -2
index 45213017fa4b..59f182b28fc1 100644
@@ -28,7 +28,12 @@
 #define DMFC_GENERAL1		0x0014
 #define DMFC_GENERAL2		0x0018
 #define DMFC_IC_CTRL		0x001c
-#define DMFC_STAT		0x0020
+#define DMFC_WR_CHAN_ALT	0x0020
+#define DMFC_WR_CHAN_DEF_ALT	0x0024
+#define DMFC_DP_CHAN_ALT	0x0028
+#define DMFC_DP_CHAN_DEF_ALT	0x002c
+#define DMFC_GENERAL1_ALT	0x0030
+#define DMFC_STAT		0x0034
 
 #define DMFC_WR_CHAN_1_28		0
 #define DMFC_WR_CHAN_2_41		8
@@ -133,6 +138,20 @@ int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc)
 }
 EXPORT_SYMBOL_GPL(ipu_dmfc_enable_channel);
 
+static void ipu_dmfc_wait_fifos(struct ipu_dmfc_priv *priv)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+
+	while ((readl(priv->base + DMFC_STAT) & 0x02fff000) != 0x02fff000) {
+		if (time_after(jiffies, timeout)) {
+			dev_warn(priv->dev,
+				 "Timeout waiting for DMFC FIFOs to clear\n");
+			break;
+		}
+		cpu_relax();
+	}
+}
+
 void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc)
 {
 	struct ipu_dmfc_priv *priv = dmfc->priv;
@@ -141,8 +160,10 @@ void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc)
 
 	priv->use_count--;
 
-	if (!priv->use_count)
+	if (!priv->use_count) {
+		ipu_dmfc_wait_fifos(priv);
 		ipu_module_disable(priv->ipu, IPU_CONF_DMFC_EN);
+	}
 
 	if (priv->use_count < 0)
 		priv->use_count = 0;
Signature · 1 line
1.8.3.1