mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-04 06:54:27 -05:00 
			
		
		
		
	bcm27xx: remove linux 5.10 compatibility
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
This commit is contained in:
		
							parent
							
								
									99ad84b6d9
								
							
						
					
					
						commit
						d5c4f24b2a
					
				@ -15,19 +15,3 @@ define KernelPackage/hwmon-raspberrypi/description
 | 
				
			|||||||
endef
 | 
					endef
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(eval $(call KernelPackage,hwmon-raspberrypi))
 | 
					$(eval $(call KernelPackage,hwmon-raspberrypi))
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
define KernelPackage/hwmon-rpi-poe-fan
 | 
					 | 
				
			||||||
  SUBMENU:=$(HWMON_MENU)
 | 
					 | 
				
			||||||
  TITLE:=Raspberry Pi PoE HAT fan
 | 
					 | 
				
			||||||
  DEPENDS:=@TARGET_bcm27xx @LINUX_5_10 +kmod-hwmon-core
 | 
					 | 
				
			||||||
  KCONFIG:=CONFIG_SENSORS_RPI_POE_FAN
 | 
					 | 
				
			||||||
  FILES:=$(LINUX_DIR)/drivers/hwmon/rpi-poe-fan.ko
 | 
					 | 
				
			||||||
  AUTOLOAD:=$(call AutoProbe,rpi-poe-fan)
 | 
					 | 
				
			||||||
endef
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
define KernelPackage/hwmon-rpi-poe-fan/description
 | 
					 | 
				
			||||||
  Raspberry Pi PoE HAT fan driver
 | 
					 | 
				
			||||||
endef
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
$(eval $(call KernelPackage,hwmon-rpi-poe-fan))
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -932,7 +932,7 @@ define KernelPackage/sound-soc-rpi-cirrus
 | 
				
			|||||||
    CONFIG_SND_SOC_WM8804 \
 | 
					    CONFIG_SND_SOC_WM8804 \
 | 
				
			||||||
    CONFIG_SND_SOC_WM_ADSP
 | 
					    CONFIG_SND_SOC_WM_ADSP
 | 
				
			||||||
  FILES:= \
 | 
					  FILES:= \
 | 
				
			||||||
    $(LINUX_DIR)/drivers/mfd/arizona.ko@ge5.15 \
 | 
					    $(LINUX_DIR)/drivers/mfd/arizona.ko \
 | 
				
			||||||
    $(LINUX_DIR)/sound/soc/bcm/snd-soc-rpi-cirrus.ko \
 | 
					    $(LINUX_DIR)/sound/soc/bcm/snd-soc-rpi-cirrus.ko \
 | 
				
			||||||
    $(LINUX_DIR)/sound/soc/codecs/snd-soc-arizona.ko \
 | 
					    $(LINUX_DIR)/sound/soc/codecs/snd-soc-arizona.ko \
 | 
				
			||||||
    $(LINUX_DIR)/sound/soc/codecs/snd-soc-wm-adsp.ko \
 | 
					    $(LINUX_DIR)/sound/soc/codecs/snd-soc-wm-adsp.ko \
 | 
				
			||||||
 | 
				
			|||||||
@ -40,7 +40,7 @@ define KernelPackage/drm-vc4
 | 
				
			|||||||
    $(LINUX_DIR)/drivers/gpu/drm/vc4/vc4.ko \
 | 
					    $(LINUX_DIR)/drivers/gpu/drm/vc4/vc4.ko \
 | 
				
			||||||
    $(LINUX_DIR)/drivers/gpu/drm/drm_kms_helper.ko \
 | 
					    $(LINUX_DIR)/drivers/gpu/drm/drm_kms_helper.ko \
 | 
				
			||||||
    $(LINUX_DIR)/drivers/media/cec/cec.ko@lt5.10 \
 | 
					    $(LINUX_DIR)/drivers/media/cec/cec.ko@lt5.10 \
 | 
				
			||||||
    $(LINUX_DIR)/drivers/media/cec/core/cec.ko@ge5.10
 | 
					    $(LINUX_DIR)/drivers/media/cec/core/cec.ko
 | 
				
			||||||
  AUTOLOAD:=$(call AutoProbe,vc4)
 | 
					  AUTOLOAD:=$(call AutoProbe,vc4)
 | 
				
			||||||
endef
 | 
					endef
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,99 +0,0 @@
 | 
				
			|||||||
From c966ee565b122d840d7aac4c07c53b0d679d2d33 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Dan Pasanen <dan.pasanen@gmail.com>
 | 
					 | 
				
			||||||
Date: Thu, 21 Sep 2017 09:55:42 -0500
 | 
					 | 
				
			||||||
Subject: [PATCH] arm: partially revert
 | 
					 | 
				
			||||||
 702b94bff3c50542a6e4ab9a4f4cef093262fe65
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
* Re-expose some dmi APIs for use in VCSM
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 arch/arm/include/asm/cacheflush.h | 21 +++++++++++++++++++++
 | 
					 | 
				
			||||||
 arch/arm/include/asm/glue-cache.h |  2 ++
 | 
					 | 
				
			||||||
 arch/arm/mm/proc-macros.S         |  2 ++
 | 
					 | 
				
			||||||
 arch/arm/mm/proc-syms.c           |  3 +++
 | 
					 | 
				
			||||||
 4 files changed, 28 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/arch/arm/include/asm/cacheflush.h
 | 
					 | 
				
			||||||
+++ b/arch/arm/include/asm/cacheflush.h
 | 
					 | 
				
			||||||
@@ -91,6 +91,21 @@
 | 
					 | 
				
			||||||
  *	DMA Cache Coherency
 | 
					 | 
				
			||||||
  *	===================
 | 
					 | 
				
			||||||
  *
 | 
					 | 
				
			||||||
+ *	dma_inv_range(start, end)
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ *		Invalidate (discard) the specified virtual address range.
 | 
					 | 
				
			||||||
+ *		May not write back any entries.  If 'start' or 'end'
 | 
					 | 
				
			||||||
+ *		are not cache line aligned, those lines must be written
 | 
					 | 
				
			||||||
+ *		back.
 | 
					 | 
				
			||||||
+ *		- start  - virtual start address
 | 
					 | 
				
			||||||
+ *		- end    - virtual end address
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ *	dma_clean_range(start, end)
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ *		Clean (write back) the specified virtual address range.
 | 
					 | 
				
			||||||
+ *		- start  - virtual start address
 | 
					 | 
				
			||||||
+ *		- end    - virtual end address
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
  *	dma_flush_range(start, end)
 | 
					 | 
				
			||||||
  *
 | 
					 | 
				
			||||||
  *		Clean and invalidate the specified virtual address range.
 | 
					 | 
				
			||||||
@@ -112,6 +127,8 @@ struct cpu_cache_fns {
 | 
					 | 
				
			||||||
 	void (*dma_map_area)(const void *, size_t, int);
 | 
					 | 
				
			||||||
 	void (*dma_unmap_area)(const void *, size_t, int);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	void (*dma_inv_range)(const void *, const void *);
 | 
					 | 
				
			||||||
+	void (*dma_clean_range)(const void *, const void *);
 | 
					 | 
				
			||||||
 	void (*dma_flush_range)(const void *, const void *);
 | 
					 | 
				
			||||||
 } __no_randomize_layout;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -137,6 +154,8 @@ extern struct cpu_cache_fns cpu_cache;
 | 
					 | 
				
			||||||
  * is visible to DMA, or data written by DMA to system memory is
 | 
					 | 
				
			||||||
  * visible to the CPU.
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
+#define dmac_inv_range			cpu_cache.dma_inv_range
 | 
					 | 
				
			||||||
+#define dmac_clean_range		cpu_cache.dma_clean_range
 | 
					 | 
				
			||||||
 #define dmac_flush_range		cpu_cache.dma_flush_range
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #else
 | 
					 | 
				
			||||||
@@ -156,6 +175,8 @@ extern void __cpuc_flush_dcache_area(voi
 | 
					 | 
				
			||||||
  * is visible to DMA, or data written by DMA to system memory is
 | 
					 | 
				
			||||||
  * visible to the CPU.
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
+extern void dmac_inv_range(const void *, const void *);
 | 
					 | 
				
			||||||
+extern void dmac_clean_range(const void *, const void *);
 | 
					 | 
				
			||||||
 extern void dmac_flush_range(const void *, const void *);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #endif
 | 
					 | 
				
			||||||
--- a/arch/arm/include/asm/glue-cache.h
 | 
					 | 
				
			||||||
+++ b/arch/arm/include/asm/glue-cache.h
 | 
					 | 
				
			||||||
@@ -155,6 +155,8 @@ static inline void nop_dma_unmap_area(co
 | 
					 | 
				
			||||||
 #define __cpuc_coherent_user_range	__glue(_CACHE,_coherent_user_range)
 | 
					 | 
				
			||||||
 #define __cpuc_flush_dcache_area	__glue(_CACHE,_flush_kern_dcache_area)
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+#define dmac_inv_range			__glue(_CACHE,_dma_inv_range)
 | 
					 | 
				
			||||||
+#define dmac_clean_range		__glue(_CACHE,_dma_clean_range)
 | 
					 | 
				
			||||||
 #define dmac_flush_range		__glue(_CACHE,_dma_flush_range)
 | 
					 | 
				
			||||||
 #endif
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
--- a/arch/arm/mm/proc-macros.S
 | 
					 | 
				
			||||||
+++ b/arch/arm/mm/proc-macros.S
 | 
					 | 
				
			||||||
@@ -334,6 +334,8 @@ ENTRY(\name\()_cache_fns)
 | 
					 | 
				
			||||||
 	.long	\name\()_flush_kern_dcache_area
 | 
					 | 
				
			||||||
 	.long	\name\()_dma_map_area
 | 
					 | 
				
			||||||
 	.long	\name\()_dma_unmap_area
 | 
					 | 
				
			||||||
+	.long	\name\()_dma_inv_range
 | 
					 | 
				
			||||||
+	.long	\name\()_dma_clean_range
 | 
					 | 
				
			||||||
 	.long	\name\()_dma_flush_range
 | 
					 | 
				
			||||||
 	.size	\name\()_cache_fns, . - \name\()_cache_fns
 | 
					 | 
				
			||||||
 .endm
 | 
					 | 
				
			||||||
--- a/arch/arm/mm/proc-syms.c
 | 
					 | 
				
			||||||
+++ b/arch/arm/mm/proc-syms.c
 | 
					 | 
				
			||||||
@@ -27,6 +27,9 @@ EXPORT_SYMBOL(__cpuc_flush_user_all);
 | 
					 | 
				
			||||||
 EXPORT_SYMBOL(__cpuc_flush_user_range);
 | 
					 | 
				
			||||||
 EXPORT_SYMBOL(__cpuc_coherent_kern_range);
 | 
					 | 
				
			||||||
 EXPORT_SYMBOL(__cpuc_flush_dcache_area);
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL(dmac_inv_range);
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL(dmac_clean_range);
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL(dmac_flush_range);
 | 
					 | 
				
			||||||
 #else
 | 
					 | 
				
			||||||
 EXPORT_SYMBOL(cpu_cache);
 | 
					 | 
				
			||||||
 #endif
 | 
					 | 
				
			||||||
@ -1,56 +0,0 @@
 | 
				
			|||||||
From f4888774b6bf2f68fa2b389690eee07d7e8efdb9 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Mon, 29 Oct 2018 14:45:45 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] Revert "rtc: pcf8523: properly handle oscillator stop
 | 
					 | 
				
			||||||
 bit"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This reverts commit ede44c908d44b166a5b6bd7caacd105c2ff5a70f.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
See: https://github.com/raspberrypi/firmware/issues/1065
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/rtc/rtc-pcf8523.c | 25 ++++++++++++++++++++++---
 | 
					 | 
				
			||||||
 1 file changed, 22 insertions(+), 3 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/rtc/rtc-pcf8523.c
 | 
					 | 
				
			||||||
+++ b/drivers/rtc/rtc-pcf8523.c
 | 
					 | 
				
			||||||
@@ -205,8 +205,28 @@ static int pcf8523_rtc_read_time(struct
 | 
					 | 
				
			||||||
 	if (err < 0)
 | 
					 | 
				
			||||||
 		return err;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if (regs[0] & REG_SECONDS_OS)
 | 
					 | 
				
			||||||
-		return -EINVAL;
 | 
					 | 
				
			||||||
+	if (regs[0] & REG_SECONDS_OS) {
 | 
					 | 
				
			||||||
+		/*
 | 
					 | 
				
			||||||
+		 * If the oscillator was stopped, try to clear the flag. Upon
 | 
					 | 
				
			||||||
+		 * power-up the flag is always set, but if we cannot clear it
 | 
					 | 
				
			||||||
+		 * the oscillator isn't running properly for some reason. The
 | 
					 | 
				
			||||||
+		 * sensible thing therefore is to return an error, signalling
 | 
					 | 
				
			||||||
+		 * that the clock cannot be assumed to be correct.
 | 
					 | 
				
			||||||
+		 */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		regs[0] &= ~REG_SECONDS_OS;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		err = pcf8523_write(client, REG_SECONDS, regs[0]);
 | 
					 | 
				
			||||||
+		if (err < 0)
 | 
					 | 
				
			||||||
+			return err;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		err = pcf8523_read(client, REG_SECONDS, ®s[0]);
 | 
					 | 
				
			||||||
+		if (err < 0)
 | 
					 | 
				
			||||||
+			return err;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		if (regs[0] & REG_SECONDS_OS)
 | 
					 | 
				
			||||||
+			return -EAGAIN;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	tm->tm_sec = bcd2bin(regs[0] & 0x7f);
 | 
					 | 
				
			||||||
 	tm->tm_min = bcd2bin(regs[1] & 0x7f);
 | 
					 | 
				
			||||||
@@ -242,7 +262,6 @@ static int pcf8523_rtc_set_time(struct d
 | 
					 | 
				
			||||||
 		return err;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	regs[0] = REG_SECONDS;
 | 
					 | 
				
			||||||
-	/* This will purposely overwrite REG_SECONDS_OS */
 | 
					 | 
				
			||||||
 	regs[1] = bin2bcd(tm->tm_sec);
 | 
					 | 
				
			||||||
 	regs[2] = bin2bcd(tm->tm_min);
 | 
					 | 
				
			||||||
 	regs[3] = bin2bcd(tm->tm_hour);
 | 
					 | 
				
			||||||
@ -1,96 +0,0 @@
 | 
				
			|||||||
From 2aa4bd3751f6792bf00d59e4d9cd8a5550872cdb Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: popcornmix <popcornmix@gmail.com>
 | 
					 | 
				
			||||||
Date: Fri, 15 Mar 2019 21:11:10 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] Revert "staging: bcm2835-audio: Drop DT dependency"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This reverts commit b7491a9fca2dc2535b9dc922550a37c5baae9d3d.
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 .../vc04_services/bcm2835-audio/bcm2835.c     | 31 +++++++++++++------
 | 
					 | 
				
			||||||
 1 file changed, 22 insertions(+), 9 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/staging/vc04_services/bcm2835-audio/bcm2835.c
 | 
					 | 
				
			||||||
+++ b/drivers/staging/vc04_services/bcm2835-audio/bcm2835.c
 | 
					 | 
				
			||||||
@@ -6,13 +6,13 @@
 | 
					 | 
				
			||||||
 #include <linux/init.h>
 | 
					 | 
				
			||||||
 #include <linux/slab.h>
 | 
					 | 
				
			||||||
 #include <linux/module.h>
 | 
					 | 
				
			||||||
+#include <linux/of.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #include "bcm2835.h"
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static bool enable_hdmi;
 | 
					 | 
				
			||||||
 static bool enable_headphones;
 | 
					 | 
				
			||||||
 static bool enable_compat_alsa = true;
 | 
					 | 
				
			||||||
-static int num_channels = MAX_SUBSTREAMS;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 module_param(enable_hdmi, bool, 0444);
 | 
					 | 
				
			||||||
 MODULE_PARM_DESC(enable_hdmi, "Enables HDMI virtual audio device");
 | 
					 | 
				
			||||||
@@ -21,8 +21,6 @@ MODULE_PARM_DESC(enable_headphones, "Ena
 | 
					 | 
				
			||||||
 module_param(enable_compat_alsa, bool, 0444);
 | 
					 | 
				
			||||||
 MODULE_PARM_DESC(enable_compat_alsa,
 | 
					 | 
				
			||||||
 		 "Enables ALSA compatibility virtual audio device");
 | 
					 | 
				
			||||||
-module_param(num_channels, int, 0644);
 | 
					 | 
				
			||||||
-MODULE_PARM_DESC(num_channels, "Number of audio channels (default: 8)");
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static void bcm2835_devm_free_vchi_ctx(struct device *dev, void *res)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
@@ -296,19 +294,28 @@ static int snd_add_child_devices(struct
 | 
					 | 
				
			||||||
 static int snd_bcm2835_alsa_probe(struct platform_device *pdev)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct device *dev = &pdev->dev;
 | 
					 | 
				
			||||||
+	u32 numchans;
 | 
					 | 
				
			||||||
 	int err;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if (num_channels <= 0 || num_channels > MAX_SUBSTREAMS) {
 | 
					 | 
				
			||||||
-		num_channels = MAX_SUBSTREAMS;
 | 
					 | 
				
			||||||
-		dev_warn(dev, "Illegal num_channels value, will use %u\n",
 | 
					 | 
				
			||||||
-			 num_channels);
 | 
					 | 
				
			||||||
+	err = of_property_read_u32(dev->of_node, "brcm,pwm-channels",
 | 
					 | 
				
			||||||
+				   &numchans);
 | 
					 | 
				
			||||||
+	if (err) {
 | 
					 | 
				
			||||||
+		dev_err(dev, "Failed to get DT property 'brcm,pwm-channels'");
 | 
					 | 
				
			||||||
+		return err;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (numchans == 0 || numchans > MAX_SUBSTREAMS) {
 | 
					 | 
				
			||||||
+		numchans = MAX_SUBSTREAMS;
 | 
					 | 
				
			||||||
+		dev_warn(dev,
 | 
					 | 
				
			||||||
+			 "Illegal 'brcm,pwm-channels' value, will use %u\n",
 | 
					 | 
				
			||||||
+			 numchans);
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	err = bcm2835_devm_add_vchi_ctx(dev);
 | 
					 | 
				
			||||||
 	if (err)
 | 
					 | 
				
			||||||
 		return err;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	err = snd_add_child_devices(dev, num_channels);
 | 
					 | 
				
			||||||
+	err = snd_add_child_devices(dev, numchans);
 | 
					 | 
				
			||||||
 	if (err)
 | 
					 | 
				
			||||||
 		return err;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -330,6 +337,12 @@ static int snd_bcm2835_alsa_resume(struc
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #endif
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static const struct of_device_id snd_bcm2835_of_match_table[] = {
 | 
					 | 
				
			||||||
+	{ .compatible = "brcm,bcm2835-audio",},
 | 
					 | 
				
			||||||
+	{},
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+MODULE_DEVICE_TABLE(of, snd_bcm2835_of_match_table);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static struct platform_driver bcm2835_alsa_driver = {
 | 
					 | 
				
			||||||
 	.probe = snd_bcm2835_alsa_probe,
 | 
					 | 
				
			||||||
 #ifdef CONFIG_PM
 | 
					 | 
				
			||||||
@@ -338,6 +351,7 @@ static struct platform_driver bcm2835_al
 | 
					 | 
				
			||||||
 #endif
 | 
					 | 
				
			||||||
 	.driver = {
 | 
					 | 
				
			||||||
 		.name = "bcm2835_audio",
 | 
					 | 
				
			||||||
+		.of_match_table = snd_bcm2835_of_match_table,
 | 
					 | 
				
			||||||
 	},
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
 module_platform_driver(bcm2835_alsa_driver);
 | 
					 | 
				
			||||||
@@ -345,4 +359,3 @@ module_platform_driver(bcm2835_alsa_driv
 | 
					 | 
				
			||||||
 MODULE_AUTHOR("Dom Cobley");
 | 
					 | 
				
			||||||
 MODULE_DESCRIPTION("Alsa driver for BCM2835 chip");
 | 
					 | 
				
			||||||
 MODULE_LICENSE("GPL");
 | 
					 | 
				
			||||||
-MODULE_ALIAS("platform:bcm2835_audio");
 | 
					 | 
				
			||||||
@ -1,32 +0,0 @@
 | 
				
			|||||||
From 086a38a1e0eea3b7cbb207384b92d5dc82c62454 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.com>
 | 
					 | 
				
			||||||
Date: Mon, 20 Apr 2020 13:41:10 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] Revert "spi: spidev: Fix CS polarity if GPIO
 | 
					 | 
				
			||||||
 descriptors are used"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This reverts commit 83b2a8fe43bda0c11981ad6afa5dd0104d78be28.
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/spi/spidev.c | 5 -----
 | 
					 | 
				
			||||||
 1 file changed, 5 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/spi/spidev.c
 | 
					 | 
				
			||||||
+++ b/drivers/spi/spidev.c
 | 
					 | 
				
			||||||
@@ -402,7 +402,6 @@ spidev_ioctl(struct file *filp, unsigned
 | 
					 | 
				
			||||||
 		else
 | 
					 | 
				
			||||||
 			retval = get_user(tmp, (u32 __user *)arg);
 | 
					 | 
				
			||||||
 		if (retval == 0) {
 | 
					 | 
				
			||||||
-			struct spi_controller *ctlr = spi->controller;
 | 
					 | 
				
			||||||
 			u32	save = spi->mode;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 			if (tmp & ~SPI_MODE_MASK) {
 | 
					 | 
				
			||||||
@@ -410,10 +409,6 @@ spidev_ioctl(struct file *filp, unsigned
 | 
					 | 
				
			||||||
 				break;
 | 
					 | 
				
			||||||
 			}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-			if (ctlr->use_gpio_descriptors && ctlr->cs_gpiods &&
 | 
					 | 
				
			||||||
-			    ctlr->cs_gpiods[spi->chip_select])
 | 
					 | 
				
			||||||
-				tmp |= SPI_CS_HIGH;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 			tmp |= spi->mode & ~SPI_MODE_MASK;
 | 
					 | 
				
			||||||
 			spi->mode = (u16)tmp;
 | 
					 | 
				
			||||||
 			retval = spi_setup(spi);
 | 
					 | 
				
			||||||
@ -1,58 +0,0 @@
 | 
				
			|||||||
From a8fb0d43b8acd25d68a0d2c24fd0260393148447 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.com>
 | 
					 | 
				
			||||||
Date: Tue, 3 Nov 2020 11:49:53 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] Revert "mailbox: avoid timer start from callback"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This reverts commit c7dacf5b0f32957b24ef29df1207dc2cd8307743.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The Pi 400 shutdown/poweroff mechanism relies on being able to set
 | 
					 | 
				
			||||||
a GPIO on the expander in the pm_power_off handler, something that
 | 
					 | 
				
			||||||
requires two mailbox calls - GET_GPIO_STATE and SET_GPIO_STATE. A
 | 
					 | 
				
			||||||
recent kernel change introduces a reasonable possibility that the
 | 
					 | 
				
			||||||
GET call doesn't completes, and bisecting led to a commit from
 | 
					 | 
				
			||||||
October that changes the timer usage of the mailbox.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
My theory is that there is a race condition in the new code that breaks
 | 
					 | 
				
			||||||
the poll timer, but that it normally goes unnoticed because subsequent
 | 
					 | 
				
			||||||
mailbox activity wakes it up again. The power-off mailbox calls happen
 | 
					 | 
				
			||||||
at a time when other subsystems have been shut down, so if one of them
 | 
					 | 
				
			||||||
fails then there is nothing to allow it to recover.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
See: https://github.com/raspberrypi/linux/issues/3941
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/mailbox/mailbox.c | 12 +++++-------
 | 
					 | 
				
			||||||
 1 file changed, 5 insertions(+), 7 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/mailbox/mailbox.c
 | 
					 | 
				
			||||||
+++ b/drivers/mailbox/mailbox.c
 | 
					 | 
				
			||||||
@@ -82,12 +82,9 @@ static void msg_submit(struct mbox_chan
 | 
					 | 
				
			||||||
 exit:
 | 
					 | 
				
			||||||
 	spin_unlock_irqrestore(&chan->lock, flags);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	/* kick start the timer immediately to avoid delays */
 | 
					 | 
				
			||||||
-	if (!err && (chan->txdone_method & TXDONE_BY_POLL)) {
 | 
					 | 
				
			||||||
-		/* but only if not already active */
 | 
					 | 
				
			||||||
-		if (!hrtimer_active(&chan->mbox->poll_hrt))
 | 
					 | 
				
			||||||
-			hrtimer_start(&chan->mbox->poll_hrt, 0, HRTIMER_MODE_REL);
 | 
					 | 
				
			||||||
-	}
 | 
					 | 
				
			||||||
+	if (!err && (chan->txdone_method & TXDONE_BY_POLL))
 | 
					 | 
				
			||||||
+		/* kick start the timer immediately to avoid delays */
 | 
					 | 
				
			||||||
+		hrtimer_start(&chan->mbox->poll_hrt, 0, HRTIMER_MODE_REL);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static void tx_tick(struct mbox_chan *chan, int r)
 | 
					 | 
				
			||||||
@@ -125,10 +122,11 @@ static enum hrtimer_restart txdone_hrtim
 | 
					 | 
				
			||||||
 		struct mbox_chan *chan = &mbox->chans[i];
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 		if (chan->active_req && chan->cl) {
 | 
					 | 
				
			||||||
-			resched = true;
 | 
					 | 
				
			||||||
 			txdone = chan->mbox->ops->last_tx_done(chan);
 | 
					 | 
				
			||||||
 			if (txdone)
 | 
					 | 
				
			||||||
 				tx_tick(chan, 0);
 | 
					 | 
				
			||||||
+			else
 | 
					 | 
				
			||||||
+				resched = true;
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@ -1,47 +0,0 @@
 | 
				
			|||||||
From dbfae4876cd4c8525a0100f19307f16cf7fb384a Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Steve Glendinning <steve.glendinning@smsc.com>
 | 
					 | 
				
			||||||
Date: Thu, 19 Feb 2015 18:47:12 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] smsx95xx: fix crimes against truesize
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
smsc95xx is adjusting truesize when it shouldn't, and following a recent patch from Eric this is now triggering warnings.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This patch stops smsc95xx from changing truesize.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Steve Glendinning <steve.glendinning@smsc.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/usb/smsc95xx.c | 10 ++++++++--
 | 
					 | 
				
			||||||
 1 file changed, 8 insertions(+), 2 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/usb/smsc95xx.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/usb/smsc95xx.c
 | 
					 | 
				
			||||||
@@ -67,6 +67,10 @@ static bool turbo_mode = true;
 | 
					 | 
				
			||||||
 module_param(turbo_mode, bool, 0644);
 | 
					 | 
				
			||||||
 MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction");
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static bool truesize_mode = false;
 | 
					 | 
				
			||||||
+module_param(truesize_mode, bool, 0644);
 | 
					 | 
				
			||||||
+MODULE_PARM_DESC(truesize_mode, "Report larger truesize value");
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static int __must_check __smsc95xx_read_reg(struct usbnet *dev, u32 index,
 | 
					 | 
				
			||||||
 					    u32 *data, int in_pm)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
@@ -1839,7 +1843,8 @@ static int smsc95xx_rx_fixup(struct usbn
 | 
					 | 
				
			||||||
 				if (dev->net->features & NETIF_F_RXCSUM)
 | 
					 | 
				
			||||||
 					smsc95xx_rx_csum_offload(skb);
 | 
					 | 
				
			||||||
 				skb_trim(skb, skb->len - 4); /* remove fcs */
 | 
					 | 
				
			||||||
-				skb->truesize = size + sizeof(struct sk_buff);
 | 
					 | 
				
			||||||
+				if (truesize_mode)
 | 
					 | 
				
			||||||
+					skb->truesize = size + sizeof(struct sk_buff);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 				return 1;
 | 
					 | 
				
			||||||
 			}
 | 
					 | 
				
			||||||
@@ -1857,7 +1862,8 @@ static int smsc95xx_rx_fixup(struct usbn
 | 
					 | 
				
			||||||
 			if (dev->net->features & NETIF_F_RXCSUM)
 | 
					 | 
				
			||||||
 				smsc95xx_rx_csum_offload(ax_skb);
 | 
					 | 
				
			||||||
 			skb_trim(ax_skb, ax_skb->len - 4); /* remove fcs */
 | 
					 | 
				
			||||||
-			ax_skb->truesize = size + sizeof(struct sk_buff);
 | 
					 | 
				
			||||||
+			if (truesize_mode)
 | 
					 | 
				
			||||||
+				ax_skb->truesize = size + sizeof(struct sk_buff);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 			usbnet_skb_return(dev, ax_skb);
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
@ -1,43 +0,0 @@
 | 
				
			|||||||
From 5792c081bb959c57b381439e40d07c919193a993 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Sam Nazarko <email@samnazarko.co.uk>
 | 
					 | 
				
			||||||
Date: Fri, 1 Apr 2016 17:27:21 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] smsc95xx: Experimental: Enable turbo_mode and
 | 
					 | 
				
			||||||
 packetsize=2560 by default
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
See: http://forum.kodi.tv/showthread.php?tid=285288
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/usb/smsc95xx.c | 14 +++++++++-----
 | 
					 | 
				
			||||||
 1 file changed, 9 insertions(+), 5 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/usb/smsc95xx.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/usb/smsc95xx.c
 | 
					 | 
				
			||||||
@@ -71,6 +71,10 @@ static bool truesize_mode = false;
 | 
					 | 
				
			||||||
 module_param(truesize_mode, bool, 0644);
 | 
					 | 
				
			||||||
 MODULE_PARM_DESC(truesize_mode, "Report larger truesize value");
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static int packetsize = 2560;
 | 
					 | 
				
			||||||
+module_param(packetsize, int, 0644);
 | 
					 | 
				
			||||||
+MODULE_PARM_DESC(packetsize, "Override the RX URB packet size");
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static int __must_check __smsc95xx_read_reg(struct usbnet *dev, u32 index,
 | 
					 | 
				
			||||||
 					    u32 *data, int in_pm)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
@@ -917,13 +921,13 @@ static int smsc95xx_reset(struct usbnet
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	if (!turbo_mode) {
 | 
					 | 
				
			||||||
 		burst_cap = 0;
 | 
					 | 
				
			||||||
-		dev->rx_urb_size = MAX_SINGLE_PACKET_SIZE;
 | 
					 | 
				
			||||||
+		dev->rx_urb_size = packetsize ? packetsize : MAX_SINGLE_PACKET_SIZE;
 | 
					 | 
				
			||||||
 	} else if (dev->udev->speed == USB_SPEED_HIGH) {
 | 
					 | 
				
			||||||
-		burst_cap = DEFAULT_HS_BURST_CAP_SIZE / HS_USB_PKT_SIZE;
 | 
					 | 
				
			||||||
-		dev->rx_urb_size = DEFAULT_HS_BURST_CAP_SIZE;
 | 
					 | 
				
			||||||
+		dev->rx_urb_size = packetsize ? packetsize : DEFAULT_HS_BURST_CAP_SIZE;
 | 
					 | 
				
			||||||
+		burst_cap = dev->rx_urb_size / HS_USB_PKT_SIZE;
 | 
					 | 
				
			||||||
 	} else {
 | 
					 | 
				
			||||||
-		burst_cap = DEFAULT_FS_BURST_CAP_SIZE / FS_USB_PKT_SIZE;
 | 
					 | 
				
			||||||
-		dev->rx_urb_size = DEFAULT_FS_BURST_CAP_SIZE;
 | 
					 | 
				
			||||||
+		dev->rx_urb_size = packetsize ? packetsize : DEFAULT_FS_BURST_CAP_SIZE;
 | 
					 | 
				
			||||||
+		burst_cap = dev->rx_urb_size / FS_USB_PKT_SIZE;
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	netif_dbg(dev, ifup, dev->net, "rx_urb_size=%ld\n",
 | 
					 | 
				
			||||||
@ -1,96 +0,0 @@
 | 
				
			|||||||
From a5e86e4e7cc86e6a24844d758d2258c8b23f18f0 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: popcornmix <popcornmix@gmail.com>
 | 
					 | 
				
			||||||
Date: Tue, 26 Mar 2013 17:26:38 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] Allow mac address to be set in smsc95xx
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: popcornmix <popcornmix@gmail.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/usb/smsc95xx.c | 56 ++++++++++++++++++++++++++++++++++++++
 | 
					 | 
				
			||||||
 1 file changed, 56 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/usb/smsc95xx.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/usb/smsc95xx.c
 | 
					 | 
				
			||||||
@@ -50,6 +50,7 @@
 | 
					 | 
				
			||||||
 #define SUSPEND_SUSPEND3		(0x08)
 | 
					 | 
				
			||||||
 #define SUSPEND_ALLMODES		(SUSPEND_SUSPEND0 | SUSPEND_SUSPEND1 | \
 | 
					 | 
				
			||||||
 					 SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3)
 | 
					 | 
				
			||||||
+#define MAC_ADDR_LEN                    (6)
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 struct smsc95xx_priv {
 | 
					 | 
				
			||||||
 	u32 mac_cr;
 | 
					 | 
				
			||||||
@@ -75,6 +76,10 @@ static int packetsize = 2560;
 | 
					 | 
				
			||||||
 module_param(packetsize, int, 0644);
 | 
					 | 
				
			||||||
 MODULE_PARM_DESC(packetsize, "Override the RX URB packet size");
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static char *macaddr = ":";
 | 
					 | 
				
			||||||
+module_param(macaddr, charp, 0);
 | 
					 | 
				
			||||||
+MODULE_PARM_DESC(macaddr, "MAC address");
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static int __must_check __smsc95xx_read_reg(struct usbnet *dev, u32 index,
 | 
					 | 
				
			||||||
 					    u32 *data, int in_pm)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
@@ -773,6 +778,53 @@ static int smsc95xx_ioctl(struct net_dev
 | 
					 | 
				
			||||||
 	return phy_mii_ioctl(netdev->phydev, rq, cmd);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+/* Check the macaddr module parameter for a MAC address */
 | 
					 | 
				
			||||||
+static int smsc95xx_is_macaddr_param(struct usbnet *dev, u8 *dev_mac)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+       int i, j, got_num, num;
 | 
					 | 
				
			||||||
+       u8 mtbl[MAC_ADDR_LEN];
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+       if (macaddr[0] == ':')
 | 
					 | 
				
			||||||
+               return 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+       i = 0;
 | 
					 | 
				
			||||||
+       j = 0;
 | 
					 | 
				
			||||||
+       num = 0;
 | 
					 | 
				
			||||||
+       got_num = 0;
 | 
					 | 
				
			||||||
+       while (j < MAC_ADDR_LEN) {
 | 
					 | 
				
			||||||
+               if (macaddr[i] && macaddr[i] != ':') {
 | 
					 | 
				
			||||||
+                       got_num++;
 | 
					 | 
				
			||||||
+                       if ('0' <= macaddr[i] && macaddr[i] <= '9')
 | 
					 | 
				
			||||||
+                               num = num * 16 + macaddr[i] - '0';
 | 
					 | 
				
			||||||
+                       else if ('A' <= macaddr[i] && macaddr[i] <= 'F')
 | 
					 | 
				
			||||||
+                               num = num * 16 + 10 + macaddr[i] - 'A';
 | 
					 | 
				
			||||||
+                       else if ('a' <= macaddr[i] && macaddr[i] <= 'f')
 | 
					 | 
				
			||||||
+                               num = num * 16 + 10 + macaddr[i] - 'a';
 | 
					 | 
				
			||||||
+                       else
 | 
					 | 
				
			||||||
+                               break;
 | 
					 | 
				
			||||||
+                       i++;
 | 
					 | 
				
			||||||
+               } else if (got_num == 2) {
 | 
					 | 
				
			||||||
+                       mtbl[j++] = (u8) num;
 | 
					 | 
				
			||||||
+                       num = 0;
 | 
					 | 
				
			||||||
+                       got_num = 0;
 | 
					 | 
				
			||||||
+                       i++;
 | 
					 | 
				
			||||||
+               } else {
 | 
					 | 
				
			||||||
+                       break;
 | 
					 | 
				
			||||||
+               }
 | 
					 | 
				
			||||||
+       }
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+       if (j == MAC_ADDR_LEN) {
 | 
					 | 
				
			||||||
+               netif_dbg(dev, ifup, dev->net, "Overriding MAC address with: "
 | 
					 | 
				
			||||||
+               "%02x:%02x:%02x:%02x:%02x:%02x\n", mtbl[0], mtbl[1], mtbl[2],
 | 
					 | 
				
			||||||
+                                               mtbl[3], mtbl[4], mtbl[5]);
 | 
					 | 
				
			||||||
+               for (i = 0; i < MAC_ADDR_LEN; i++)
 | 
					 | 
				
			||||||
+                       dev_mac[i] = mtbl[i];
 | 
					 | 
				
			||||||
+               return 1;
 | 
					 | 
				
			||||||
+       } else {
 | 
					 | 
				
			||||||
+               return 0;
 | 
					 | 
				
			||||||
+       }
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static void smsc95xx_init_mac_address(struct usbnet *dev)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	/* maybe the boot loader passed the MAC address in devicetree */
 | 
					 | 
				
			||||||
@@ -795,6 +847,10 @@ static void smsc95xx_init_mac_address(st
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	/* Check module parameters */
 | 
					 | 
				
			||||||
+	if (smsc95xx_is_macaddr_param(dev, dev->net->dev_addr))
 | 
					 | 
				
			||||||
+		return;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	/* no useful static MAC address found. generate a random one */
 | 
					 | 
				
			||||||
 	eth_hw_addr_random(dev->net);
 | 
					 | 
				
			||||||
 	netif_dbg(dev, ifup, dev->net, "MAC address set to eth_random_addr\n");
 | 
					 | 
				
			||||||
@ -1,28 +0,0 @@
 | 
				
			|||||||
From f146f2bb597fe00b6c2e5da169a766dc8ab2a4fa Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Fri, 13 Mar 2015 12:43:36 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] Protect __release_resource against resources without
 | 
					 | 
				
			||||||
 parents
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Without this patch, removing a device tree overlay can crash here.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 kernel/resource.c | 6 ++++++
 | 
					 | 
				
			||||||
 1 file changed, 6 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/kernel/resource.c
 | 
					 | 
				
			||||||
+++ b/kernel/resource.c
 | 
					 | 
				
			||||||
@@ -214,6 +214,12 @@ static int __release_resource(struct res
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct resource *tmp, **p, *chd;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	if (!old->parent) {
 | 
					 | 
				
			||||||
+		WARN(old->sibling, "sibling but no parent");
 | 
					 | 
				
			||||||
+		if (old->sibling)
 | 
					 | 
				
			||||||
+			return -EINVAL;
 | 
					 | 
				
			||||||
+		return 0;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
 	p = &old->parent->child;
 | 
					 | 
				
			||||||
 	for (;;) {
 | 
					 | 
				
			||||||
 		tmp = *p;
 | 
					 | 
				
			||||||
@ -1,24 +0,0 @@
 | 
				
			|||||||
From 55a3a6691b480c57613f9db3a0e1aca02b7f68c1 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Thu, 9 Feb 2017 14:33:30 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] irq-bcm2836: Avoid "Invalid trigger warning"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Initialise the level for each IRQ to avoid a warning from the
 | 
					 | 
				
			||||||
arm arch timer code.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/irqchip/irq-bcm2836.c | 2 +-
 | 
					 | 
				
			||||||
 1 file changed, 1 insertion(+), 1 deletion(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/irqchip/irq-bcm2836.c
 | 
					 | 
				
			||||||
+++ b/drivers/irqchip/irq-bcm2836.c
 | 
					 | 
				
			||||||
@@ -128,7 +128,7 @@ static int bcm2836_map(struct irq_domain
 | 
					 | 
				
			||||||
 	irq_set_percpu_devid(irq);
 | 
					 | 
				
			||||||
 	irq_domain_set_info(d, irq, hw, chip, d->host_data,
 | 
					 | 
				
			||||||
 			    handle_percpu_devid_irq, NULL, NULL);
 | 
					 | 
				
			||||||
-	irq_set_status_flags(irq, IRQ_NOAUTOEN);
 | 
					 | 
				
			||||||
+	irq_set_status_flags(irq, IRQ_NOAUTOEN | IRQ_TYPE_LEVEL_LOW);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	return 0;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
@ -1,127 +0,0 @@
 | 
				
			|||||||
From ad12646921360036adf7ecdcf1325b9a880b316a Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= <noralf@tronnes.org>
 | 
					 | 
				
			||||||
Date: Fri, 12 Jun 2015 19:01:05 +0200
 | 
					 | 
				
			||||||
Subject: [PATCH] irqchip: bcm2835: Add FIQ support
 | 
					 | 
				
			||||||
MIME-Version: 1.0
 | 
					 | 
				
			||||||
Content-Type: text/plain; charset=UTF-8
 | 
					 | 
				
			||||||
Content-Transfer-Encoding: 8bit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add a duplicate irq range with an offset on the hwirq's so the
 | 
					 | 
				
			||||||
driver can detect that enable_fiq() is used.
 | 
					 | 
				
			||||||
Tested with downstream dwc_otg USB controller driver.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
 | 
					 | 
				
			||||||
Reviewed-by: Eric Anholt <eric@anholt.net>
 | 
					 | 
				
			||||||
Acked-by: Stephen Warren <swarren@wwwdotorg.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 arch/arm/mach-bcm/Kconfig     |  1 +
 | 
					 | 
				
			||||||
 drivers/irqchip/irq-bcm2835.c | 51 +++++++++++++++++++++++++++++++----
 | 
					 | 
				
			||||||
 2 files changed, 47 insertions(+), 5 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/arch/arm/mach-bcm/Kconfig
 | 
					 | 
				
			||||||
+++ b/arch/arm/mach-bcm/Kconfig
 | 
					 | 
				
			||||||
@@ -161,6 +161,7 @@ config ARCH_BCM2835
 | 
					 | 
				
			||||||
 	select ARM_TIMER_SP804
 | 
					 | 
				
			||||||
 	select HAVE_ARM_ARCH_TIMER if ARCH_MULTI_V7
 | 
					 | 
				
			||||||
 	select BCM2835_TIMER
 | 
					 | 
				
			||||||
+	select FIQ
 | 
					 | 
				
			||||||
 	select PINCTRL
 | 
					 | 
				
			||||||
 	select PINCTRL_BCM2835
 | 
					 | 
				
			||||||
 	select MFD_CORE
 | 
					 | 
				
			||||||
--- a/drivers/irqchip/irq-bcm2835.c
 | 
					 | 
				
			||||||
+++ b/drivers/irqchip/irq-bcm2835.c
 | 
					 | 
				
			||||||
@@ -45,7 +45,7 @@
 | 
					 | 
				
			||||||
 #include <asm/exception.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /* Put the bank and irq (32 bits) into the hwirq */
 | 
					 | 
				
			||||||
-#define MAKE_HWIRQ(b, n)	((b << 5) | (n))
 | 
					 | 
				
			||||||
+#define MAKE_HWIRQ(b, n)	(((b) << 5) | (n))
 | 
					 | 
				
			||||||
 #define HWIRQ_BANK(i)		(i >> 5)
 | 
					 | 
				
			||||||
 #define HWIRQ_BIT(i)		BIT(i & 0x1f)
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -62,9 +62,13 @@
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #define REG_FIQ_CONTROL		0x0c
 | 
					 | 
				
			||||||
 #define FIQ_CONTROL_ENABLE	BIT(7)
 | 
					 | 
				
			||||||
+#define REG_FIQ_ENABLE		FIQ_CONTROL_ENABLE
 | 
					 | 
				
			||||||
+#define REG_FIQ_DISABLE	0
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #define NR_BANKS		3
 | 
					 | 
				
			||||||
 #define IRQS_PER_BANK		32
 | 
					 | 
				
			||||||
+#define NUMBER_IRQS		MAKE_HWIRQ(NR_BANKS, 0)
 | 
					 | 
				
			||||||
+#define FIQ_START		(NR_IRQS_BANK0 + MAKE_HWIRQ(NR_BANKS - 1, 0))
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static const int reg_pending[] __initconst = { 0x00, 0x04, 0x08 };
 | 
					 | 
				
			||||||
 static const int reg_enable[] __initconst = { 0x18, 0x10, 0x14 };
 | 
					 | 
				
			||||||
@@ -89,14 +93,38 @@ static void __exception_irq_entry bcm283
 | 
					 | 
				
			||||||
 	struct pt_regs *regs);
 | 
					 | 
				
			||||||
 static void bcm2836_chained_handle_irq(struct irq_desc *desc);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static inline unsigned int hwirq_to_fiq(unsigned long hwirq)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	hwirq -= NUMBER_IRQS;
 | 
					 | 
				
			||||||
+	/*
 | 
					 | 
				
			||||||
+	 * The hwirq numbering used in this driver is:
 | 
					 | 
				
			||||||
+	 *   BASE (0-7) GPU1 (32-63) GPU2 (64-95).
 | 
					 | 
				
			||||||
+	 * This differ from the one used in the FIQ register:
 | 
					 | 
				
			||||||
+	 *   GPU1 (0-31) GPU2 (32-63) BASE (64-71)
 | 
					 | 
				
			||||||
+	 */
 | 
					 | 
				
			||||||
+	if (hwirq >= 32)
 | 
					 | 
				
			||||||
+		return hwirq - 32;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return hwirq + 64;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static void armctrl_mask_irq(struct irq_data *d)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
-	writel_relaxed(HWIRQ_BIT(d->hwirq), intc.disable[HWIRQ_BANK(d->hwirq)]);
 | 
					 | 
				
			||||||
+	if (d->hwirq >= NUMBER_IRQS)
 | 
					 | 
				
			||||||
+		writel_relaxed(REG_FIQ_DISABLE, intc.base + REG_FIQ_CONTROL);
 | 
					 | 
				
			||||||
+	else
 | 
					 | 
				
			||||||
+		writel_relaxed(HWIRQ_BIT(d->hwirq),
 | 
					 | 
				
			||||||
+			       intc.disable[HWIRQ_BANK(d->hwirq)]);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static void armctrl_unmask_irq(struct irq_data *d)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
-	writel_relaxed(HWIRQ_BIT(d->hwirq), intc.enable[HWIRQ_BANK(d->hwirq)]);
 | 
					 | 
				
			||||||
+	if (d->hwirq >= NUMBER_IRQS)
 | 
					 | 
				
			||||||
+		writel_relaxed(REG_FIQ_ENABLE | hwirq_to_fiq(d->hwirq),
 | 
					 | 
				
			||||||
+			       intc.base + REG_FIQ_CONTROL);
 | 
					 | 
				
			||||||
+	else
 | 
					 | 
				
			||||||
+		writel_relaxed(HWIRQ_BIT(d->hwirq),
 | 
					 | 
				
			||||||
+			       intc.enable[HWIRQ_BANK(d->hwirq)]);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static struct irq_chip armctrl_chip = {
 | 
					 | 
				
			||||||
@@ -142,8 +170,9 @@ static int __init armctrl_of_init(struct
 | 
					 | 
				
			||||||
 	if (!base)
 | 
					 | 
				
			||||||
 		panic("%pOF: unable to map IC registers\n", node);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	intc.domain = irq_domain_add_linear(node, MAKE_HWIRQ(NR_BANKS, 0),
 | 
					 | 
				
			||||||
-			&armctrl_ops, NULL);
 | 
					 | 
				
			||||||
+	intc.base = base;
 | 
					 | 
				
			||||||
+	intc.domain = irq_domain_add_linear(node, NUMBER_IRQS * 2,
 | 
					 | 
				
			||||||
+					    &armctrl_ops, NULL);
 | 
					 | 
				
			||||||
 	if (!intc.domain)
 | 
					 | 
				
			||||||
 		panic("%pOF: unable to create IRQ domain\n", node);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -186,6 +215,18 @@ static int __init armctrl_of_init(struct
 | 
					 | 
				
			||||||
 		set_handle_irq(bcm2835_handle_irq);
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	/* Make a duplicate irq range which is used to enable FIQ */
 | 
					 | 
				
			||||||
+	for (b = 0; b < NR_BANKS; b++) {
 | 
					 | 
				
			||||||
+		for (i = 0; i < bank_irqs[b]; i++) {
 | 
					 | 
				
			||||||
+			irq = irq_create_mapping(intc.domain,
 | 
					 | 
				
			||||||
+					MAKE_HWIRQ(b, i) + NUMBER_IRQS);
 | 
					 | 
				
			||||||
+			BUG_ON(irq <= 0);
 | 
					 | 
				
			||||||
+			irq_set_chip(irq, &armctrl_chip);
 | 
					 | 
				
			||||||
+			set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	init_FIQ(FIQ_START);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	return 0;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@ -1,99 +0,0 @@
 | 
				
			|||||||
From 18774f96f2766eb711d462842626ae603110c18e Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= <noralf@tronnes.org>
 | 
					 | 
				
			||||||
Date: Fri, 23 Oct 2015 16:26:55 +0200
 | 
					 | 
				
			||||||
Subject: [PATCH] irqchip: irq-bcm2835: Add 2836 FIQ support
 | 
					 | 
				
			||||||
MIME-Version: 1.0
 | 
					 | 
				
			||||||
Content-Type: text/plain; charset=UTF-8
 | 
					 | 
				
			||||||
Content-Transfer-Encoding: 8bit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/irqchip/irq-bcm2835.c | 43 +++++++++++++++++++++++++++++++++--
 | 
					 | 
				
			||||||
 1 file changed, 41 insertions(+), 2 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/irqchip/irq-bcm2835.c
 | 
					 | 
				
			||||||
+++ b/drivers/irqchip/irq-bcm2835.c
 | 
					 | 
				
			||||||
@@ -41,8 +41,11 @@
 | 
					 | 
				
			||||||
 #include <linux/of_irq.h>
 | 
					 | 
				
			||||||
 #include <linux/irqchip.h>
 | 
					 | 
				
			||||||
 #include <linux/irqdomain.h>
 | 
					 | 
				
			||||||
+#include <linux/mfd/syscon.h>
 | 
					 | 
				
			||||||
+#include <linux/regmap.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #include <asm/exception.h>
 | 
					 | 
				
			||||||
+#include <asm/mach/irq.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /* Put the bank and irq (32 bits) into the hwirq */
 | 
					 | 
				
			||||||
 #define MAKE_HWIRQ(b, n)	(((b) << 5) | (n))
 | 
					 | 
				
			||||||
@@ -60,6 +63,9 @@
 | 
					 | 
				
			||||||
 #define BANK0_VALID_MASK	(BANK0_HWIRQ_MASK | BANK1_HWIRQ | BANK2_HWIRQ \
 | 
					 | 
				
			||||||
 					| SHORTCUT1_MASK | SHORTCUT2_MASK)
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+#undef ARM_LOCAL_GPU_INT_ROUTING
 | 
					 | 
				
			||||||
+#define ARM_LOCAL_GPU_INT_ROUTING 0x0c
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 #define REG_FIQ_CONTROL		0x0c
 | 
					 | 
				
			||||||
 #define FIQ_CONTROL_ENABLE	BIT(7)
 | 
					 | 
				
			||||||
 #define REG_FIQ_ENABLE		FIQ_CONTROL_ENABLE
 | 
					 | 
				
			||||||
@@ -86,6 +92,7 @@ struct armctrl_ic {
 | 
					 | 
				
			||||||
 	void __iomem *enable[NR_BANKS];
 | 
					 | 
				
			||||||
 	void __iomem *disable[NR_BANKS];
 | 
					 | 
				
			||||||
 	struct irq_domain *domain;
 | 
					 | 
				
			||||||
+	struct regmap *local_regmap;
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static struct armctrl_ic intc __read_mostly;
 | 
					 | 
				
			||||||
@@ -119,12 +126,35 @@ static void armctrl_mask_irq(struct irq_
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static void armctrl_unmask_irq(struct irq_data *d)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
-	if (d->hwirq >= NUMBER_IRQS)
 | 
					 | 
				
			||||||
+	if (d->hwirq >= NUMBER_IRQS) {
 | 
					 | 
				
			||||||
+		if (num_online_cpus() > 1) {
 | 
					 | 
				
			||||||
+			unsigned int data;
 | 
					 | 
				
			||||||
+			int ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+			if (!intc.local_regmap) {
 | 
					 | 
				
			||||||
+				pr_err("FIQ is disabled due to missing regmap\n");
 | 
					 | 
				
			||||||
+				return;
 | 
					 | 
				
			||||||
+			}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+			ret = regmap_read(intc.local_regmap,
 | 
					 | 
				
			||||||
+					  ARM_LOCAL_GPU_INT_ROUTING, &data);
 | 
					 | 
				
			||||||
+			if (ret) {
 | 
					 | 
				
			||||||
+				pr_err("Failed to read int routing %d\n", ret);
 | 
					 | 
				
			||||||
+				return;
 | 
					 | 
				
			||||||
+			}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+			data &= ~0xc;
 | 
					 | 
				
			||||||
+			data |= (1 << 2);
 | 
					 | 
				
			||||||
+			regmap_write(intc.local_regmap,
 | 
					 | 
				
			||||||
+				     ARM_LOCAL_GPU_INT_ROUTING, data);
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 		writel_relaxed(REG_FIQ_ENABLE | hwirq_to_fiq(d->hwirq),
 | 
					 | 
				
			||||||
 			       intc.base + REG_FIQ_CONTROL);
 | 
					 | 
				
			||||||
-	else
 | 
					 | 
				
			||||||
+	} else {
 | 
					 | 
				
			||||||
 		writel_relaxed(HWIRQ_BIT(d->hwirq),
 | 
					 | 
				
			||||||
 			       intc.enable[HWIRQ_BANK(d->hwirq)]);
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static struct irq_chip armctrl_chip = {
 | 
					 | 
				
			||||||
@@ -215,6 +245,15 @@ static int __init armctrl_of_init(struct
 | 
					 | 
				
			||||||
 		set_handle_irq(bcm2835_handle_irq);
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	if (is_2836) {
 | 
					 | 
				
			||||||
+		intc.local_regmap =
 | 
					 | 
				
			||||||
+			syscon_regmap_lookup_by_compatible("brcm,bcm2836-arm-local");
 | 
					 | 
				
			||||||
+		if (IS_ERR(intc.local_regmap)) {
 | 
					 | 
				
			||||||
+			pr_err("Failed to get local register map. FIQ is disabled for cpus > 1\n");
 | 
					 | 
				
			||||||
+			intc.local_regmap = NULL;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	/* Make a duplicate irq range which is used to enable FIQ */
 | 
					 | 
				
			||||||
 	for (b = 0; b < NR_BANKS; b++) {
 | 
					 | 
				
			||||||
 		for (i = 0; i < bank_irqs[b]; i++) {
 | 
					 | 
				
			||||||
@ -1,24 +0,0 @@
 | 
				
			|||||||
From 8469be136aebf9fe06746ec47e4495c77d5522f5 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Tue, 14 Jul 2015 10:26:09 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] spi: spidev: Completely disable the spidev warning
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
An alternative strategy would be to use "rpi,spidev" instead, but that
 | 
					 | 
				
			||||||
would require many Raspberry Pi Device Tree changes.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/spi/spidev.c | 2 +-
 | 
					 | 
				
			||||||
 1 file changed, 1 insertion(+), 1 deletion(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/spi/spidev.c
 | 
					 | 
				
			||||||
+++ b/drivers/spi/spidev.c
 | 
					 | 
				
			||||||
@@ -733,7 +733,7 @@ static int spidev_probe(struct spi_devic
 | 
					 | 
				
			||||||
 	 * compatible string, it is a Linux implementation thing
 | 
					 | 
				
			||||||
 	 * rather than a description of the hardware.
 | 
					 | 
				
			||||||
 	 */
 | 
					 | 
				
			||||||
-	WARN(spi->dev.of_node &&
 | 
					 | 
				
			||||||
+	WARN(0 && spi->dev.of_node &&
 | 
					 | 
				
			||||||
 	     of_device_is_compatible(spi->dev.of_node, "spidev"),
 | 
					 | 
				
			||||||
 	     "%pOF: buggy DT: spidev listed directly in DT\n", spi->dev.of_node);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@ -1,101 +0,0 @@
 | 
				
			|||||||
From 1fafff0383c50bbbb2d5bb3205923904f3f55ce8 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= <noralf@tronnes.org>
 | 
					 | 
				
			||||||
Date: Sat, 3 Oct 2015 22:22:55 +0200
 | 
					 | 
				
			||||||
Subject: [PATCH] dmaengine: bcm2835: Load driver early and support
 | 
					 | 
				
			||||||
 legacy API
 | 
					 | 
				
			||||||
MIME-Version: 1.0
 | 
					 | 
				
			||||||
Content-Type: text/plain; charset=UTF-8
 | 
					 | 
				
			||||||
Content-Transfer-Encoding: 8bit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Load driver early since at least bcm2708_fb doesn't support deferred
 | 
					 | 
				
			||||||
probing and even if it did, we don't want the video driver deferred.
 | 
					 | 
				
			||||||
Support the legacy DMA API which is needed by bcm2708_fb.
 | 
					 | 
				
			||||||
Don't mask out channel 2.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/dma/Kconfig       |  2 +-
 | 
					 | 
				
			||||||
 drivers/dma/bcm2835-dma.c | 26 +++++++++++++++++++++++++-
 | 
					 | 
				
			||||||
 2 files changed, 26 insertions(+), 2 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/dma/Kconfig
 | 
					 | 
				
			||||||
+++ b/drivers/dma/Kconfig
 | 
					 | 
				
			||||||
@@ -134,7 +134,7 @@ config COH901318
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 config DMA_BCM2835
 | 
					 | 
				
			||||||
 	tristate "BCM2835 DMA engine support"
 | 
					 | 
				
			||||||
-	depends on ARCH_BCM2835
 | 
					 | 
				
			||||||
+	depends on ARCH_BCM2835 || ARCH_BCM2708 || ARCH_BCM2709
 | 
					 | 
				
			||||||
 	select DMA_ENGINE
 | 
					 | 
				
			||||||
 	select DMA_VIRTUAL_CHANNELS
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
--- a/drivers/dma/bcm2835-dma.c
 | 
					 | 
				
			||||||
+++ b/drivers/dma/bcm2835-dma.c
 | 
					 | 
				
			||||||
@@ -25,6 +25,7 @@
 | 
					 | 
				
			||||||
 #include <linux/interrupt.h>
 | 
					 | 
				
			||||||
 #include <linux/list.h>
 | 
					 | 
				
			||||||
 #include <linux/module.h>
 | 
					 | 
				
			||||||
+#include <linux/platform_data/dma-bcm2708.h>
 | 
					 | 
				
			||||||
 #include <linux/platform_device.h>
 | 
					 | 
				
			||||||
 #include <linux/slab.h>
 | 
					 | 
				
			||||||
 #include <linux/io.h>
 | 
					 | 
				
			||||||
@@ -36,6 +37,7 @@
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #define BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED 14
 | 
					 | 
				
			||||||
 #define BCM2835_DMA_CHAN_NAME_SIZE 8
 | 
					 | 
				
			||||||
+#define BCM2835_DMA_BULK_MASK  BIT(0)
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /**
 | 
					 | 
				
			||||||
  * struct bcm2835_dmadev - BCM2835 DMA controller
 | 
					 | 
				
			||||||
@@ -906,6 +908,9 @@ static int bcm2835_dma_probe(struct plat
 | 
					 | 
				
			||||||
 	base = devm_ioremap_resource(&pdev->dev, res);
 | 
					 | 
				
			||||||
 	if (IS_ERR(base))
 | 
					 | 
				
			||||||
 		return PTR_ERR(base);
 | 
					 | 
				
			||||||
+	rc = bcm_dmaman_probe(pdev, base, BCM2835_DMA_BULK_MASK);
 | 
					 | 
				
			||||||
+	if (rc)
 | 
					 | 
				
			||||||
+		dev_err(&pdev->dev, "Failed to initialize the legacy API\n");
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	od->base = base;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -951,6 +956,9 @@ static int bcm2835_dma_probe(struct plat
 | 
					 | 
				
			||||||
 		goto err_no_dma;
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	/* Channel 0 is used by the legacy API */
 | 
					 | 
				
			||||||
+	chans_available &= ~BCM2835_DMA_BULK_MASK;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	/* get irqs for each channel that we support */
 | 
					 | 
				
			||||||
 	for (i = 0; i <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; i++) {
 | 
					 | 
				
			||||||
 		/* skip masked out channels */
 | 
					 | 
				
			||||||
@@ -1025,6 +1033,7 @@ static int bcm2835_dma_remove(struct pla
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct bcm2835_dmadev *od = platform_get_drvdata(pdev);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	bcm_dmaman_remove(pdev);
 | 
					 | 
				
			||||||
 	dma_async_device_unregister(&od->ddev);
 | 
					 | 
				
			||||||
 	bcm2835_dma_free(od);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -1040,7 +1049,22 @@ static struct platform_driver bcm2835_dm
 | 
					 | 
				
			||||||
 	},
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-module_platform_driver(bcm2835_dma_driver);
 | 
					 | 
				
			||||||
+static int bcm2835_dma_init(void)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return platform_driver_register(&bcm2835_dma_driver);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void bcm2835_dma_exit(void)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	platform_driver_unregister(&bcm2835_dma_driver);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ * Load after serial driver (arch_initcall) so we see the messages if it fails,
 | 
					 | 
				
			||||||
+ * but before drivers (module_init) that need a DMA channel.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+subsys_initcall(bcm2835_dma_init);
 | 
					 | 
				
			||||||
+module_exit(bcm2835_dma_exit);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 MODULE_ALIAS("platform:bcm2835-dma");
 | 
					 | 
				
			||||||
 MODULE_DESCRIPTION("BCM2835 DMA engine driver");
 | 
					 | 
				
			||||||
@ -1,27 +0,0 @@
 | 
				
			|||||||
From b7ad81911a06fd8047ca36c07bd49d8317833502 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: popcornmix <popcornmix@gmail.com>
 | 
					 | 
				
			||||||
Date: Mon, 25 Jan 2016 17:25:12 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] firmware: Updated mailbox header
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 include/soc/bcm2835/raspberrypi-firmware.h | 3 +++
 | 
					 | 
				
			||||||
 1 file changed, 3 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/include/soc/bcm2835/raspberrypi-firmware.h
 | 
					 | 
				
			||||||
+++ b/include/soc/bcm2835/raspberrypi-firmware.h
 | 
					 | 
				
			||||||
@@ -9,6 +9,8 @@
 | 
					 | 
				
			||||||
 #include <linux/types.h>
 | 
					 | 
				
			||||||
 #include <linux/of_device.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+#define RPI_FIRMWARE_CHAN_FB		1
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 struct rpi_firmware;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 enum rpi_firmware_property_status {
 | 
					 | 
				
			||||||
@@ -161,5 +163,6 @@ static inline struct rpi_firmware *rpi_f
 | 
					 | 
				
			||||||
 	return NULL;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 #endif
 | 
					 | 
				
			||||||
+int rpi_firmware_transaction(struct rpi_firmware *fw, u32 chan, u32 data);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #endif /* __SOC_RASPBERRY_FIRMWARE_H__ */
 | 
					 | 
				
			||||||
@ -1,20 +0,0 @@
 | 
				
			|||||||
From d9fa2c594a6dfd5f538c50eaa6a06449d06c5a8e Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Wed, 15 Jun 2016 16:48:41 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] rtc: Add SPI alias for pcf2123 driver
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Without this alias, Device Tree won't cause the driver
 | 
					 | 
				
			||||||
to be loaded.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
See: https://github.com/raspberrypi/linux/pull/1510
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/rtc/rtc-pcf2123.c | 1 +
 | 
					 | 
				
			||||||
 1 file changed, 1 insertion(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/rtc/rtc-pcf2123.c
 | 
					 | 
				
			||||||
+++ b/drivers/rtc/rtc-pcf2123.c
 | 
					 | 
				
			||||||
@@ -465,3 +465,4 @@ module_spi_driver(pcf2123_driver);
 | 
					 | 
				
			||||||
 MODULE_AUTHOR("Chris Verges <chrisv@cyberswitching.com>");
 | 
					 | 
				
			||||||
 MODULE_DESCRIPTION("NXP PCF2123 RTC driver");
 | 
					 | 
				
			||||||
 MODULE_LICENSE("GPL");
 | 
					 | 
				
			||||||
+MODULE_ALIAS("spi:rtc-pcf2123");
 | 
					 | 
				
			||||||
@ -1,102 +0,0 @@
 | 
				
			|||||||
From 351b9cbfc6aed837c7e23462d7109372de22e2bb Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= <noralf@tronnes.org>
 | 
					 | 
				
			||||||
Date: Fri, 7 Oct 2016 16:50:59 +0200
 | 
					 | 
				
			||||||
Subject: [PATCH] watchdog: bcm2835: Support setting reboot partition
 | 
					 | 
				
			||||||
MIME-Version: 1.0
 | 
					 | 
				
			||||||
Content-Type: text/plain; charset=UTF-8
 | 
					 | 
				
			||||||
Content-Transfer-Encoding: 8bit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The Raspberry Pi firmware looks at the RSTS register to know which
 | 
					 | 
				
			||||||
partition to boot from. The reboot syscall command
 | 
					 | 
				
			||||||
LINUX_REBOOT_CMD_RESTART2 supports passing in a string argument.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add support for passing in a partition number 0..63 to boot from.
 | 
					 | 
				
			||||||
Partition 63 is a special partiton indicating halt.
 | 
					 | 
				
			||||||
If the partition doesn't exist, the firmware falls back to partition 0.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/watchdog/bcm2835_wdt.c | 49 +++++++++++++++++++---------------
 | 
					 | 
				
			||||||
 1 file changed, 27 insertions(+), 22 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/watchdog/bcm2835_wdt.c
 | 
					 | 
				
			||||||
+++ b/drivers/watchdog/bcm2835_wdt.c
 | 
					 | 
				
			||||||
@@ -32,13 +32,7 @@
 | 
					 | 
				
			||||||
 #define PM_RSTC_WRCFG_SET		0x00000030
 | 
					 | 
				
			||||||
 #define PM_RSTC_WRCFG_FULL_RESET	0x00000020
 | 
					 | 
				
			||||||
 #define PM_RSTC_RESET			0x00000102
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-/*
 | 
					 | 
				
			||||||
- * The Raspberry Pi firmware uses the RSTS register to know which partition
 | 
					 | 
				
			||||||
- * to boot from. The partition value is spread into bits 0, 2, 4, 6, 8, 10.
 | 
					 | 
				
			||||||
- * Partition 63 is a special partition used by the firmware to indicate halt.
 | 
					 | 
				
			||||||
- */
 | 
					 | 
				
			||||||
-#define PM_RSTS_RASPBERRYPI_HALT	0x555
 | 
					 | 
				
			||||||
+#define PM_RSTS_PARTITION_CLR          0xfffffaaa
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #define SECS_TO_WDOG_TICKS(x) ((x) << 16)
 | 
					 | 
				
			||||||
 #define WDOG_TICKS_TO_SECS(x) ((x) >> 16)
 | 
					 | 
				
			||||||
@@ -97,9 +91,24 @@ static unsigned int bcm2835_wdt_get_time
 | 
					 | 
				
			||||||
 	return WDOG_TICKS_TO_SECS(ret & PM_WDOG_TIME_SET);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-static void __bcm2835_restart(struct bcm2835_wdt *wdt)
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ * The Raspberry Pi firmware uses the RSTS register to know which partiton
 | 
					 | 
				
			||||||
+ * to boot from. The partiton value is spread into bits 0, 2, 4, 6, 8, 10.
 | 
					 | 
				
			||||||
+ * Partiton 63 is a special partition used by the firmware to indicate halt.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void __bcm2835_restart(struct bcm2835_wdt *wdt, u8 partition)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
-	u32 val;
 | 
					 | 
				
			||||||
+	u32 val, rsts;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	rsts = (partition & BIT(0)) | ((partition & BIT(1)) << 1) |
 | 
					 | 
				
			||||||
+	       ((partition & BIT(2)) << 2) | ((partition & BIT(3)) << 3) |
 | 
					 | 
				
			||||||
+	       ((partition & BIT(4)) << 4) | ((partition & BIT(5)) << 5);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	val = readl_relaxed(wdt->base + PM_RSTS);
 | 
					 | 
				
			||||||
+	val &= PM_RSTS_PARTITION_CLR;
 | 
					 | 
				
			||||||
+	val |= PM_PASSWORD | rsts;
 | 
					 | 
				
			||||||
+	writel_relaxed(val, wdt->base + PM_RSTS);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* use a timeout of 10 ticks (~150us) */
 | 
					 | 
				
			||||||
 	writel_relaxed(10 | PM_PASSWORD, wdt->base + PM_WDOG);
 | 
					 | 
				
			||||||
@@ -117,7 +126,13 @@ static int bcm2835_restart(struct watchd
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct bcm2835_wdt *wdt = watchdog_get_drvdata(wdog);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	__bcm2835_restart(wdt);
 | 
					 | 
				
			||||||
+	unsigned long long val;
 | 
					 | 
				
			||||||
+	u8 partition = 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (data && !kstrtoull(data, 0, &val) && val <= 63)
 | 
					 | 
				
			||||||
+		partition = val;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	__bcm2835_restart(wdt, partition);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	return 0;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
@@ -152,19 +167,9 @@ static struct watchdog_device bcm2835_wd
 | 
					 | 
				
			||||||
 static void bcm2835_power_off(void)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct bcm2835_wdt *wdt = bcm2835_power_off_wdt;
 | 
					 | 
				
			||||||
-	u32 val;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	/*
 | 
					 | 
				
			||||||
-	 * We set the watchdog hard reset bit here to distinguish this reset
 | 
					 | 
				
			||||||
-	 * from the normal (full) reset. bootcode.bin will not reboot after a
 | 
					 | 
				
			||||||
-	 * hard reset.
 | 
					 | 
				
			||||||
-	 */
 | 
					 | 
				
			||||||
-	val = readl_relaxed(wdt->base + PM_RSTS);
 | 
					 | 
				
			||||||
-	val |= PM_PASSWORD | PM_RSTS_RASPBERRYPI_HALT;
 | 
					 | 
				
			||||||
-	writel_relaxed(val, wdt->base + PM_RSTS);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	/* Continue with normal reset mechanism */
 | 
					 | 
				
			||||||
-	__bcm2835_restart(wdt);
 | 
					 | 
				
			||||||
+	/* Partition 63 tells the firmware that this is a halt */
 | 
					 | 
				
			||||||
+	__bcm2835_restart(wdt, 63);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static int bcm2835_wdt_probe(struct platform_device *pdev)
 | 
					 | 
				
			||||||
@ -1,23 +0,0 @@
 | 
				
			|||||||
From e722eb6ced1ad7a162782fe10dd5d4225a6b4e0d Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: popcornmix <popcornmix@gmail.com>
 | 
					 | 
				
			||||||
Date: Tue, 5 Apr 2016 19:40:12 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] reboot: Use power off rather than busy spinning when
 | 
					 | 
				
			||||||
 halt is requested
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 arch/arm/kernel/reboot.c | 4 +---
 | 
					 | 
				
			||||||
 1 file changed, 1 insertion(+), 3 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/arch/arm/kernel/reboot.c
 | 
					 | 
				
			||||||
+++ b/arch/arm/kernel/reboot.c
 | 
					 | 
				
			||||||
@@ -102,9 +102,7 @@ void machine_shutdown(void)
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
 void machine_halt(void)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
-	local_irq_disable();
 | 
					 | 
				
			||||||
-	smp_send_stop();
 | 
					 | 
				
			||||||
-	while (1);
 | 
					 | 
				
			||||||
+	machine_power_off();
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /*
 | 
					 | 
				
			||||||
@ -1,19 +0,0 @@
 | 
				
			|||||||
From 0bc33dc51825662d4fc7a46c5e622de00e5cbf80 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: popcornmix <popcornmix@gmail.com>
 | 
					 | 
				
			||||||
Date: Wed, 9 Nov 2016 13:02:52 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] bcm: Make RASPBERRYPI_POWER depend on PM
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/soc/bcm/Kconfig | 1 +
 | 
					 | 
				
			||||||
 1 file changed, 1 insertion(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/soc/bcm/Kconfig
 | 
					 | 
				
			||||||
+++ b/drivers/soc/bcm/Kconfig
 | 
					 | 
				
			||||||
@@ -17,6 +17,7 @@ config RASPBERRYPI_POWER
 | 
					 | 
				
			||||||
 	bool "Raspberry Pi power domain driver"
 | 
					 | 
				
			||||||
 	depends on ARCH_BCM2835 || (COMPILE_TEST && OF)
 | 
					 | 
				
			||||||
 	depends on RASPBERRYPI_FIRMWARE=y
 | 
					 | 
				
			||||||
+	depends on PM
 | 
					 | 
				
			||||||
 	select PM_GENERIC_DOMAINS if PM
 | 
					 | 
				
			||||||
 	help
 | 
					 | 
				
			||||||
 	  This enables support for the RPi power domains which can be enabled
 | 
					 | 
				
			||||||
@ -1,45 +0,0 @@
 | 
				
			|||||||
From 41e34cd05d40eca6674e25eb9a85e5f7992d4e83 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Martin Sperl <kernel@martin.sperl.org>
 | 
					 | 
				
			||||||
Date: Fri, 2 Sep 2016 16:45:27 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] Register the clocks early during the boot process, so
 | 
					 | 
				
			||||||
 that special/critical clocks can get enabled early on in the boot process
 | 
					 | 
				
			||||||
 avoiding the risk of disabling a clock, pll_divider or pll when a claiming
 | 
					 | 
				
			||||||
 driver fails to install propperly - maybe it needs to defer.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Martin Sperl <kernel@martin.sperl.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/clk/bcm/clk-bcm2835.c | 15 +++++++++++++--
 | 
					 | 
				
			||||||
 1 file changed, 13 insertions(+), 2 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/clk/bcm/clk-bcm2835.c
 | 
					 | 
				
			||||||
+++ b/drivers/clk/bcm/clk-bcm2835.c
 | 
					 | 
				
			||||||
@@ -2290,8 +2290,15 @@ static int bcm2835_clk_probe(struct plat
 | 
					 | 
				
			||||||
 	if (ret)
 | 
					 | 
				
			||||||
 		return ret;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
 | 
					 | 
				
			||||||
+	ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
 | 
					 | 
				
			||||||
 				      &cprman->onecell);
 | 
					 | 
				
			||||||
+	if (ret)
 | 
					 | 
				
			||||||
+		return ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* note that we have registered all the clocks */
 | 
					 | 
				
			||||||
+	dev_dbg(dev, "registered %d clocks\n", asize);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static const struct cprman_plat_data cprman_bcm2835_plat_data = {
 | 
					 | 
				
			||||||
@@ -2317,7 +2324,11 @@ static struct platform_driver bcm2835_cl
 | 
					 | 
				
			||||||
 	.probe          = bcm2835_clk_probe,
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-builtin_platform_driver(bcm2835_clk_driver);
 | 
					 | 
				
			||||||
+static int __init __bcm2835_clk_driver_init(void)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return platform_driver_register(&bcm2835_clk_driver);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+core_initcall(__bcm2835_clk_driver_init);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 MODULE_AUTHOR("Eric Anholt <eric@anholt.net>");
 | 
					 | 
				
			||||||
 MODULE_DESCRIPTION("BCM2835 clock driver");
 | 
					 | 
				
			||||||
@ -1,25 +0,0 @@
 | 
				
			|||||||
From 102d9fbd0a8d762835a9f9a7917e9eace3a9d84e Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: popcornmix <popcornmix@gmail.com>
 | 
					 | 
				
			||||||
Date: Tue, 6 Dec 2016 17:05:39 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] bcm2835-rng: Avoid initialising if already enabled
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Avoids the 0x40000 cycles of warmup again if firmware has already used it
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/char/hw_random/bcm2835-rng.c | 6 ++++--
 | 
					 | 
				
			||||||
 1 file changed, 4 insertions(+), 2 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/char/hw_random/bcm2835-rng.c
 | 
					 | 
				
			||||||
+++ b/drivers/char/hw_random/bcm2835-rng.c
 | 
					 | 
				
			||||||
@@ -102,8 +102,10 @@ static int bcm2835_rng_init(struct hwrng
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* set warm-up count & enable */
 | 
					 | 
				
			||||||
-	rng_writel(priv, RNG_WARMUP_COUNT, RNG_STATUS);
 | 
					 | 
				
			||||||
-	rng_writel(priv, RNG_RBGEN, RNG_CTRL);
 | 
					 | 
				
			||||||
+	if (!(rng_readl(priv, RNG_CTRL) & RNG_RBGEN)) {
 | 
					 | 
				
			||||||
+		rng_writel(priv, RNG_WARMUP_COUNT, RNG_STATUS);
 | 
					 | 
				
			||||||
+		rng_writel(priv, RNG_RBGEN, RNG_CTRL);
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	return ret;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
@ -1,28 +0,0 @@
 | 
				
			|||||||
From 3af9c4ed5775b7675e679a80a381eafe2ce726ae Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Mon, 13 Feb 2017 17:20:08 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] clk-bcm2835: Mark used PLLs and dividers CRITICAL
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The VPU configures and relies on several PLLs and dividers. Mark all
 | 
					 | 
				
			||||||
enabled dividers and their PLLs as CRITICAL to prevent the kernel from
 | 
					 | 
				
			||||||
switching them off.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/clk/bcm/clk-bcm2835.c | 5 +++++
 | 
					 | 
				
			||||||
 1 file changed, 5 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/clk/bcm/clk-bcm2835.c
 | 
					 | 
				
			||||||
+++ b/drivers/clk/bcm/clk-bcm2835.c
 | 
					 | 
				
			||||||
@@ -1379,6 +1379,11 @@ bcm2835_register_pll_divider(struct bcm2
 | 
					 | 
				
			||||||
 	divider->div.hw.init = &init;
 | 
					 | 
				
			||||||
 	divider->div.table = NULL;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	if (!(cprman_read(cprman, data->cm_reg) & data->hold_mask)) {
 | 
					 | 
				
			||||||
+		init.flags |= CLK_IS_CRITICAL;
 | 
					 | 
				
			||||||
+		divider->div.flags |= CLK_IS_CRITICAL;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	divider->cprman = cprman;
 | 
					 | 
				
			||||||
 	divider->data = divider_data;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@ -1,120 +0,0 @@
 | 
				
			|||||||
From 05cae664e29e510111cf24f2beb1c31cba09bcce Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Mon, 13 Feb 2017 17:20:08 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] clk-bcm2835: Add claim-clocks property
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The claim-clocks property can be used to prevent PLLs and dividers
 | 
					 | 
				
			||||||
from being marked as critical. It contains a vector of clock IDs,
 | 
					 | 
				
			||||||
as defined by dt-bindings/clock/bcm2835.h.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Use this mechanism to claim PLLD_DSI0, PLLD_DSI1, PLLH_AUX and
 | 
					 | 
				
			||||||
PLLH_PIX for the vc4_kms_v3d driver.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/clk/bcm/clk-bcm2835.c | 45 ++++++++++++++++++++++++++++++++---
 | 
					 | 
				
			||||||
 1 file changed, 42 insertions(+), 3 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/clk/bcm/clk-bcm2835.c
 | 
					 | 
				
			||||||
+++ b/drivers/clk/bcm/clk-bcm2835.c
 | 
					 | 
				
			||||||
@@ -1307,6 +1307,8 @@ static const struct clk_ops bcm2835_vpu_
 | 
					 | 
				
			||||||
 	.debug_init = bcm2835_clock_debug_init,
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static bool bcm2835_clk_is_claimed(const char *name);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static struct clk_hw *bcm2835_register_pll(struct bcm2835_cprman *cprman,
 | 
					 | 
				
			||||||
 					   const void *data)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
@@ -1324,6 +1326,9 @@ static struct clk_hw *bcm2835_register_p
 | 
					 | 
				
			||||||
 	init.ops = &bcm2835_pll_clk_ops;
 | 
					 | 
				
			||||||
 	init.flags = pll_data->flags | CLK_IGNORE_UNUSED;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	if (!bcm2835_clk_is_claimed(pll_data->name))
 | 
					 | 
				
			||||||
+		init.flags |= CLK_IS_CRITICAL;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
 | 
					 | 
				
			||||||
 	if (!pll)
 | 
					 | 
				
			||||||
 		return NULL;
 | 
					 | 
				
			||||||
@@ -1379,9 +1384,11 @@ bcm2835_register_pll_divider(struct bcm2
 | 
					 | 
				
			||||||
 	divider->div.hw.init = &init;
 | 
					 | 
				
			||||||
 	divider->div.table = NULL;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if (!(cprman_read(cprman, data->cm_reg) & data->hold_mask)) {
 | 
					 | 
				
			||||||
-		init.flags |= CLK_IS_CRITICAL;
 | 
					 | 
				
			||||||
-		divider->div.flags |= CLK_IS_CRITICAL;
 | 
					 | 
				
			||||||
+	if (!(cprman_read(cprman, divider_data->cm_reg) & divider_data->hold_mask)) {
 | 
					 | 
				
			||||||
+		if (!bcm2835_clk_is_claimed(divider_data->source_pll))
 | 
					 | 
				
			||||||
+			init.flags |= CLK_IS_CRITICAL;
 | 
					 | 
				
			||||||
+		if (!bcm2835_clk_is_claimed(divider_data->name))
 | 
					 | 
				
			||||||
+			divider->div.flags |= CLK_IS_CRITICAL;
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	divider->cprman = cprman;
 | 
					 | 
				
			||||||
@@ -1438,6 +1445,15 @@ static struct clk_hw *bcm2835_register_c
 | 
					 | 
				
			||||||
 	init.flags = clock_data->flags | CLK_IGNORE_UNUSED;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/*
 | 
					 | 
				
			||||||
+	 * Some GPIO clocks for ethernet/wifi PLLs are marked as
 | 
					 | 
				
			||||||
+	 * critical (since some platforms use them), but if the
 | 
					 | 
				
			||||||
+	 * firmware didn't have them turned on then they clearly
 | 
					 | 
				
			||||||
+	 * aren't actually critical.
 | 
					 | 
				
			||||||
+	 */
 | 
					 | 
				
			||||||
+	if ((cprman_read(cprman, clock_data->ctl_reg) & CM_ENABLE) == 0)
 | 
					 | 
				
			||||||
+		init.flags &= ~CLK_IS_CRITICAL;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/*
 | 
					 | 
				
			||||||
 	 * Pass the CLK_SET_RATE_PARENT flag if we are allowed to propagate
 | 
					 | 
				
			||||||
 	 * rate changes on at least of the parents.
 | 
					 | 
				
			||||||
 	 */
 | 
					 | 
				
			||||||
@@ -2216,6 +2232,8 @@ static const struct bcm2835_clk_desc clk
 | 
					 | 
				
			||||||
 		.ctl_reg = CM_PERIICTL),
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static bool bcm2835_clk_claimed[ARRAY_SIZE(clk_desc_array)];
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 /*
 | 
					 | 
				
			||||||
  * Permanently take a reference on the parent of the SDRAM clock.
 | 
					 | 
				
			||||||
  *
 | 
					 | 
				
			||||||
@@ -2235,6 +2253,19 @@ static int bcm2835_mark_sdc_parent_criti
 | 
					 | 
				
			||||||
 	return clk_prepare_enable(parent);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static bool bcm2835_clk_is_claimed(const char *name)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int i;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	for (i = 0; i < ARRAY_SIZE(clk_desc_array); i++) {
 | 
					 | 
				
			||||||
+		const char *clk_name = *(const char **)(clk_desc_array[i].data);
 | 
					 | 
				
			||||||
+		if (!strcmp(name, clk_name))
 | 
					 | 
				
			||||||
+		    return bcm2835_clk_claimed[i];
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return false;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static int bcm2835_clk_probe(struct platform_device *pdev)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct device *dev = &pdev->dev;
 | 
					 | 
				
			||||||
@@ -2244,6 +2275,7 @@ static int bcm2835_clk_probe(struct plat
 | 
					 | 
				
			||||||
 	const size_t asize = ARRAY_SIZE(clk_desc_array);
 | 
					 | 
				
			||||||
 	const struct cprman_plat_data *pdata;
 | 
					 | 
				
			||||||
 	size_t i;
 | 
					 | 
				
			||||||
+	u32 clk_id;
 | 
					 | 
				
			||||||
 	int ret;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	pdata = of_device_get_match_data(&pdev->dev);
 | 
					 | 
				
			||||||
@@ -2262,6 +2294,13 @@ static int bcm2835_clk_probe(struct plat
 | 
					 | 
				
			||||||
 	if (IS_ERR(cprman->regs))
 | 
					 | 
				
			||||||
 		return PTR_ERR(cprman->regs);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	memset(bcm2835_clk_claimed, 0, sizeof(bcm2835_clk_claimed));
 | 
					 | 
				
			||||||
+	for (i = 0;
 | 
					 | 
				
			||||||
+	     !of_property_read_u32_index(pdev->dev.of_node, "claim-clocks",
 | 
					 | 
				
			||||||
+					 i, &clk_id);
 | 
					 | 
				
			||||||
+	     i++)
 | 
					 | 
				
			||||||
+		bcm2835_clk_claimed[clk_id]= true;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	memcpy(cprman->real_parent_names, cprman_parent_names,
 | 
					 | 
				
			||||||
 	       sizeof(cprman_parent_names));
 | 
					 | 
				
			||||||
 	of_clk_parent_fill(dev->of_node, cprman->real_parent_names,
 | 
					 | 
				
			||||||
@ -1,115 +0,0 @@
 | 
				
			|||||||
From 6ac8a6c58b51d02780b7a1fc882df33ae2560798 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Mon, 6 Mar 2017 09:06:18 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] clk-bcm2835: Read max core clock from firmware
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The VPU is responsible for managing the core clock, usually under
 | 
					 | 
				
			||||||
direction from the bcm2835-cpufreq driver but not via the clk-bcm2835
 | 
					 | 
				
			||||||
driver. Since the core frequency can change without warning, it is
 | 
					 | 
				
			||||||
safer to report the maximum clock rate to users of the core clock -
 | 
					 | 
				
			||||||
I2C, SPI and the mini UART - to err on the safe side when calculating
 | 
					 | 
				
			||||||
clock divisors.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
If the DT node for the clock driver includes a reference to the
 | 
					 | 
				
			||||||
firmware node, use the firmware API to query the maximum core clock
 | 
					 | 
				
			||||||
instead of reading the divider registers.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Prior to this patch, a "100KHz" I2C bus was sometimes clocked at about
 | 
					 | 
				
			||||||
160KHz. In particular, switching to the 4.9 kernel was likely to break
 | 
					 | 
				
			||||||
SenseHAT usage on a Pi3.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/clk/bcm/clk-bcm2835.c | 39 ++++++++++++++++++++++++++++++++++-
 | 
					 | 
				
			||||||
 1 file changed, 38 insertions(+), 1 deletion(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/clk/bcm/clk-bcm2835.c
 | 
					 | 
				
			||||||
+++ b/drivers/clk/bcm/clk-bcm2835.c
 | 
					 | 
				
			||||||
@@ -35,6 +35,7 @@
 | 
					 | 
				
			||||||
 #include <linux/platform_device.h>
 | 
					 | 
				
			||||||
 #include <linux/slab.h>
 | 
					 | 
				
			||||||
 #include <dt-bindings/clock/bcm2835.h>
 | 
					 | 
				
			||||||
+#include <soc/bcm2835/raspberrypi-firmware.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #define CM_PASSWORD		0x5a000000
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -295,6 +296,8 @@
 | 
					 | 
				
			||||||
 #define SOC_BCM2711		BIT(1)
 | 
					 | 
				
			||||||
 #define SOC_ALL			(SOC_BCM2835 | SOC_BCM2711)
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+#define VCMSG_ID_CORE_CLOCK     4
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 /*
 | 
					 | 
				
			||||||
  * Names of clocks used within the driver that need to be replaced
 | 
					 | 
				
			||||||
  * with an external parent's name.  This array is in the order that
 | 
					 | 
				
			||||||
@@ -313,6 +316,7 @@ static const char *const cprman_parent_n
 | 
					 | 
				
			||||||
 struct bcm2835_cprman {
 | 
					 | 
				
			||||||
 	struct device *dev;
 | 
					 | 
				
			||||||
 	void __iomem *regs;
 | 
					 | 
				
			||||||
+	struct rpi_firmware *fw;
 | 
					 | 
				
			||||||
 	spinlock_t regs_lock; /* spinlock for all clocks */
 | 
					 | 
				
			||||||
 	unsigned int soc;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -1011,6 +1015,30 @@ static unsigned long bcm2835_clock_get_r
 | 
					 | 
				
			||||||
 	return bcm2835_clock_rate_from_divisor(clock, parent_rate, div);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static unsigned long bcm2835_clock_get_rate_vpu(struct clk_hw *hw,
 | 
					 | 
				
			||||||
+						unsigned long parent_rate)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw);
 | 
					 | 
				
			||||||
+	struct bcm2835_cprman *cprman = clock->cprman;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (cprman->fw) {
 | 
					 | 
				
			||||||
+		struct {
 | 
					 | 
				
			||||||
+			u32 id;
 | 
					 | 
				
			||||||
+			u32 val;
 | 
					 | 
				
			||||||
+		} packet;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		packet.id = VCMSG_ID_CORE_CLOCK;
 | 
					 | 
				
			||||||
+		packet.val = 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		if (!rpi_firmware_property(cprman->fw,
 | 
					 | 
				
			||||||
+					   RPI_FIRMWARE_GET_MAX_CLOCK_RATE,
 | 
					 | 
				
			||||||
+					   &packet, sizeof(packet)))
 | 
					 | 
				
			||||||
+			return packet.val;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return bcm2835_clock_get_rate(hw, parent_rate);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static void bcm2835_clock_wait_busy(struct bcm2835_clock *clock)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct bcm2835_cprman *cprman = clock->cprman;
 | 
					 | 
				
			||||||
@@ -1299,7 +1327,7 @@ static int bcm2835_vpu_clock_is_on(struc
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
 static const struct clk_ops bcm2835_vpu_clock_clk_ops = {
 | 
					 | 
				
			||||||
 	.is_prepared = bcm2835_vpu_clock_is_on,
 | 
					 | 
				
			||||||
-	.recalc_rate = bcm2835_clock_get_rate,
 | 
					 | 
				
			||||||
+	.recalc_rate = bcm2835_clock_get_rate_vpu,
 | 
					 | 
				
			||||||
 	.set_rate = bcm2835_clock_set_rate,
 | 
					 | 
				
			||||||
 	.determine_rate = bcm2835_clock_determine_rate,
 | 
					 | 
				
			||||||
 	.set_parent = bcm2835_clock_set_parent,
 | 
					 | 
				
			||||||
@@ -2274,6 +2302,7 @@ static int bcm2835_clk_probe(struct plat
 | 
					 | 
				
			||||||
 	const struct bcm2835_clk_desc *desc;
 | 
					 | 
				
			||||||
 	const size_t asize = ARRAY_SIZE(clk_desc_array);
 | 
					 | 
				
			||||||
 	const struct cprman_plat_data *pdata;
 | 
					 | 
				
			||||||
+	struct device_node *fw_node;
 | 
					 | 
				
			||||||
 	size_t i;
 | 
					 | 
				
			||||||
 	u32 clk_id;
 | 
					 | 
				
			||||||
 	int ret;
 | 
					 | 
				
			||||||
@@ -2294,6 +2323,14 @@ static int bcm2835_clk_probe(struct plat
 | 
					 | 
				
			||||||
 	if (IS_ERR(cprman->regs))
 | 
					 | 
				
			||||||
 		return PTR_ERR(cprman->regs);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	fw_node = of_parse_phandle(dev->of_node, "firmware", 0);
 | 
					 | 
				
			||||||
+	if (fw_node) {
 | 
					 | 
				
			||||||
+		struct rpi_firmware *fw = rpi_firmware_get(NULL);
 | 
					 | 
				
			||||||
+		if (!fw)
 | 
					 | 
				
			||||||
+			return -EPROBE_DEFER;
 | 
					 | 
				
			||||||
+		cprman->fw = fw;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	memset(bcm2835_clk_claimed, 0, sizeof(bcm2835_clk_claimed));
 | 
					 | 
				
			||||||
 	for (i = 0;
 | 
					 | 
				
			||||||
 	     !of_property_read_u32_index(pdev->dev.of_node, "claim-clocks",
 | 
					 | 
				
			||||||
@ -1,35 +0,0 @@
 | 
				
			|||||||
From 2a0e0c15c001bfde3666f159552e414217fe44ac Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Thu, 9 Feb 2017 14:36:44 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] sound: Demote deferral errors to INFO level
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
At present there is no mechanism to specify driver load order,
 | 
					 | 
				
			||||||
which can lead to deferrals and repeated retries until successful.
 | 
					 | 
				
			||||||
Since this situation is expected, reduce the dmesg level to
 | 
					 | 
				
			||||||
INFO and mention that the operation will be retried.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 sound/soc/soc-core.c | 4 ++--
 | 
					 | 
				
			||||||
 1 file changed, 2 insertions(+), 2 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/sound/soc/soc-core.c
 | 
					 | 
				
			||||||
+++ b/sound/soc/soc-core.c
 | 
					 | 
				
			||||||
@@ -1016,7 +1016,7 @@ int snd_soc_add_pcm_runtime(struct snd_s
 | 
					 | 
				
			||||||
 	for_each_link_cpus(dai_link, i, cpu) {
 | 
					 | 
				
			||||||
 		asoc_rtd_to_cpu(rtd, i) = snd_soc_find_dai(cpu);
 | 
					 | 
				
			||||||
 		if (!asoc_rtd_to_cpu(rtd, i)) {
 | 
					 | 
				
			||||||
-			dev_info(card->dev, "ASoC: CPU DAI %s not registered\n",
 | 
					 | 
				
			||||||
+			dev_info(card->dev, "ASoC: CPU DAI %s not registered - will retry\n",
 | 
					 | 
				
			||||||
 				 cpu->dai_name);
 | 
					 | 
				
			||||||
 			goto _err_defer;
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
@@ -1027,7 +1027,7 @@ int snd_soc_add_pcm_runtime(struct snd_s
 | 
					 | 
				
			||||||
 	for_each_link_codecs(dai_link, i, codec) {
 | 
					 | 
				
			||||||
 		asoc_rtd_to_codec(rtd, i) = snd_soc_find_dai(codec);
 | 
					 | 
				
			||||||
 		if (!asoc_rtd_to_codec(rtd, i)) {
 | 
					 | 
				
			||||||
-			dev_info(card->dev, "ASoC: CODEC DAI %s not registered\n",
 | 
					 | 
				
			||||||
+			dev_info(card->dev, "ASoC: CODEC DAI %s not registered- will retry\n",
 | 
					 | 
				
			||||||
 				 codec->dai_name);
 | 
					 | 
				
			||||||
 			goto _err_defer;
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
@ -1,137 +0,0 @@
 | 
				
			|||||||
From 55f18cc01fc9c93999ea935f7d869d136f5204ed Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Claggy3 <stephen.maclagan@hotmail.com>
 | 
					 | 
				
			||||||
Date: Sat, 11 Feb 2017 14:00:30 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] Update vfpmodule.c
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Christopher Alexander Tobias Schulze - May 2, 2015, 11:57 a.m.
 | 
					 | 
				
			||||||
This patch fixes a problem with VFP state save and restore related
 | 
					 | 
				
			||||||
to exception handling (panic with message "BUG: unsupported FP
 | 
					 | 
				
			||||||
instruction in kernel mode") present on VFP11 floating point units
 | 
					 | 
				
			||||||
(as used with ARM1176JZF-S CPUs, e.g. on first generation Raspberry
 | 
					 | 
				
			||||||
Pi boards). This patch was developed and discussed on
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   https://github.com/raspberrypi/linux/issues/859
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
A precondition to see the crashes is that floating point exception
 | 
					 | 
				
			||||||
traps are enabled. In this case, the VFP11 might determine that a FPU
 | 
					 | 
				
			||||||
operation needs to trap at a point in time when it is not possible to
 | 
					 | 
				
			||||||
signal this to the ARM11 core any more. The VFP11 will then set the
 | 
					 | 
				
			||||||
FPEXC.EX bit and store the trapped opcode in FPINST. (In some cases,
 | 
					 | 
				
			||||||
a second opcode might have been accepted by the VFP11 before the
 | 
					 | 
				
			||||||
exception was detected and could be reported to the ARM11 - in this
 | 
					 | 
				
			||||||
case, the VFP11 also sets FPEXC.FP2V and stores the second opcode in
 | 
					 | 
				
			||||||
FPINST2.)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
If FPEXC.EX is set, the VFP11 will "bounce" the next FPU opcode issued
 | 
					 | 
				
			||||||
by the ARM11 CPU, which will be seen by the ARM11 as an undefined opcode
 | 
					 | 
				
			||||||
trap. The VFP support code examines the FPEXC.EX and FPEXC.FP2V bits
 | 
					 | 
				
			||||||
to decide what actions to take, i.e., whether to emulate the opcodes
 | 
					 | 
				
			||||||
found in FPINST and FPINST2, and whether to retry the bounced instruction.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
If a user space application has left the VFP11 in this "pending trap"
 | 
					 | 
				
			||||||
state, the next FPU opcode issued to the VFP11 might actually be the
 | 
					 | 
				
			||||||
VSTMIA operation vfp_save_state() uses to store the FPU registers
 | 
					 | 
				
			||||||
to memory (in our test cases, when building the signal stack frame).
 | 
					 | 
				
			||||||
In this case, the kernel crashes as described above.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This patch fixes the problem by making sure that vfp_save_state() is
 | 
					 | 
				
			||||||
always entered with FPEXC.EX cleared. (The current value of FPEXC has
 | 
					 | 
				
			||||||
already been saved, so this does not corrupt the context. Clearing
 | 
					 | 
				
			||||||
FPEXC.EX has no effects on FPINST or FPINST2. Also note that many
 | 
					 | 
				
			||||||
callers already modify FPEXC by setting FPEXC.EN before invoking
 | 
					 | 
				
			||||||
vfp_save_state().)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This patch also addresses a second problem related to FPEXC.EX: After
 | 
					 | 
				
			||||||
returning from signal handling, the kernel reloads the VFP context
 | 
					 | 
				
			||||||
from the user mode stack. However, the current code explicitly clears
 | 
					 | 
				
			||||||
both FPEXC.EX and FPEXC.FP2V during reload. As VFP11 requires these
 | 
					 | 
				
			||||||
bits to be preserved, this patch disables clearing them for VFP
 | 
					 | 
				
			||||||
implementations belonging to architecture 1. There should be no
 | 
					 | 
				
			||||||
negative side effects: the user can set both bits by executing FPU
 | 
					 | 
				
			||||||
opcodes anyway, and while user code may now place arbitrary values
 | 
					 | 
				
			||||||
into FPINST and FPINST2 (e.g., non-VFP ARM opcodes) the VFP support
 | 
					 | 
				
			||||||
code knows which instructions can be emulated, and rejects other
 | 
					 | 
				
			||||||
opcodes with "unhandled bounce" messages, so there should be no
 | 
					 | 
				
			||||||
security impact from allowing reloading FPEXC.EX and FPEXC.FP2V.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Christopher Alexander Tobias Schulze <cat.schulze@alice-dsl.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 arch/arm/vfp/vfpmodule.c | 25 +++++++++++++++++++------
 | 
					 | 
				
			||||||
 1 file changed, 19 insertions(+), 6 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/arch/arm/vfp/vfpmodule.c
 | 
					 | 
				
			||||||
+++ b/arch/arm/vfp/vfpmodule.c
 | 
					 | 
				
			||||||
@@ -176,8 +176,11 @@ static int vfp_notifier(struct notifier_
 | 
					 | 
				
			||||||
 		 * case the thread migrates to a different CPU. The
 | 
					 | 
				
			||||||
 		 * restoring is done lazily.
 | 
					 | 
				
			||||||
 		 */
 | 
					 | 
				
			||||||
-		if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu])
 | 
					 | 
				
			||||||
+		if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu]) {
 | 
					 | 
				
			||||||
+			/* vfp_save_state oopses on VFP11 if EX bit set */
 | 
					 | 
				
			||||||
+			fmxr(FPEXC, fpexc & ~FPEXC_EX);
 | 
					 | 
				
			||||||
 			vfp_save_state(vfp_current_hw_state[cpu], fpexc);
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
 #endif
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 		/*
 | 
					 | 
				
			||||||
@@ -454,13 +457,16 @@ static int vfp_pm_suspend(void)
 | 
					 | 
				
			||||||
 	/* if vfp is on, then save state for resumption */
 | 
					 | 
				
			||||||
 	if (fpexc & FPEXC_EN) {
 | 
					 | 
				
			||||||
 		pr_debug("%s: saving vfp state\n", __func__);
 | 
					 | 
				
			||||||
+		/* vfp_save_state oopses on VFP11 if EX bit set */
 | 
					 | 
				
			||||||
+		fmxr(FPEXC, fpexc & ~FPEXC_EX);
 | 
					 | 
				
			||||||
 		vfp_save_state(&ti->vfpstate, fpexc);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 		/* disable, just in case */
 | 
					 | 
				
			||||||
 		fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN);
 | 
					 | 
				
			||||||
 	} else if (vfp_current_hw_state[ti->cpu]) {
 | 
					 | 
				
			||||||
 #ifndef CONFIG_SMP
 | 
					 | 
				
			||||||
-		fmxr(FPEXC, fpexc | FPEXC_EN);
 | 
					 | 
				
			||||||
+		/* vfp_save_state oopses on VFP11 if EX bit set */
 | 
					 | 
				
			||||||
+		fmxr(FPEXC, (fpexc & ~FPEXC_EX) | FPEXC_EN);
 | 
					 | 
				
			||||||
 		vfp_save_state(vfp_current_hw_state[ti->cpu], fpexc);
 | 
					 | 
				
			||||||
 		fmxr(FPEXC, fpexc);
 | 
					 | 
				
			||||||
 #endif
 | 
					 | 
				
			||||||
@@ -523,7 +529,8 @@ void vfp_sync_hwstate(struct thread_info
 | 
					 | 
				
			||||||
 		/*
 | 
					 | 
				
			||||||
 		 * Save the last VFP state on this CPU.
 | 
					 | 
				
			||||||
 		 */
 | 
					 | 
				
			||||||
-		fmxr(FPEXC, fpexc | FPEXC_EN);
 | 
					 | 
				
			||||||
+		/* vfp_save_state oopses on VFP11 if EX bit set */
 | 
					 | 
				
			||||||
+		fmxr(FPEXC, (fpexc & ~FPEXC_EX) | FPEXC_EN);
 | 
					 | 
				
			||||||
 		vfp_save_state(&thread->vfpstate, fpexc | FPEXC_EN);
 | 
					 | 
				
			||||||
 		fmxr(FPEXC, fpexc);
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
@@ -589,6 +596,7 @@ int vfp_restore_user_hwstate(struct user
 | 
					 | 
				
			||||||
 	struct thread_info *thread = current_thread_info();
 | 
					 | 
				
			||||||
 	struct vfp_hard_struct *hwstate = &thread->vfpstate.hard;
 | 
					 | 
				
			||||||
 	unsigned long fpexc;
 | 
					 | 
				
			||||||
+	u32 fpsid = fmrx(FPSID);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* Disable VFP to avoid corrupting the new thread state. */
 | 
					 | 
				
			||||||
 	vfp_flush_hwstate(thread);
 | 
					 | 
				
			||||||
@@ -611,8 +619,12 @@ int vfp_restore_user_hwstate(struct user
 | 
					 | 
				
			||||||
 	/* Ensure the VFP is enabled. */
 | 
					 | 
				
			||||||
 	fpexc |= FPEXC_EN;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	/* Ensure FPINST2 is invalid and the exception flag is cleared. */
 | 
					 | 
				
			||||||
-	fpexc &= ~(FPEXC_EX | FPEXC_FP2V);
 | 
					 | 
				
			||||||
+	/* Mask FPXEC_EX and FPEXC_FP2V if not required by VFP arch */
 | 
					 | 
				
			||||||
+	if ((fpsid & FPSID_ARCH_MASK) != (1 << FPSID_ARCH_BIT)) {
 | 
					 | 
				
			||||||
+		/* Ensure FPINST2 is invalid and the exception flag is cleared. */
 | 
					 | 
				
			||||||
+		fpexc &= ~(FPEXC_EX | FPEXC_FP2V);
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	hwstate->fpexc = fpexc;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	hwstate->fpinst = ufp_exc->fpinst;
 | 
					 | 
				
			||||||
@@ -726,7 +738,8 @@ void kernel_neon_begin(void)
 | 
					 | 
				
			||||||
 	cpu = get_cpu();
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	fpexc = fmrx(FPEXC) | FPEXC_EN;
 | 
					 | 
				
			||||||
-	fmxr(FPEXC, fpexc);
 | 
					 | 
				
			||||||
+	/* vfp_save_state oopses on VFP11 if EX bit set */
 | 
					 | 
				
			||||||
+	fmxr(FPEXC, fpexc & ~FPEXC_EX);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/*
 | 
					 | 
				
			||||||
 	 * Save the userland NEON/VFP state. Under UP,
 | 
					 | 
				
			||||||
@ -1,189 +0,0 @@
 | 
				
			|||||||
From ee7f5d2470712e25e2bde81ead7c191a3eff41e2 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= <noralf@tronnes.org>
 | 
					 | 
				
			||||||
Date: Tue, 1 Nov 2016 15:15:41 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] i2c: bcm2835: Add debug support
 | 
					 | 
				
			||||||
MIME-Version: 1.0
 | 
					 | 
				
			||||||
Content-Type: text/plain; charset=UTF-8
 | 
					 | 
				
			||||||
Content-Transfer-Encoding: 8bit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This adds a debug module parameter to aid in debugging transfer issues
 | 
					 | 
				
			||||||
by printing info to the kernel log. When enabled, status values are
 | 
					 | 
				
			||||||
collected in the interrupt routine and msg info in
 | 
					 | 
				
			||||||
bcm2835_i2c_start_transfer(). This is done in a way that tries to avoid
 | 
					 | 
				
			||||||
affecting timing. Having printk in the isr can mask issues.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
debug values (additive):
 | 
					 | 
				
			||||||
1: Print info on error
 | 
					 | 
				
			||||||
2: Print info on all transfers
 | 
					 | 
				
			||||||
3: Print messages before transfer is started
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The value can be changed at runtime:
 | 
					 | 
				
			||||||
/sys/module/i2c_bcm2835/parameters/debug
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Example output, debug=3:
 | 
					 | 
				
			||||||
[  747.114448] bcm2835_i2c_xfer: msg(1/2) write addr=0x54, len=2 flags= [i2c1]
 | 
					 | 
				
			||||||
[  747.114463] bcm2835_i2c_xfer: msg(2/2) read addr=0x54, len=32 flags= [i2c1]
 | 
					 | 
				
			||||||
[  747.117809] start_transfer: msg(1/2) write addr=0x54, len=2 flags= [i2c1]
 | 
					 | 
				
			||||||
[  747.117825] isr: remain=2, status=0x30000055 : TA TXW TXD TXE  [i2c1]
 | 
					 | 
				
			||||||
[  747.117839] start_transfer: msg(2/2) read addr=0x54, len=32 flags= [i2c1]
 | 
					 | 
				
			||||||
[  747.117849] isr: remain=32, status=0xd0000039 : TA RXR TXD RXD  [i2c1]
 | 
					 | 
				
			||||||
[  747.117861] isr: remain=20, status=0xd0000039 : TA RXR TXD RXD  [i2c1]
 | 
					 | 
				
			||||||
[  747.117870] isr: remain=8, status=0x32 : DONE TXD RXD  [i2c1]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/i2c/busses/i2c-bcm2835.c | 99 +++++++++++++++++++++++++++++++-
 | 
					 | 
				
			||||||
 1 file changed, 98 insertions(+), 1 deletion(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/i2c/busses/i2c-bcm2835.c
 | 
					 | 
				
			||||||
+++ b/drivers/i2c/busses/i2c-bcm2835.c
 | 
					 | 
				
			||||||
@@ -56,6 +56,18 @@
 | 
					 | 
				
			||||||
 #define BCM2835_I2C_CDIV_MIN	0x0002
 | 
					 | 
				
			||||||
 #define BCM2835_I2C_CDIV_MAX	0xFFFE
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static unsigned int debug;
 | 
					 | 
				
			||||||
+module_param(debug, uint, 0644);
 | 
					 | 
				
			||||||
+MODULE_PARM_DESC(debug, "1=err, 2=isr, 3=xfer");
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define BCM2835_DEBUG_MAX	512
 | 
					 | 
				
			||||||
+struct bcm2835_debug {
 | 
					 | 
				
			||||||
+	struct i2c_msg *msg;
 | 
					 | 
				
			||||||
+	int msg_idx;
 | 
					 | 
				
			||||||
+	size_t remain;
 | 
					 | 
				
			||||||
+	u32 status;
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 struct bcm2835_i2c_dev {
 | 
					 | 
				
			||||||
 	struct device *dev;
 | 
					 | 
				
			||||||
 	void __iomem *regs;
 | 
					 | 
				
			||||||
@@ -68,8 +80,78 @@ struct bcm2835_i2c_dev {
 | 
					 | 
				
			||||||
 	u32 msg_err;
 | 
					 | 
				
			||||||
 	u8 *msg_buf;
 | 
					 | 
				
			||||||
 	size_t msg_buf_remaining;
 | 
					 | 
				
			||||||
+	struct bcm2835_debug debug[BCM2835_DEBUG_MAX];
 | 
					 | 
				
			||||||
+	unsigned int debug_num;
 | 
					 | 
				
			||||||
+	unsigned int debug_num_msgs;
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static inline void bcm2835_debug_add(struct bcm2835_i2c_dev *i2c_dev, u32 s)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	if (!i2c_dev->debug_num_msgs || i2c_dev->debug_num >= BCM2835_DEBUG_MAX)
 | 
					 | 
				
			||||||
+		return;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	i2c_dev->debug[i2c_dev->debug_num].msg = i2c_dev->curr_msg;
 | 
					 | 
				
			||||||
+	i2c_dev->debug[i2c_dev->debug_num].msg_idx =
 | 
					 | 
				
			||||||
+				i2c_dev->debug_num_msgs - i2c_dev->num_msgs;
 | 
					 | 
				
			||||||
+	i2c_dev->debug[i2c_dev->debug_num].remain = i2c_dev->msg_buf_remaining;
 | 
					 | 
				
			||||||
+	i2c_dev->debug[i2c_dev->debug_num].status = s;
 | 
					 | 
				
			||||||
+	i2c_dev->debug_num++;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void bcm2835_debug_print_status(struct bcm2835_i2c_dev *i2c_dev,
 | 
					 | 
				
			||||||
+				       struct bcm2835_debug *d)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	u32 s = d->status;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	pr_info("isr: remain=%zu, status=0x%x : %s%s%s%s%s%s%s%s%s%s [i2c%d]\n",
 | 
					 | 
				
			||||||
+		d->remain, s,
 | 
					 | 
				
			||||||
+		s & BCM2835_I2C_S_TA ? "TA " : "",
 | 
					 | 
				
			||||||
+		s & BCM2835_I2C_S_DONE ? "DONE " : "",
 | 
					 | 
				
			||||||
+		s & BCM2835_I2C_S_TXW ? "TXW " : "",
 | 
					 | 
				
			||||||
+		s & BCM2835_I2C_S_RXR ? "RXR " : "",
 | 
					 | 
				
			||||||
+		s & BCM2835_I2C_S_TXD ? "TXD " : "",
 | 
					 | 
				
			||||||
+		s & BCM2835_I2C_S_RXD ? "RXD " : "",
 | 
					 | 
				
			||||||
+		s & BCM2835_I2C_S_TXE ? "TXE " : "",
 | 
					 | 
				
			||||||
+		s & BCM2835_I2C_S_RXF ? "RXF " : "",
 | 
					 | 
				
			||||||
+		s & BCM2835_I2C_S_ERR ? "ERR " : "",
 | 
					 | 
				
			||||||
+		s & BCM2835_I2C_S_CLKT ? "CLKT " : "",
 | 
					 | 
				
			||||||
+		i2c_dev->adapter.nr);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void bcm2835_debug_print_msg(struct bcm2835_i2c_dev *i2c_dev,
 | 
					 | 
				
			||||||
+				    struct i2c_msg *msg, int i, int total,
 | 
					 | 
				
			||||||
+				    const char *fname)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	pr_info("%s: msg(%d/%d) %s addr=0x%02x, len=%u flags=%s%s%s%s%s%s%s [i2c%d]\n",
 | 
					 | 
				
			||||||
+		fname, i, total,
 | 
					 | 
				
			||||||
+		msg->flags & I2C_M_RD ? "read" : "write", msg->addr, msg->len,
 | 
					 | 
				
			||||||
+		msg->flags & I2C_M_TEN ? "TEN" : "",
 | 
					 | 
				
			||||||
+		msg->flags & I2C_M_RECV_LEN ? "RECV_LEN" : "",
 | 
					 | 
				
			||||||
+		msg->flags & I2C_M_NO_RD_ACK ? "NO_RD_ACK" : "",
 | 
					 | 
				
			||||||
+		msg->flags & I2C_M_IGNORE_NAK ? "IGNORE_NAK" : "",
 | 
					 | 
				
			||||||
+		msg->flags & I2C_M_REV_DIR_ADDR ? "REV_DIR_ADDR" : "",
 | 
					 | 
				
			||||||
+		msg->flags & I2C_M_NOSTART ? "NOSTART" : "",
 | 
					 | 
				
			||||||
+		msg->flags & I2C_M_STOP ? "STOP" : "",
 | 
					 | 
				
			||||||
+		i2c_dev->adapter.nr);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void bcm2835_debug_print(struct bcm2835_i2c_dev *i2c_dev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct bcm2835_debug *d;
 | 
					 | 
				
			||||||
+	unsigned int i;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	for (i = 0; i < i2c_dev->debug_num; i++) {
 | 
					 | 
				
			||||||
+		d = &i2c_dev->debug[i];
 | 
					 | 
				
			||||||
+		if (d->status == ~0)
 | 
					 | 
				
			||||||
+			bcm2835_debug_print_msg(i2c_dev, d->msg, d->msg_idx,
 | 
					 | 
				
			||||||
+				i2c_dev->debug_num_msgs, "start_transfer");
 | 
					 | 
				
			||||||
+		else
 | 
					 | 
				
			||||||
+			bcm2835_debug_print_status(i2c_dev, d);
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	if (i2c_dev->debug_num >= BCM2835_DEBUG_MAX)
 | 
					 | 
				
			||||||
+		pr_info("BCM2835_DEBUG_MAX reached\n");
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static inline void bcm2835_i2c_writel(struct bcm2835_i2c_dev *i2c_dev,
 | 
					 | 
				
			||||||
 				      u32 reg, u32 val)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
@@ -257,6 +339,7 @@ static void bcm2835_i2c_start_transfer(s
 | 
					 | 
				
			||||||
 	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_A, msg->addr);
 | 
					 | 
				
			||||||
 	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DLEN, msg->len);
 | 
					 | 
				
			||||||
 	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, c);
 | 
					 | 
				
			||||||
+	bcm2835_debug_add(i2c_dev, ~0);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static void bcm2835_i2c_finish_transfer(struct bcm2835_i2c_dev *i2c_dev)
 | 
					 | 
				
			||||||
@@ -283,6 +366,7 @@ static irqreturn_t bcm2835_i2c_isr(int t
 | 
					 | 
				
			||||||
 	u32 val, err;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S);
 | 
					 | 
				
			||||||
+	bcm2835_debug_add(i2c_dev, val);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	err = val & (BCM2835_I2C_S_CLKT | BCM2835_I2C_S_ERR);
 | 
					 | 
				
			||||||
 	if (err) {
 | 
					 | 
				
			||||||
@@ -349,6 +433,13 @@ static int bcm2835_i2c_xfer(struct i2c_a
 | 
					 | 
				
			||||||
 	unsigned long time_left;
 | 
					 | 
				
			||||||
 	int i;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	if (debug)
 | 
					 | 
				
			||||||
+		i2c_dev->debug_num_msgs = num;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (debug > 2)
 | 
					 | 
				
			||||||
+		for (i = 0; i < num; i++)
 | 
					 | 
				
			||||||
+			bcm2835_debug_print_msg(i2c_dev, &msgs[i], i + 1, num, __func__);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	for (i = 0; i < (num - 1); i++)
 | 
					 | 
				
			||||||
 		if (msgs[i].flags & I2C_M_RD) {
 | 
					 | 
				
			||||||
 			dev_warn_once(i2c_dev->dev,
 | 
					 | 
				
			||||||
@@ -367,6 +458,10 @@ static int bcm2835_i2c_xfer(struct i2c_a
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	bcm2835_i2c_finish_transfer(i2c_dev);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	if (debug > 1 || (debug && (!time_left || i2c_dev->msg_err)))
 | 
					 | 
				
			||||||
+		bcm2835_debug_print(i2c_dev);
 | 
					 | 
				
			||||||
+	i2c_dev->debug_num_msgs = 0;
 | 
					 | 
				
			||||||
+	i2c_dev->debug_num = 0;
 | 
					 | 
				
			||||||
 	if (!time_left) {
 | 
					 | 
				
			||||||
 		bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C,
 | 
					 | 
				
			||||||
 				   BCM2835_I2C_C_CLEAR);
 | 
					 | 
				
			||||||
@@ -377,7 +472,9 @@ static int bcm2835_i2c_xfer(struct i2c_a
 | 
					 | 
				
			||||||
 	if (!i2c_dev->msg_err)
 | 
					 | 
				
			||||||
 		return num;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	dev_dbg(i2c_dev->dev, "i2c transfer failed: %x\n", i2c_dev->msg_err);
 | 
					 | 
				
			||||||
+	if (debug)
 | 
					 | 
				
			||||||
+		dev_err(i2c_dev->dev, "i2c transfer failed: %x\n",
 | 
					 | 
				
			||||||
+			i2c_dev->msg_err);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	if (i2c_dev->msg_err & BCM2835_I2C_S_ERR)
 | 
					 | 
				
			||||||
 		return -EREMOTEIO;
 | 
					 | 
				
			||||||
@ -1,25 +0,0 @@
 | 
				
			|||||||
From a432a3f7142e7e76fd1708debe509ffbea3a3658 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Eric Anholt <eric@anholt.net>
 | 
					 | 
				
			||||||
Date: Thu, 18 Dec 2014 16:07:15 -0800
 | 
					 | 
				
			||||||
Subject: [PATCH] mm: Remove the PFN busy warning
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
See commit dae803e165a11bc88ca8dbc07a11077caf97bbcb -- the warning is
 | 
					 | 
				
			||||||
expected sometimes when using CMA.  However, that commit still spams
 | 
					 | 
				
			||||||
my kernel log with these warnings.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Eric Anholt <eric@anholt.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 mm/page_alloc.c | 2 --
 | 
					 | 
				
			||||||
 1 file changed, 2 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/mm/page_alloc.c
 | 
					 | 
				
			||||||
+++ b/mm/page_alloc.c
 | 
					 | 
				
			||||||
@@ -8603,8 +8603,6 @@ int alloc_contig_range(unsigned long sta
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* Make sure the range is really isolated. */
 | 
					 | 
				
			||||||
 	if (test_pages_isolated(outer_start, end, 0)) {
 | 
					 | 
				
			||||||
-		pr_info_ratelimited("%s: [%lx, %lx) PFNs busy\n",
 | 
					 | 
				
			||||||
-			__func__, outer_start, end);
 | 
					 | 
				
			||||||
 		ret = -EBUSY;
 | 
					 | 
				
			||||||
 		goto done;
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
@ -1,112 +0,0 @@
 | 
				
			|||||||
From 9ab633ec6dd4bb60c13486c8474cf30045c0a422 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Tue, 23 Jan 2018 16:52:45 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] irqchip: irq-bcm2836: Remove regmap and syscon use
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The syscon node defines a register range that duplicates that used by
 | 
					 | 
				
			||||||
the local_intc node on bcm2836/7. Since irq-bcm2835 and irq-bcm2836 are
 | 
					 | 
				
			||||||
built in and always present together (both drivers are enabled by
 | 
					 | 
				
			||||||
CONFIG_ARCH_BCM2835), it is possible to replace the syscon usage with a
 | 
					 | 
				
			||||||
global variable that simplifies the code. Doing so does lose the
 | 
					 | 
				
			||||||
locking provided by regmap, but as only one side is using the regmap
 | 
					 | 
				
			||||||
interface (irq-bcm2835 uses readl and write) there is no loss of
 | 
					 | 
				
			||||||
atomicity.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
See: https://github.com/raspberrypi/firmware/issues/926
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/irqchip/irq-bcm2835.c | 32 ++++++++++++--------------------
 | 
					 | 
				
			||||||
 drivers/irqchip/irq-bcm2836.c |  5 +++++
 | 
					 | 
				
			||||||
 2 files changed, 17 insertions(+), 20 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/irqchip/irq-bcm2835.c
 | 
					 | 
				
			||||||
+++ b/drivers/irqchip/irq-bcm2835.c
 | 
					 | 
				
			||||||
@@ -41,8 +41,6 @@
 | 
					 | 
				
			||||||
 #include <linux/of_irq.h>
 | 
					 | 
				
			||||||
 #include <linux/irqchip.h>
 | 
					 | 
				
			||||||
 #include <linux/irqdomain.h>
 | 
					 | 
				
			||||||
-#include <linux/mfd/syscon.h>
 | 
					 | 
				
			||||||
-#include <linux/regmap.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #include <asm/exception.h>
 | 
					 | 
				
			||||||
 #include <asm/mach/irq.h>
 | 
					 | 
				
			||||||
@@ -92,7 +90,7 @@ struct armctrl_ic {
 | 
					 | 
				
			||||||
 	void __iomem *enable[NR_BANKS];
 | 
					 | 
				
			||||||
 	void __iomem *disable[NR_BANKS];
 | 
					 | 
				
			||||||
 	struct irq_domain *domain;
 | 
					 | 
				
			||||||
-	struct regmap *local_regmap;
 | 
					 | 
				
			||||||
+	void __iomem *local_base;
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static struct armctrl_ic intc __read_mostly;
 | 
					 | 
				
			||||||
@@ -129,24 +127,20 @@ static void armctrl_unmask_irq(struct ir
 | 
					 | 
				
			||||||
 	if (d->hwirq >= NUMBER_IRQS) {
 | 
					 | 
				
			||||||
 		if (num_online_cpus() > 1) {
 | 
					 | 
				
			||||||
 			unsigned int data;
 | 
					 | 
				
			||||||
-			int ret;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-			if (!intc.local_regmap) {
 | 
					 | 
				
			||||||
-				pr_err("FIQ is disabled due to missing regmap\n");
 | 
					 | 
				
			||||||
+			if (!intc.local_base) {
 | 
					 | 
				
			||||||
+				pr_err("FIQ is disabled due to missing arm_local_intc\n");
 | 
					 | 
				
			||||||
 				return;
 | 
					 | 
				
			||||||
 			}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-			ret = regmap_read(intc.local_regmap,
 | 
					 | 
				
			||||||
-					  ARM_LOCAL_GPU_INT_ROUTING, &data);
 | 
					 | 
				
			||||||
-			if (ret) {
 | 
					 | 
				
			||||||
-				pr_err("Failed to read int routing %d\n", ret);
 | 
					 | 
				
			||||||
-				return;
 | 
					 | 
				
			||||||
-			}
 | 
					 | 
				
			||||||
+			data = readl_relaxed(intc.local_base +
 | 
					 | 
				
			||||||
+					     ARM_LOCAL_GPU_INT_ROUTING);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 			data &= ~0xc;
 | 
					 | 
				
			||||||
 			data |= (1 << 2);
 | 
					 | 
				
			||||||
-			regmap_write(intc.local_regmap,
 | 
					 | 
				
			||||||
-				     ARM_LOCAL_GPU_INT_ROUTING, data);
 | 
					 | 
				
			||||||
+			writel_relaxed(data,
 | 
					 | 
				
			||||||
+				       intc.local_base +
 | 
					 | 
				
			||||||
+				       ARM_LOCAL_GPU_INT_ROUTING);
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 		writel_relaxed(REG_FIQ_ENABLE | hwirq_to_fiq(d->hwirq),
 | 
					 | 
				
			||||||
@@ -246,12 +240,10 @@ static int __init armctrl_of_init(struct
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	if (is_2836) {
 | 
					 | 
				
			||||||
-		intc.local_regmap =
 | 
					 | 
				
			||||||
-			syscon_regmap_lookup_by_compatible("brcm,bcm2836-arm-local");
 | 
					 | 
				
			||||||
-		if (IS_ERR(intc.local_regmap)) {
 | 
					 | 
				
			||||||
-			pr_err("Failed to get local register map. FIQ is disabled for cpus > 1\n");
 | 
					 | 
				
			||||||
-			intc.local_regmap = NULL;
 | 
					 | 
				
			||||||
-		}
 | 
					 | 
				
			||||||
+		extern void __iomem * __attribute__((weak)) arm_local_intc;
 | 
					 | 
				
			||||||
+		intc.local_base = arm_local_intc;
 | 
					 | 
				
			||||||
+		if (!intc.local_base)
 | 
					 | 
				
			||||||
+			pr_err("Failed to get local intc base. FIQ is disabled for cpus > 1\n");
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* Make a duplicate irq range which is used to enable FIQ */
 | 
					 | 
				
			||||||
--- a/drivers/irqchip/irq-bcm2836.c
 | 
					 | 
				
			||||||
+++ b/drivers/irqchip/irq-bcm2836.c
 | 
					 | 
				
			||||||
@@ -22,6 +22,9 @@ struct bcm2836_arm_irqchip_intc {
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static struct bcm2836_arm_irqchip_intc intc  __read_mostly;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+void __iomem *arm_local_intc;
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL_GPL(arm_local_intc);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static void bcm2836_arm_irqchip_mask_per_cpu_irq(unsigned int reg_offset,
 | 
					 | 
				
			||||||
 						 unsigned int bit,
 | 
					 | 
				
			||||||
 						 int cpu)
 | 
					 | 
				
			||||||
@@ -323,6 +326,8 @@ static int __init bcm2836_arm_irqchip_l1
 | 
					 | 
				
			||||||
 		panic("%pOF: unable to map local interrupt registers\n", node);
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	arm_local_intc = intc.base;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	bcm2835_init_local_timer_frequency();
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	intc.domain = irq_domain_add_linear(node, LAST_IRQ + 1,
 | 
					 | 
				
			||||||
@ -1,48 +0,0 @@
 | 
				
			|||||||
From 021908b753875198daddfa9e77a0d2fd8004a469 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Tue, 17 Oct 2017 15:04:29 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] lan78xx: Enable LEDs and auto-negotiation
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
For applications of the LAN78xx that don't have valid programmed
 | 
					 | 
				
			||||||
EEPROMs or OTPs, enabling both LEDs and auto-negotiation by default
 | 
					 | 
				
			||||||
seems reasonable.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/usb/lan78xx.c | 11 +++++++++++
 | 
					 | 
				
			||||||
 1 file changed, 11 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/usb/lan78xx.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/usb/lan78xx.c
 | 
					 | 
				
			||||||
@@ -2463,6 +2463,11 @@ static int lan78xx_reset(struct lan78xx_
 | 
					 | 
				
			||||||
 	int ret = 0;
 | 
					 | 
				
			||||||
 	unsigned long timeout;
 | 
					 | 
				
			||||||
 	u8 sig;
 | 
					 | 
				
			||||||
+	bool has_eeprom;
 | 
					 | 
				
			||||||
+	bool has_otp;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	has_eeprom = !lan78xx_read_eeprom(dev, 0, 0, NULL);
 | 
					 | 
				
			||||||
+	has_otp = !lan78xx_read_otp(dev, 0, 0, NULL);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	ret = lan78xx_read_reg(dev, HW_CFG, &buf);
 | 
					 | 
				
			||||||
 	buf |= HW_CFG_LRST_;
 | 
					 | 
				
			||||||
@@ -2516,6 +2521,9 @@ static int lan78xx_reset(struct lan78xx_
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	ret = lan78xx_read_reg(dev, HW_CFG, &buf);
 | 
					 | 
				
			||||||
 	buf |= HW_CFG_MEF_;
 | 
					 | 
				
			||||||
+	/* If no valid EEPROM and no valid OTP, enable the LEDs by default */
 | 
					 | 
				
			||||||
+	if (!has_eeprom && !has_otp)
 | 
					 | 
				
			||||||
+	    buf |= HW_CFG_LED0_EN_ | HW_CFG_LED1_EN_;
 | 
					 | 
				
			||||||
 	ret = lan78xx_write_reg(dev, HW_CFG, buf);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	ret = lan78xx_read_reg(dev, USB_CFG0, &buf);
 | 
					 | 
				
			||||||
@@ -2571,6 +2579,9 @@ static int lan78xx_reset(struct lan78xx_
 | 
					 | 
				
			||||||
 			buf |= MAC_CR_AUTO_DUPLEX_ | MAC_CR_AUTO_SPEED_;
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
+	/* If no valid EEPROM and no valid OTP, enable AUTO negotiation */
 | 
					 | 
				
			||||||
+	if (!has_eeprom && !has_otp)
 | 
					 | 
				
			||||||
+	    buf |= MAC_CR_AUTO_DUPLEX_ | MAC_CR_AUTO_SPEED_;
 | 
					 | 
				
			||||||
 	ret = lan78xx_write_reg(dev, MAC_CR, buf);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	ret = lan78xx_read_reg(dev, MAC_TX, &buf);
 | 
					 | 
				
			||||||
@ -1,29 +0,0 @@
 | 
				
			|||||||
From a79b4327445a827be28b9939592da8e812a19b29 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Tue, 23 Feb 2016 17:26:48 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] amba_pl011: Don't use DT aliases for numbering
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The pl011 driver looks for DT aliases of the form "serial<n>",
 | 
					 | 
				
			||||||
and if found uses <n> as the device ID. This can cause
 | 
					 | 
				
			||||||
/dev/ttyAMA0 to become /dev/ttyAMA1, which is confusing if the
 | 
					 | 
				
			||||||
other serial port is provided by the 8250 driver which doesn't
 | 
					 | 
				
			||||||
use the same logic.
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/tty/serial/amba-pl011.c | 5 +++++
 | 
					 | 
				
			||||||
 1 file changed, 5 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/tty/serial/amba-pl011.c
 | 
					 | 
				
			||||||
+++ b/drivers/tty/serial/amba-pl011.c
 | 
					 | 
				
			||||||
@@ -2578,7 +2578,12 @@ static int pl011_setup_port(struct devic
 | 
					 | 
				
			||||||
 	if (IS_ERR(base))
 | 
					 | 
				
			||||||
 		return PTR_ERR(base);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	/* Don't use DT serial<n> aliases - it causes the device to
 | 
					 | 
				
			||||||
+	   be renumbered to ttyAMA1 if it is the second serial port in the
 | 
					 | 
				
			||||||
+	   system, even though the other one is ttyS0. The 8250 driver
 | 
					 | 
				
			||||||
+	   doesn't use this logic, so always remains ttyS0.
 | 
					 | 
				
			||||||
 	index = pl011_probe_dt_alias(index, dev);
 | 
					 | 
				
			||||||
+	*/
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	uap->old_cr = 0;
 | 
					 | 
				
			||||||
 	uap->port.dev = dev;
 | 
					 | 
				
			||||||
@ -1,86 +0,0 @@
 | 
				
			|||||||
From e3f28ae66fd94f4a7a10099a96d6dd5273904943 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Wed, 1 Mar 2017 16:07:39 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] amba_pl011: Round input clock up
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The UART clock is initialised to be as close to the requested
 | 
					 | 
				
			||||||
frequency as possible without exceeding it. Now that there is a
 | 
					 | 
				
			||||||
clock manager that returns the actual frequencies, an expected
 | 
					 | 
				
			||||||
48MHz clock is reported as 47999625. If the requested baudrate
 | 
					 | 
				
			||||||
== requested clock/16, there is no headroom and the slight
 | 
					 | 
				
			||||||
reduction in actual clock rate results in failure.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Detect cases where it looks like a "round" clock was chosen and
 | 
					 | 
				
			||||||
adjust the reported clock to match that "round" value. As the
 | 
					 | 
				
			||||||
code comment says:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * If increasing a clock by less than 0.1% changes it
 | 
					 | 
				
			||||||
 * from ..999.. to ..000.., round up.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/tty/serial/amba-pl011.c | 23 +++++++++++++++++++++--
 | 
					 | 
				
			||||||
 1 file changed, 21 insertions(+), 2 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/tty/serial/amba-pl011.c
 | 
					 | 
				
			||||||
+++ b/drivers/tty/serial/amba-pl011.c
 | 
					 | 
				
			||||||
@@ -1642,6 +1642,23 @@ static void pl011_put_poll_char(struct u
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #endif /* CONFIG_CONSOLE_POLL */
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+unsigned long pl011_clk_round(unsigned long clk)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	unsigned long scaler;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/*
 | 
					 | 
				
			||||||
+	 * If increasing a clock by less than 0.1% changes it
 | 
					 | 
				
			||||||
+	 * from ..999.. to ..000.., round up.
 | 
					 | 
				
			||||||
+	 */
 | 
					 | 
				
			||||||
+	scaler = 1;
 | 
					 | 
				
			||||||
+	while (scaler * 100000 < clk)
 | 
					 | 
				
			||||||
+		scaler *= 10;
 | 
					 | 
				
			||||||
+	if ((clk + scaler - 1)/scaler % 1000 == 0)
 | 
					 | 
				
			||||||
+		clk = (clk/scaler + 1) * scaler;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return clk;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static int pl011_hwinit(struct uart_port *port)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct uart_amba_port *uap =
 | 
					 | 
				
			||||||
@@ -1658,7 +1675,7 @@ static int pl011_hwinit(struct uart_port
 | 
					 | 
				
			||||||
 	if (retval)
 | 
					 | 
				
			||||||
 		return retval;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	uap->port.uartclk = clk_get_rate(uap->clk);
 | 
					 | 
				
			||||||
+	uap->port.uartclk = pl011_clk_round(clk_get_rate(uap->clk));
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* Clear pending error and receive interrupts */
 | 
					 | 
				
			||||||
 	pl011_write(UART011_OEIS | UART011_BEIS | UART011_PEIS |
 | 
					 | 
				
			||||||
@@ -2292,7 +2309,7 @@ static int pl011_console_setup(struct co
 | 
					 | 
				
			||||||
 			plat->init();
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	uap->port.uartclk = clk_get_rate(uap->clk);
 | 
					 | 
				
			||||||
+	uap->port.uartclk = pl011_clk_round(clk_get_rate(uap->clk));
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	if (uap->vendor->fixed_options) {
 | 
					 | 
				
			||||||
 		baud = uap->fixed_baud;
 | 
					 | 
				
			||||||
@@ -2509,6 +2526,7 @@ static struct uart_driver amba_reg = {
 | 
					 | 
				
			||||||
 	.cons			= AMBA_CONSOLE,
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+#if 0
 | 
					 | 
				
			||||||
 static int pl011_probe_dt_alias(int index, struct device *dev)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct device_node *np;
 | 
					 | 
				
			||||||
@@ -2540,6 +2558,7 @@ static int pl011_probe_dt_alias(int inde
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	return ret;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /* unregisters the driver also if no more ports are left */
 | 
					 | 
				
			||||||
 static void pl011_unregister_port(struct uart_amba_port *uap)
 | 
					 | 
				
			||||||
@ -1,27 +0,0 @@
 | 
				
			|||||||
From f44d321993ccf1707f5774dc443b900adae5d9d5 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Fri, 29 Sep 2017 10:32:19 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] amba_pl011: Insert mb() for correct FIFO handling
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The pl011 register accessor functions use the _relaxed versions of the
 | 
					 | 
				
			||||||
standard readl() and writel() functions, meaning that there are no
 | 
					 | 
				
			||||||
automatic memory barriers. When polling a FIFO status register to check
 | 
					 | 
				
			||||||
for fullness, it is necessary to ensure that any outstanding writes have
 | 
					 | 
				
			||||||
completed; otherwise the flags are effectively stale, making it possible
 | 
					 | 
				
			||||||
that the next write is to a full FIFO.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/tty/serial/amba-pl011.c | 1 +
 | 
					 | 
				
			||||||
 1 file changed, 1 insertion(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/tty/serial/amba-pl011.c
 | 
					 | 
				
			||||||
+++ b/drivers/tty/serial/amba-pl011.c
 | 
					 | 
				
			||||||
@@ -1377,6 +1377,7 @@ static bool pl011_tx_char(struct uart_am
 | 
					 | 
				
			||||||
 		return false; /* unable to transmit character */
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	pl011_write(c, uap, REG_DR);
 | 
					 | 
				
			||||||
+	mb();
 | 
					 | 
				
			||||||
 	uap->port.icount.tx++;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	return true;
 | 
					 | 
				
			||||||
@ -1,50 +0,0 @@
 | 
				
			|||||||
From 8ccf96729972504acf4ca0e1868278ba549e0f5a Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Fri, 29 Sep 2017 10:32:19 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] amba_pl011: Add cts-event-workaround DT property
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The BCM2835 PL011 implementation seems to have a bug that can lead to a
 | 
					 | 
				
			||||||
transmission lockup if CTS changes frequently. A workaround was added to
 | 
					 | 
				
			||||||
the driver with a vendor-specific flag to enable it, but this flag is
 | 
					 | 
				
			||||||
currently not set for ARM implementations.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add a "cts-event-workaround" property to Pi DTBs and use the presence
 | 
					 | 
				
			||||||
of that property to force the flag to be enabled in the driver.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
See: https://github.com/raspberrypi/linux/issues/1280
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 Documentation/devicetree/bindings/serial/pl011.yaml | 6 ++++++
 | 
					 | 
				
			||||||
 drivers/tty/serial/amba-pl011.c                     | 5 +++++
 | 
					 | 
				
			||||||
 2 files changed, 11 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/Documentation/devicetree/bindings/serial/pl011.yaml
 | 
					 | 
				
			||||||
+++ b/Documentation/devicetree/bindings/serial/pl011.yaml
 | 
					 | 
				
			||||||
@@ -98,6 +98,12 @@ properties:
 | 
					 | 
				
			||||||
     $ref: /schemas/types.yaml#/definitions/uint32
 | 
					 | 
				
			||||||
     default: 3000
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+  cts-event-workaround:
 | 
					 | 
				
			||||||
+    description:
 | 
					 | 
				
			||||||
+      Enables the (otherwise vendor-specific) workaround for the
 | 
					 | 
				
			||||||
+      CTS-induced TX lockup.
 | 
					 | 
				
			||||||
+    type: boolean
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 required:
 | 
					 | 
				
			||||||
   - compatible
 | 
					 | 
				
			||||||
   - reg
 | 
					 | 
				
			||||||
--- a/drivers/tty/serial/amba-pl011.c
 | 
					 | 
				
			||||||
+++ b/drivers/tty/serial/amba-pl011.c
 | 
					 | 
				
			||||||
@@ -2665,6 +2665,11 @@ static int pl011_probe(struct amba_devic
 | 
					 | 
				
			||||||
 	if (IS_ERR(uap->clk))
 | 
					 | 
				
			||||||
 		return PTR_ERR(uap->clk);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	if (of_property_read_bool(dev->dev.of_node, "cts-event-workaround")) {
 | 
					 | 
				
			||||||
+	    vendor->cts_event_workaround = true;
 | 
					 | 
				
			||||||
+	    dev_info(&dev->dev, "cts_event_workaround enabled\n");
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	uap->reg_offset = vendor->reg_offset;
 | 
					 | 
				
			||||||
 	uap->vendor = vendor;
 | 
					 | 
				
			||||||
 	uap->fifosize = vendor->get_fifosize(dev);
 | 
					 | 
				
			||||||
@ -1,85 +0,0 @@
 | 
				
			|||||||
From 28c2408f991d97de5e3d1da9711cf5486f411d02 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Thu, 11 Jul 2019 13:13:39 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] tty: amba-pl011: Make TX optimisation conditional
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pl011_tx_chars takes a "from_irq" parameter to reduce the number of
 | 
					 | 
				
			||||||
register accesses. When from_irq is true the function assumes that the
 | 
					 | 
				
			||||||
FIFO is half empty and writes up to half a FIFO's worth of bytes
 | 
					 | 
				
			||||||
without polling the FIFO status register, the reasoning being that
 | 
					 | 
				
			||||||
the function is being called as a result of the TX interrupt being
 | 
					 | 
				
			||||||
raised. This logic would work were it not for the fact that
 | 
					 | 
				
			||||||
pl011_rx_chars, called from pl011_int before pl011_tx_chars, releases
 | 
					 | 
				
			||||||
the spinlock before calling tty_flip_buffer_push.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
A user thread writing to the UART claims the spinlock and ultimately
 | 
					 | 
				
			||||||
calls pl011_tx_chars with from_irq set to false. This reverts to the
 | 
					 | 
				
			||||||
older logic that polls the FIFO status register before sending every
 | 
					 | 
				
			||||||
byte. If this happen on an SMP system during the section of the IRQ
 | 
					 | 
				
			||||||
handler where the spinlock has been released, then by the time the TX
 | 
					 | 
				
			||||||
interrupt handler is called, the FIFO may already be full, and any
 | 
					 | 
				
			||||||
further writes are likely to be lost.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The fix involves adding a per-port flag that is true iff running from
 | 
					 | 
				
			||||||
within the interrupt handler and the spinlock has not yet been released.
 | 
					 | 
				
			||||||
This flag is then used as the value for the from_irq parameter of
 | 
					 | 
				
			||||||
pl011_tx_chars, causing polling to be used in the unsafe case.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Fixes: 1e84d22322ce ("serial/amba-pl011: Refactor and simplify TX FIFO handling")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/tty/serial/amba-pl011.c | 7 ++++++-
 | 
					 | 
				
			||||||
 1 file changed, 6 insertions(+), 1 deletion(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/tty/serial/amba-pl011.c
 | 
					 | 
				
			||||||
+++ b/drivers/tty/serial/amba-pl011.c
 | 
					 | 
				
			||||||
@@ -265,6 +265,7 @@ struct uart_amba_port {
 | 
					 | 
				
			||||||
 	unsigned int		old_cr;		/* state during shutdown */
 | 
					 | 
				
			||||||
 	unsigned int		fixed_baud;	/* vendor-set fixed baud rate */
 | 
					 | 
				
			||||||
 	char			type[12];
 | 
					 | 
				
			||||||
+	bool			irq_locked;	/* in irq, unreleased lock */
 | 
					 | 
				
			||||||
 #ifdef CONFIG_DMA_ENGINE
 | 
					 | 
				
			||||||
 	/* DMA stuff */
 | 
					 | 
				
			||||||
 	bool			using_tx_dma;
 | 
					 | 
				
			||||||
@@ -811,6 +812,7 @@ __acquires(&uap->port.lock)
 | 
					 | 
				
			||||||
 	if (!uap->using_tx_dma)
 | 
					 | 
				
			||||||
 		return;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	uap->irq_locked = 0;
 | 
					 | 
				
			||||||
 	dmaengine_terminate_async(uap->dmatx.chan);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	if (uap->dmatx.queued) {
 | 
					 | 
				
			||||||
@@ -937,6 +939,7 @@ static void pl011_dma_rx_chars(struct ua
 | 
					 | 
				
			||||||
 		fifotaken = pl011_fifo_to_tty(uap);
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	uap->irq_locked = 0;
 | 
					 | 
				
			||||||
 	spin_unlock(&uap->port.lock);
 | 
					 | 
				
			||||||
 	dev_vdbg(uap->port.dev,
 | 
					 | 
				
			||||||
 		 "Took %d chars from DMA buffer and %d chars from the FIFO\n",
 | 
					 | 
				
			||||||
@@ -1341,6 +1344,7 @@ __acquires(&uap->port.lock)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	pl011_fifo_to_tty(uap);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	uap->irq_locked = 0;
 | 
					 | 
				
			||||||
 	spin_unlock(&uap->port.lock);
 | 
					 | 
				
			||||||
 	tty_flip_buffer_push(&uap->port.state->port);
 | 
					 | 
				
			||||||
 	/*
 | 
					 | 
				
			||||||
@@ -1474,6 +1478,7 @@ static irqreturn_t pl011_int(int irq, vo
 | 
					 | 
				
			||||||
 	int handled = 0;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	spin_lock_irqsave(&uap->port.lock, flags);
 | 
					 | 
				
			||||||
+	uap->irq_locked = 1;
 | 
					 | 
				
			||||||
 	status = pl011_read(uap, REG_RIS) & uap->im;
 | 
					 | 
				
			||||||
 	if (status) {
 | 
					 | 
				
			||||||
 		do {
 | 
					 | 
				
			||||||
@@ -1493,7 +1498,7 @@ static irqreturn_t pl011_int(int irq, vo
 | 
					 | 
				
			||||||
 				      UART011_CTSMIS|UART011_RIMIS))
 | 
					 | 
				
			||||||
 				pl011_modem_status(uap);
 | 
					 | 
				
			||||||
 			if (status & UART011_TXIS)
 | 
					 | 
				
			||||||
-				pl011_tx_chars(uap, true);
 | 
					 | 
				
			||||||
+				pl011_tx_chars(uap, uap->irq_locked);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 			if (pass_counter-- == 0)
 | 
					 | 
				
			||||||
 				break;
 | 
					 | 
				
			||||||
@ -1,61 +0,0 @@
 | 
				
			|||||||
From 3cec61f1b19a6f589f8b2aef97977e1c38eb7b70 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Fri, 24 Jan 2020 11:38:28 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] tty: amba-pl011: Add un/throttle support
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The PL011 driver lacks throttle and unthrottle methods. As a result,
 | 
					 | 
				
			||||||
sending more data to the Pi than it can immediately sink while CRTSCTS
 | 
					 | 
				
			||||||
is enabled causes a NULL pointer to be followed.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add a throttle handler that disables the RX interrupts, and an
 | 
					 | 
				
			||||||
unthrottle handler that reenables them.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/tty/serial/amba-pl011.c | 28 ++++++++++++++++++++++++++++
 | 
					 | 
				
			||||||
 1 file changed, 28 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/tty/serial/amba-pl011.c
 | 
					 | 
				
			||||||
+++ b/drivers/tty/serial/amba-pl011.c
 | 
					 | 
				
			||||||
@@ -1317,6 +1317,32 @@ static void pl011_start_tx(struct uart_p
 | 
					 | 
				
			||||||
 		pl011_start_tx_pio(uap);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static void pl011_throttle(struct uart_port *port)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct uart_amba_port *uap =
 | 
					 | 
				
			||||||
+	    container_of(port, struct uart_amba_port, port);
 | 
					 | 
				
			||||||
+	unsigned long flags;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	spin_lock_irqsave(&uap->port.lock, flags);
 | 
					 | 
				
			||||||
+	uap->im &= ~(UART011_RTIM | UART011_RXIM);
 | 
					 | 
				
			||||||
+	pl011_write(uap->im, uap, REG_IMSC);
 | 
					 | 
				
			||||||
+	spin_unlock_irqrestore(&uap->port.lock, flags);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void pl011_unthrottle(struct uart_port *port)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct uart_amba_port *uap =
 | 
					 | 
				
			||||||
+	    container_of(port, struct uart_amba_port, port);
 | 
					 | 
				
			||||||
+	unsigned long flags;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	spin_lock_irqsave(&uap->port.lock, flags);
 | 
					 | 
				
			||||||
+	uap->im |= UART011_RTIM;
 | 
					 | 
				
			||||||
+	if (!pl011_dma_rx_running(uap))
 | 
					 | 
				
			||||||
+	    uap->im |= UART011_RXIM;
 | 
					 | 
				
			||||||
+	pl011_write(uap->im, uap, REG_IMSC);
 | 
					 | 
				
			||||||
+	spin_unlock_irqrestore(&uap->port.lock, flags);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static void pl011_stop_rx(struct uart_port *port)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct uart_amba_port *uap =
 | 
					 | 
				
			||||||
@@ -2139,6 +2165,8 @@ static const struct uart_ops amba_pl011_
 | 
					 | 
				
			||||||
 	.stop_tx	= pl011_stop_tx,
 | 
					 | 
				
			||||||
 	.start_tx	= pl011_start_tx,
 | 
					 | 
				
			||||||
 	.stop_rx	= pl011_stop_rx,
 | 
					 | 
				
			||||||
+	.throttle	= pl011_throttle,
 | 
					 | 
				
			||||||
+	.unthrottle	= pl011_unthrottle,
 | 
					 | 
				
			||||||
 	.enable_ms	= pl011_enable_ms,
 | 
					 | 
				
			||||||
 	.break_ctl	= pl011_break_ctl,
 | 
					 | 
				
			||||||
 	.startup	= pl011_startup,
 | 
					 | 
				
			||||||
@ -1,42 +0,0 @@
 | 
				
			|||||||
From e9addfee683ceabeae5da9dc5503fef536f7385a Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Wed, 29 Jan 2020 09:35:19 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] tty: amba-pl011: Avoid rare write-when-full error
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Under some circumstances on BCM283x processors data loss can be
 | 
					 | 
				
			||||||
observed - a single byte missing from the TX output stream. These bytes
 | 
					 | 
				
			||||||
are always the last byte of a batch of 8 written from pl011_tx_chars
 | 
					 | 
				
			||||||
when from_irq is true, meaning that the FIFO full flag is not checked
 | 
					 | 
				
			||||||
before writing.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The transmit optimisation relies on the FIFO being half-empty when the
 | 
					 | 
				
			||||||
TX interrupt is raised. Instrumenting the driver further showed that
 | 
					 | 
				
			||||||
the failure case correlated with the TX FIFO full flag being set at the
 | 
					 | 
				
			||||||
point where the last byte was written to the data register, which
 | 
					 | 
				
			||||||
explains the data loss but not how the FIFO appeared to be prematurely
 | 
					 | 
				
			||||||
full. A possible explanation is that a FIFO write was in flight at the
 | 
					 | 
				
			||||||
time the interrupt was raised, but as yet there is no hypothesis as to
 | 
					 | 
				
			||||||
how this might occur.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
In the absence of a clear understanding of the failure mechanism, avoid
 | 
					 | 
				
			||||||
the problem by checking the FIFO levels before writing the last byte of
 | 
					 | 
				
			||||||
the group, which will have minimal performance impact.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/tty/serial/amba-pl011.c | 4 ++++
 | 
					 | 
				
			||||||
 1 file changed, 4 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/tty/serial/amba-pl011.c
 | 
					 | 
				
			||||||
+++ b/drivers/tty/serial/amba-pl011.c
 | 
					 | 
				
			||||||
@@ -1438,6 +1438,10 @@ static bool pl011_tx_chars(struct uart_a
 | 
					 | 
				
			||||||
 		if (likely(from_irq) && count-- == 0)
 | 
					 | 
				
			||||||
 			break;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+		if (likely(from_irq) && count == 0 &&
 | 
					 | 
				
			||||||
+		    pl011_read(uap, REG_FR) & UART01x_FR_TXFF)
 | 
					 | 
				
			||||||
+			break;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 		if (!pl011_tx_char(uap, xmit->buf[xmit->tail], from_irq))
 | 
					 | 
				
			||||||
 			break;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@ -1,22 +0,0 @@
 | 
				
			|||||||
From 68ef623a719071e05f5d62270c7bee35a2984452 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: notro <notro@tronnes.org>
 | 
					 | 
				
			||||||
Date: Thu, 10 Jul 2014 13:59:47 +0200
 | 
					 | 
				
			||||||
Subject: [PATCH] pinctrl-bcm2835: Set base to 0 give expected gpio
 | 
					 | 
				
			||||||
 numbering
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Noralf Tronnes <notro@tronnes.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/pinctrl/bcm/pinctrl-bcm2835.c | 2 +-
 | 
					 | 
				
			||||||
 1 file changed, 1 insertion(+), 1 deletion(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/pinctrl/bcm/pinctrl-bcm2835.c
 | 
					 | 
				
			||||||
+++ b/drivers/pinctrl/bcm/pinctrl-bcm2835.c
 | 
					 | 
				
			||||||
@@ -362,7 +362,7 @@ static const struct gpio_chip bcm2835_gp
 | 
					 | 
				
			||||||
 	.get = bcm2835_gpio_get,
 | 
					 | 
				
			||||||
 	.set = bcm2835_gpio_set,
 | 
					 | 
				
			||||||
 	.set_config = gpiochip_generic_config,
 | 
					 | 
				
			||||||
-	.base = -1,
 | 
					 | 
				
			||||||
+	.base = 0,
 | 
					 | 
				
			||||||
 	.ngpio = BCM2835_NUM_GPIOS,
 | 
					 | 
				
			||||||
 	.can_sleep = false,
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
@ -1,150 +0,0 @@
 | 
				
			|||||||
From 0ef60858b9927024cd310038c97976e27e4daada Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: popcornmix <popcornmix@gmail.com>
 | 
					 | 
				
			||||||
Date: Sun, 12 May 2013 12:24:19 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] Main bcm2708/bcm2709 linux port
 | 
					 | 
				
			||||||
MIME-Version: 1.0
 | 
					 | 
				
			||||||
Content-Type: text/plain; charset=UTF-8
 | 
					 | 
				
			||||||
Content-Transfer-Encoding: 8bit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: popcornmix <popcornmix@gmail.com>
 | 
					 | 
				
			||||||
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bcm2709: Drop platform smp and timer init code
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
irq-bcm2836 handles this through these functions:
 | 
					 | 
				
			||||||
bcm2835_init_local_timer_frequency()
 | 
					 | 
				
			||||||
bcm2836_arm_irqchip_smp_init()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bcm270x: Use watchdog for reboot/poweroff
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The watchdog driver already has support for reboot/poweroff.
 | 
					 | 
				
			||||||
Make use of this and remove the code from the platform files.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
board_bcm2835: Remove coherent dma pool increase - API has gone
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 arch/arm/mach-bcm/Kconfig         |  1 +
 | 
					 | 
				
			||||||
 arch/arm/mm/proc-v6.S             | 15 ++++++++++++---
 | 
					 | 
				
			||||||
 drivers/irqchip/irq-bcm2835.c     |  7 ++++++-
 | 
					 | 
				
			||||||
 drivers/mailbox/bcm2835-mailbox.c | 18 ++++++++++++++++--
 | 
					 | 
				
			||||||
 4 files changed, 35 insertions(+), 6 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/arch/arm/mach-bcm/Kconfig
 | 
					 | 
				
			||||||
+++ b/arch/arm/mach-bcm/Kconfig
 | 
					 | 
				
			||||||
@@ -165,6 +165,7 @@ config ARCH_BCM2835
 | 
					 | 
				
			||||||
 	select PINCTRL
 | 
					 | 
				
			||||||
 	select PINCTRL_BCM2835
 | 
					 | 
				
			||||||
 	select MFD_CORE
 | 
					 | 
				
			||||||
+	select MFD_SYSCON if ARCH_MULTI_V7
 | 
					 | 
				
			||||||
 	help
 | 
					 | 
				
			||||||
 	  This enables support for the Broadcom BCM2711 and BCM283x SoCs.
 | 
					 | 
				
			||||||
 	  This SoC is used in the Raspberry Pi and Roku 2 devices.
 | 
					 | 
				
			||||||
--- a/arch/arm/mm/proc-v6.S
 | 
					 | 
				
			||||||
+++ b/arch/arm/mm/proc-v6.S
 | 
					 | 
				
			||||||
@@ -70,10 +70,19 @@ ENDPROC(cpu_v6_reset)
 | 
					 | 
				
			||||||
  *
 | 
					 | 
				
			||||||
  *	IRQs are already disabled.
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* See jira SW-5991 for details of this workaround */
 | 
					 | 
				
			||||||
 ENTRY(cpu_v6_do_idle)
 | 
					 | 
				
			||||||
-	mov	r1, #0
 | 
					 | 
				
			||||||
-	mcr	p15, 0, r1, c7, c10, 4		@ DWB - WFI may enter a low-power mode
 | 
					 | 
				
			||||||
-	mcr	p15, 0, r1, c7, c0, 4		@ wait for interrupt
 | 
					 | 
				
			||||||
+	.align 5
 | 
					 | 
				
			||||||
+	mov     r1, #2
 | 
					 | 
				
			||||||
+1:	subs	r1, #1
 | 
					 | 
				
			||||||
+	nop
 | 
					 | 
				
			||||||
+	mcreq	p15, 0, r1, c7, c10, 4		@ DWB - WFI may enter a low-power mode
 | 
					 | 
				
			||||||
+	mcreq	p15, 0, r1, c7, c0, 4		@ wait for interrupt
 | 
					 | 
				
			||||||
+	nop
 | 
					 | 
				
			||||||
+	nop
 | 
					 | 
				
			||||||
+	nop
 | 
					 | 
				
			||||||
+	bne 1b
 | 
					 | 
				
			||||||
 	ret	lr
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 ENTRY(cpu_v6_dcache_clean_area)
 | 
					 | 
				
			||||||
--- a/drivers/irqchip/irq-bcm2835.c
 | 
					 | 
				
			||||||
+++ b/drivers/irqchip/irq-bcm2835.c
 | 
					 | 
				
			||||||
@@ -43,7 +43,9 @@
 | 
					 | 
				
			||||||
 #include <linux/irqdomain.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #include <asm/exception.h>
 | 
					 | 
				
			||||||
+#ifndef CONFIG_ARM64
 | 
					 | 
				
			||||||
 #include <asm/mach/irq.h>
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /* Put the bank and irq (32 bits) into the hwirq */
 | 
					 | 
				
			||||||
 #define MAKE_HWIRQ(b, n)	(((b) << 5) | (n))
 | 
					 | 
				
			||||||
@@ -72,6 +74,7 @@
 | 
					 | 
				
			||||||
 #define NR_BANKS		3
 | 
					 | 
				
			||||||
 #define IRQS_PER_BANK		32
 | 
					 | 
				
			||||||
 #define NUMBER_IRQS		MAKE_HWIRQ(NR_BANKS, 0)
 | 
					 | 
				
			||||||
+#undef FIQ_START
 | 
					 | 
				
			||||||
 #define FIQ_START		(NR_IRQS_BANK0 + MAKE_HWIRQ(NR_BANKS - 1, 0))
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static const int reg_pending[] __initconst = { 0x00, 0x04, 0x08 };
 | 
					 | 
				
			||||||
@@ -253,10 +256,12 @@ static int __init armctrl_of_init(struct
 | 
					 | 
				
			||||||
 					MAKE_HWIRQ(b, i) + NUMBER_IRQS);
 | 
					 | 
				
			||||||
 			BUG_ON(irq <= 0);
 | 
					 | 
				
			||||||
 			irq_set_chip(irq, &armctrl_chip);
 | 
					 | 
				
			||||||
-			set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
 | 
					 | 
				
			||||||
+			irq_set_probe(irq);
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
+#ifndef CONFIG_ARM64
 | 
					 | 
				
			||||||
 	init_FIQ(FIQ_START);
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	return 0;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
--- a/drivers/mailbox/bcm2835-mailbox.c
 | 
					 | 
				
			||||||
+++ b/drivers/mailbox/bcm2835-mailbox.c
 | 
					 | 
				
			||||||
@@ -45,12 +45,15 @@
 | 
					 | 
				
			||||||
 #define MAIL1_WRT	(ARM_0_MAIL1 + 0x00)
 | 
					 | 
				
			||||||
 #define MAIL1_STA	(ARM_0_MAIL1 + 0x18)
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+/* On ARCH_BCM270x these come through <linux/interrupt.h> (arm_control.h ) */
 | 
					 | 
				
			||||||
+#ifndef ARM_MS_FULL
 | 
					 | 
				
			||||||
 /* Status register: FIFO state. */
 | 
					 | 
				
			||||||
 #define ARM_MS_FULL		BIT(31)
 | 
					 | 
				
			||||||
 #define ARM_MS_EMPTY		BIT(30)
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /* Configuration register: Enable interrupts. */
 | 
					 | 
				
			||||||
 #define ARM_MC_IHAVEDATAIRQEN	BIT(0)
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 struct bcm2835_mbox {
 | 
					 | 
				
			||||||
 	void __iomem *regs;
 | 
					 | 
				
			||||||
@@ -145,7 +148,7 @@ static int bcm2835_mbox_probe(struct pla
 | 
					 | 
				
			||||||
 		return -ENOMEM;
 | 
					 | 
				
			||||||
 	spin_lock_init(&mbox->lock);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	ret = devm_request_irq(dev, irq_of_parse_and_map(dev->of_node, 0),
 | 
					 | 
				
			||||||
+	ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
 | 
					 | 
				
			||||||
 			       bcm2835_mbox_irq, 0, dev_name(dev), mbox);
 | 
					 | 
				
			||||||
 	if (ret) {
 | 
					 | 
				
			||||||
 		dev_err(dev, "Failed to register a mailbox IRQ handler: %d\n",
 | 
					 | 
				
			||||||
@@ -195,7 +198,18 @@ static struct platform_driver bcm2835_mb
 | 
					 | 
				
			||||||
 	},
 | 
					 | 
				
			||||||
 	.probe		= bcm2835_mbox_probe,
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
-module_platform_driver(bcm2835_mbox_driver);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int __init bcm2835_mbox_init(void)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return platform_driver_register(&bcm2835_mbox_driver);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+arch_initcall(bcm2835_mbox_init);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void __init bcm2835_mbox_exit(void)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	platform_driver_unregister(&bcm2835_mbox_driver);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+module_exit(bcm2835_mbox_exit);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
 | 
					 | 
				
			||||||
 MODULE_DESCRIPTION("BCM2835 mailbox IPC driver");
 | 
					 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,828 +0,0 @@
 | 
				
			|||||||
From 4ac7128970e15d83ef3c2a65e65ca9ee4096c9d1 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: James Hughes <james.hughes@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Thu, 14 Mar 2019 13:27:54 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] Pulled in the multi frame buffer support from the Pi3
 | 
					 | 
				
			||||||
 repo
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/video/fbdev/bcm2708_fb.c           | 457 +++++++++++++++------
 | 
					 | 
				
			||||||
 include/soc/bcm2835/raspberrypi-firmware.h |  13 +
 | 
					 | 
				
			||||||
 2 files changed, 337 insertions(+), 133 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/video/fbdev/bcm2708_fb.c
 | 
					 | 
				
			||||||
+++ b/drivers/video/fbdev/bcm2708_fb.c
 | 
					 | 
				
			||||||
@@ -2,6 +2,7 @@
 | 
					 | 
				
			||||||
  *  linux/drivers/video/bcm2708_fb.c
 | 
					 | 
				
			||||||
  *
 | 
					 | 
				
			||||||
  * Copyright (C) 2010 Broadcom
 | 
					 | 
				
			||||||
+ * Copyright (C) 2018 Raspberry Pi (Trading) Ltd
 | 
					 | 
				
			||||||
  *
 | 
					 | 
				
			||||||
  * This file is subject to the terms and conditions of the GNU General Public
 | 
					 | 
				
			||||||
  * License.  See the file COPYING in the main directory of this archive
 | 
					 | 
				
			||||||
@@ -13,6 +14,7 @@
 | 
					 | 
				
			||||||
  * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com>
 | 
					 | 
				
			||||||
  *
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 #include <linux/module.h>
 | 
					 | 
				
			||||||
 #include <linux/kernel.h>
 | 
					 | 
				
			||||||
 #include <linux/errno.h>
 | 
					 | 
				
			||||||
@@ -33,6 +35,7 @@
 | 
					 | 
				
			||||||
 #include <linux/io.h>
 | 
					 | 
				
			||||||
 #include <linux/dma-mapping.h>
 | 
					 | 
				
			||||||
 #include <soc/bcm2835/raspberrypi-firmware.h>
 | 
					 | 
				
			||||||
+#include <linux/mutex.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 //#define BCM2708_FB_DEBUG
 | 
					 | 
				
			||||||
 #define MODULE_NAME "bcm2708_fb"
 | 
					 | 
				
			||||||
@@ -79,64 +82,150 @@ struct bcm2708_fb_stats {
 | 
					 | 
				
			||||||
 	u32 dma_irqs;
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+struct vc4_display_settings_t {
 | 
					 | 
				
			||||||
+	u32 display_num;
 | 
					 | 
				
			||||||
+	u32 width;
 | 
					 | 
				
			||||||
+	u32 height;
 | 
					 | 
				
			||||||
+	u32 depth;
 | 
					 | 
				
			||||||
+	u32 pitch;
 | 
					 | 
				
			||||||
+	u32 virtual_width;
 | 
					 | 
				
			||||||
+	u32 virtual_height;
 | 
					 | 
				
			||||||
+	u32 virtual_width_offset;
 | 
					 | 
				
			||||||
+	u32 virtual_height_offset;
 | 
					 | 
				
			||||||
+	unsigned long fb_bus_address;
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+struct bcm2708_fb_dev;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 struct bcm2708_fb {
 | 
					 | 
				
			||||||
 	struct fb_info fb;
 | 
					 | 
				
			||||||
 	struct platform_device *dev;
 | 
					 | 
				
			||||||
-	struct rpi_firmware *fw;
 | 
					 | 
				
			||||||
 	u32 cmap[16];
 | 
					 | 
				
			||||||
 	u32 gpu_cmap[256];
 | 
					 | 
				
			||||||
-	int dma_chan;
 | 
					 | 
				
			||||||
-	int dma_irq;
 | 
					 | 
				
			||||||
-	void __iomem *dma_chan_base;
 | 
					 | 
				
			||||||
-	void *cb_base;		/* DMA control blocks */
 | 
					 | 
				
			||||||
-	dma_addr_t cb_handle;
 | 
					 | 
				
			||||||
 	struct dentry *debugfs_dir;
 | 
					 | 
				
			||||||
-	wait_queue_head_t dma_waitq;
 | 
					 | 
				
			||||||
-	struct bcm2708_fb_stats stats;
 | 
					 | 
				
			||||||
+	struct dentry *debugfs_subdir;
 | 
					 | 
				
			||||||
 	unsigned long fb_bus_address;
 | 
					 | 
				
			||||||
-	bool disable_arm_alloc;
 | 
					 | 
				
			||||||
+	struct { u32 base, length; } gpu;
 | 
					 | 
				
			||||||
+	struct vc4_display_settings_t display_settings;
 | 
					 | 
				
			||||||
+	struct debugfs_regset32 screeninfo_regset;
 | 
					 | 
				
			||||||
+	struct bcm2708_fb_dev *fbdev;
 | 
					 | 
				
			||||||
 	unsigned int image_size;
 | 
					 | 
				
			||||||
 	dma_addr_t dma_addr;
 | 
					 | 
				
			||||||
 	void *cpuaddr;
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+#define MAX_FRAMEBUFFERS 3
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+struct bcm2708_fb_dev {
 | 
					 | 
				
			||||||
+	int firmware_supports_multifb;
 | 
					 | 
				
			||||||
+	/* Protects the DMA system from multiple FB access */
 | 
					 | 
				
			||||||
+	struct mutex dma_mutex;
 | 
					 | 
				
			||||||
+	int dma_chan;
 | 
					 | 
				
			||||||
+	int dma_irq;
 | 
					 | 
				
			||||||
+	void __iomem *dma_chan_base;
 | 
					 | 
				
			||||||
+	wait_queue_head_t dma_waitq;
 | 
					 | 
				
			||||||
+	bool disable_arm_alloc;
 | 
					 | 
				
			||||||
+	struct bcm2708_fb_stats dma_stats;
 | 
					 | 
				
			||||||
+	void *cb_base;	/* DMA control blocks */
 | 
					 | 
				
			||||||
+	dma_addr_t cb_handle;
 | 
					 | 
				
			||||||
+	int instance_count;
 | 
					 | 
				
			||||||
+	int num_displays;
 | 
					 | 
				
			||||||
+	struct rpi_firmware *fw;
 | 
					 | 
				
			||||||
+	struct bcm2708_fb displays[MAX_FRAMEBUFFERS];
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 #define to_bcm2708(info)	container_of(info, struct bcm2708_fb, fb)
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static void bcm2708_fb_debugfs_deinit(struct bcm2708_fb *fb)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
-	debugfs_remove_recursive(fb->debugfs_dir);
 | 
					 | 
				
			||||||
-	fb->debugfs_dir = NULL;
 | 
					 | 
				
			||||||
+	debugfs_remove_recursive(fb->debugfs_subdir);
 | 
					 | 
				
			||||||
+	fb->debugfs_subdir = NULL;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	fb->fbdev->instance_count--;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (!fb->fbdev->instance_count) {
 | 
					 | 
				
			||||||
+		debugfs_remove_recursive(fb->debugfs_dir);
 | 
					 | 
				
			||||||
+		fb->debugfs_dir = NULL;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static int bcm2708_fb_debugfs_init(struct bcm2708_fb *fb)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
+	char buf[3];
 | 
					 | 
				
			||||||
+	struct bcm2708_fb_dev *fbdev = fb->fbdev;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	static struct debugfs_reg32 stats_registers[] = {
 | 
					 | 
				
			||||||
-		{
 | 
					 | 
				
			||||||
-			"dma_copies",
 | 
					 | 
				
			||||||
-			offsetof(struct bcm2708_fb_stats, dma_copies)
 | 
					 | 
				
			||||||
-		},
 | 
					 | 
				
			||||||
-		{
 | 
					 | 
				
			||||||
-			"dma_irqs",
 | 
					 | 
				
			||||||
-			offsetof(struct bcm2708_fb_stats, dma_irqs)
 | 
					 | 
				
			||||||
-		},
 | 
					 | 
				
			||||||
+	{"dma_copies", offsetof(struct bcm2708_fb_stats, dma_copies)},
 | 
					 | 
				
			||||||
+	{"dma_irqs",   offsetof(struct bcm2708_fb_stats, dma_irqs)},
 | 
					 | 
				
			||||||
 	};
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	fb->debugfs_dir = debugfs_create_dir(DRIVER_NAME, NULL);
 | 
					 | 
				
			||||||
+	static struct debugfs_reg32 screeninfo[] = {
 | 
					 | 
				
			||||||
+	{"width",	 offsetof(struct fb_var_screeninfo, xres)},
 | 
					 | 
				
			||||||
+	{"height",	 offsetof(struct fb_var_screeninfo, yres)},
 | 
					 | 
				
			||||||
+	{"bpp",		 offsetof(struct fb_var_screeninfo, bits_per_pixel)},
 | 
					 | 
				
			||||||
+	{"xres_virtual", offsetof(struct fb_var_screeninfo, xres_virtual)},
 | 
					 | 
				
			||||||
+	{"yres_virtual", offsetof(struct fb_var_screeninfo, yres_virtual)},
 | 
					 | 
				
			||||||
+	{"xoffset",	 offsetof(struct fb_var_screeninfo, xoffset)},
 | 
					 | 
				
			||||||
+	{"yoffset",	 offsetof(struct fb_var_screeninfo, yoffset)},
 | 
					 | 
				
			||||||
+	};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	fb->debugfs_dir = debugfs_lookup(DRIVER_NAME, NULL);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (!fb->debugfs_dir)
 | 
					 | 
				
			||||||
+		fb->debugfs_dir = debugfs_create_dir(DRIVER_NAME, NULL);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	if (!fb->debugfs_dir) {
 | 
					 | 
				
			||||||
-		pr_warn("%s: could not create debugfs entry\n",
 | 
					 | 
				
			||||||
-			__func__);
 | 
					 | 
				
			||||||
+		dev_warn(fb->fb.dev, "%s: could not create debugfs folder\n",
 | 
					 | 
				
			||||||
+			 __func__);
 | 
					 | 
				
			||||||
 		return -EFAULT;
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	fb->stats.regset.regs = stats_registers;
 | 
					 | 
				
			||||||
-	fb->stats.regset.nregs = ARRAY_SIZE(stats_registers);
 | 
					 | 
				
			||||||
-	fb->stats.regset.base = &fb->stats;
 | 
					 | 
				
			||||||
+	snprintf(buf, sizeof(buf), "%u", fb->display_settings.display_num);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	fb->debugfs_subdir = debugfs_create_dir(buf, fb->debugfs_dir);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	debugfs_create_regset32("stats", 0444, fb->debugfs_dir,
 | 
					 | 
				
			||||||
 				&fb->stats.regset);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (!fb->debugfs_subdir) {
 | 
					 | 
				
			||||||
+		dev_warn(fb->fb.dev, "%s: could not create debugfs entry %u\n",
 | 
					 | 
				
			||||||
+			 __func__, fb->display_settings.display_num);
 | 
					 | 
				
			||||||
+		return -EFAULT;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	fbdev->dma_stats.regset.regs = stats_registers;
 | 
					 | 
				
			||||||
+	fbdev->dma_stats.regset.nregs = ARRAY_SIZE(stats_registers);
 | 
					 | 
				
			||||||
+	fbdev->dma_stats.regset.base = &fbdev->dma_stats;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	debugfs_create_regset32("dma_stats", 0444, fb->debugfs_subdir,
 | 
					 | 
				
			||||||
+				&fbdev->dma_stats.regset);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	fb->screeninfo_regset.regs = screeninfo;
 | 
					 | 
				
			||||||
+	fb->screeninfo_regset.nregs = ARRAY_SIZE(screeninfo);
 | 
					 | 
				
			||||||
+	fb->screeninfo_regset.base = &fb->fb.var;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	debugfs_create_regset32("screeninfo", 0444, fb->debugfs_subdir,
 | 
					 | 
				
			||||||
+				&fb->screeninfo_regset);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	fbdev->instance_count++;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	return 0;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static void set_display_num(struct bcm2708_fb *fb)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	if (fb && fb->fbdev && fb->fbdev->firmware_supports_multifb) {
 | 
					 | 
				
			||||||
+		u32 tmp = fb->display_settings.display_num;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		if (rpi_firmware_property(fb->fbdev->fw,
 | 
					 | 
				
			||||||
+					  RPI_FIRMWARE_FRAMEBUFFER_SET_DISPLAY_NUM,
 | 
					 | 
				
			||||||
+					  &tmp,
 | 
					 | 
				
			||||||
+					  sizeof(tmp)))
 | 
					 | 
				
			||||||
+			dev_warn_once(fb->fb.dev,
 | 
					 | 
				
			||||||
+				      "Set display number call failed. Old GPU firmware?");
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static int bcm2708_fb_set_bitfields(struct fb_var_screeninfo *var)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	int ret = 0;
 | 
					 | 
				
			||||||
@@ -214,11 +303,11 @@ static int bcm2708_fb_check_var(struct f
 | 
					 | 
				
			||||||
 				struct fb_info *info)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	/* info input, var output */
 | 
					 | 
				
			||||||
-	print_debug("%s(%p) %dx%d (%dx%d), %d, %d\n",
 | 
					 | 
				
			||||||
+	print_debug("%s(%p) %ux%u (%ux%u), %ul, %u\n",
 | 
					 | 
				
			||||||
 		    __func__, info, info->var.xres, info->var.yres,
 | 
					 | 
				
			||||||
 		    info->var.xres_virtual, info->var.yres_virtual,
 | 
					 | 
				
			||||||
-		    (int)info->screen_size, info->var.bits_per_pixel);
 | 
					 | 
				
			||||||
-	print_debug("%s(%p) %dx%d (%dx%d), %d\n", __func__, var, var->xres,
 | 
					 | 
				
			||||||
+		    info->screen_size, info->var.bits_per_pixel);
 | 
					 | 
				
			||||||
+	print_debug("%s(%p) %ux%u (%ux%u), %u\n", __func__, var, var->xres,
 | 
					 | 
				
			||||||
 		    var->yres, var->xres_virtual, var->yres_virtual,
 | 
					 | 
				
			||||||
 		    var->bits_per_pixel);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -281,17 +370,24 @@ static int bcm2708_fb_set_par(struct fb_
 | 
					 | 
				
			||||||
 	};
 | 
					 | 
				
			||||||
 	int ret, image_size;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	print_debug("%s(%p) %dx%d (%dx%d), %d, %d\n", __func__, info,
 | 
					 | 
				
			||||||
+	print_debug("%s(%p) %dx%d (%dx%d), %d, %d (display %d)\n", __func__,
 | 
					 | 
				
			||||||
+		    info,
 | 
					 | 
				
			||||||
 		    info->var.xres, info->var.yres, info->var.xres_virtual,
 | 
					 | 
				
			||||||
 		    info->var.yres_virtual, (int)info->screen_size,
 | 
					 | 
				
			||||||
-		    info->var.bits_per_pixel);
 | 
					 | 
				
			||||||
+		    info->var.bits_per_pixel, value);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* Need to set the display number to act on first
 | 
					 | 
				
			||||||
+	 * Cannot do it in the tag list because on older firmware the call
 | 
					 | 
				
			||||||
+	 * will fail and stop the rest of the list being executed.
 | 
					 | 
				
			||||||
+	 * We can ignore this call failing as the default at other end is 0
 | 
					 | 
				
			||||||
+	 */
 | 
					 | 
				
			||||||
+	set_display_num(fb);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* Try allocating our own buffer. We can specify all the parameters */
 | 
					 | 
				
			||||||
 	image_size = ((info->var.xres * info->var.yres) *
 | 
					 | 
				
			||||||
 		      info->var.bits_per_pixel) >> 3;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if (!fb->disable_arm_alloc &&
 | 
					 | 
				
			||||||
+	if (!fb->fbdev->disable_arm_alloc &&
 | 
					 | 
				
			||||||
 	    (image_size != fb->image_size || !fb->dma_addr)) {
 | 
					 | 
				
			||||||
 		if (fb->dma_addr) {
 | 
					 | 
				
			||||||
 			dma_free_coherent(info->device, fb->image_size,
 | 
					 | 
				
			||||||
@@ -306,7 +402,7 @@ static int bcm2708_fb_set_par(struct fb_
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 		if (!fb->cpuaddr) {
 | 
					 | 
				
			||||||
 			fb->dma_addr = 0;
 | 
					 | 
				
			||||||
-			fb->disable_arm_alloc = true;
 | 
					 | 
				
			||||||
+			fb->fbdev->disable_arm_alloc = true;
 | 
					 | 
				
			||||||
 		} else {
 | 
					 | 
				
			||||||
 			fb->image_size = image_size;
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
@@ -317,7 +413,7 @@ static int bcm2708_fb_set_par(struct fb_
 | 
					 | 
				
			||||||
 		fbinfo.screen_size = image_size;
 | 
					 | 
				
			||||||
 		fbinfo.pitch = (info->var.xres * info->var.bits_per_pixel) >> 3;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-		ret = rpi_firmware_property_list(fb->fw, &fbinfo,
 | 
					 | 
				
			||||||
+		ret = rpi_firmware_property_list(fb->fbdev->fw, &fbinfo,
 | 
					 | 
				
			||||||
 						 sizeof(fbinfo));
 | 
					 | 
				
			||||||
 		if (ret || fbinfo.base != fb->dma_addr) {
 | 
					 | 
				
			||||||
 			/* Firmware either failed, or assigned a different base
 | 
					 | 
				
			||||||
@@ -330,7 +426,7 @@ static int bcm2708_fb_set_par(struct fb_
 | 
					 | 
				
			||||||
 			fb->image_size = 0;
 | 
					 | 
				
			||||||
 			fb->cpuaddr = NULL;
 | 
					 | 
				
			||||||
 			fb->dma_addr = 0;
 | 
					 | 
				
			||||||
-			fb->disable_arm_alloc = true;
 | 
					 | 
				
			||||||
+			fb->fbdev->disable_arm_alloc = true;
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
 	} else {
 | 
					 | 
				
			||||||
 		/* Our allocation failed - drop into the old scheme of
 | 
					 | 
				
			||||||
@@ -349,7 +445,7 @@ static int bcm2708_fb_set_par(struct fb_
 | 
					 | 
				
			||||||
 		fbinfo.tag6.tag = RPI_FIRMWARE_FRAMEBUFFER_GET_PITCH;
 | 
					 | 
				
			||||||
 		fbinfo.pitch = 0;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-		ret = rpi_firmware_property_list(fb->fw, &fbinfo,
 | 
					 | 
				
			||||||
+		ret = rpi_firmware_property_list(fb->fbdev->fw, &fbinfo,
 | 
					 | 
				
			||||||
 						 sizeof(fbinfo));
 | 
					 | 
				
			||||||
 		if (ret) {
 | 
					 | 
				
			||||||
 			dev_err(info->device,
 | 
					 | 
				
			||||||
@@ -439,7 +535,10 @@ static int bcm2708_fb_setcolreg(unsigned
 | 
					 | 
				
			||||||
 			packet->length = regno + 1;
 | 
					 | 
				
			||||||
 			memcpy(packet->cmap, fb->gpu_cmap,
 | 
					 | 
				
			||||||
 			       sizeof(packet->cmap));
 | 
					 | 
				
			||||||
-			ret = rpi_firmware_property(fb->fw,
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+			set_display_num(fb);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+			ret = rpi_firmware_property(fb->fbdev->fw,
 | 
					 | 
				
			||||||
 						    RPI_FIRMWARE_FRAMEBUFFER_SET_PALETTE,
 | 
					 | 
				
			||||||
 						    packet,
 | 
					 | 
				
			||||||
 						    (2 + packet->length) * sizeof(u32));
 | 
					 | 
				
			||||||
@@ -478,8 +577,11 @@ static int bcm2708_fb_blank(int blank_mo
 | 
					 | 
				
			||||||
 		return -EINVAL;
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	ret = rpi_firmware_property(fb->fw, RPI_FIRMWARE_FRAMEBUFFER_BLANK,
 | 
					 | 
				
			||||||
+	set_display_num(fb);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ret = rpi_firmware_property(fb->fbdev->fw, RPI_FIRMWARE_FRAMEBUFFER_BLANK,
 | 
					 | 
				
			||||||
 				    &value, sizeof(value));
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	if (ret)
 | 
					 | 
				
			||||||
 		dev_err(info->device, "%s(%d) failed: %d\n", __func__,
 | 
					 | 
				
			||||||
 			blank_mode, ret);
 | 
					 | 
				
			||||||
@@ -496,12 +598,14 @@ static int bcm2708_fb_pan_display(struct
 | 
					 | 
				
			||||||
 	info->var.yoffset = var->yoffset;
 | 
					 | 
				
			||||||
 	result = bcm2708_fb_set_par(info);
 | 
					 | 
				
			||||||
 	if (result != 0)
 | 
					 | 
				
			||||||
-		pr_err("%s(%d,%d) returns=%d\n", __func__, var->xoffset,
 | 
					 | 
				
			||||||
+		pr_err("%s(%u,%u) returns=%d\n", __func__, var->xoffset,
 | 
					 | 
				
			||||||
 		       var->yoffset, result);
 | 
					 | 
				
			||||||
 	return result;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static int bcm2708_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
 | 
					 | 
				
			||||||
+static int bcm2708_ioctl(struct fb_info *info, unsigned int cmd,
 | 
					 | 
				
			||||||
+			 unsigned long arg)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct bcm2708_fb *fb = to_bcm2708(info);
 | 
					 | 
				
			||||||
 	u32 dummy = 0;
 | 
					 | 
				
			||||||
@@ -509,7 +613,9 @@ static int bcm2708_ioctl(struct fb_info
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	switch (cmd) {
 | 
					 | 
				
			||||||
 	case FBIO_WAITFORVSYNC:
 | 
					 | 
				
			||||||
-		ret = rpi_firmware_property(fb->fw,
 | 
					 | 
				
			||||||
+		set_display_num(fb);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		ret = rpi_firmware_property(fb->fbdev->fw,
 | 
					 | 
				
			||||||
 					    RPI_FIRMWARE_FRAMEBUFFER_SET_VSYNC,
 | 
					 | 
				
			||||||
 					    &dummy, sizeof(dummy));
 | 
					 | 
				
			||||||
 		break;
 | 
					 | 
				
			||||||
@@ -526,23 +632,22 @@ static int bcm2708_ioctl(struct fb_info
 | 
					 | 
				
			||||||
 static void bcm2708_fb_fillrect(struct fb_info *info,
 | 
					 | 
				
			||||||
 				const struct fb_fillrect *rect)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
-	/* (is called) print_debug("bcm2708_fb_fillrect\n"); */
 | 
					 | 
				
			||||||
 	cfb_fillrect(info, rect);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /* A helper function for configuring dma control block */
 | 
					 | 
				
			||||||
 static void set_dma_cb(struct bcm2708_dma_cb *cb,
 | 
					 | 
				
			||||||
-		       int        burst_size,
 | 
					 | 
				
			||||||
-		       dma_addr_t dst,
 | 
					 | 
				
			||||||
-		       int        dst_stride,
 | 
					 | 
				
			||||||
-		       dma_addr_t src,
 | 
					 | 
				
			||||||
-		       int        src_stride,
 | 
					 | 
				
			||||||
-		       int        w,
 | 
					 | 
				
			||||||
-		       int        h)
 | 
					 | 
				
			||||||
+		int        burst_size,
 | 
					 | 
				
			||||||
+		dma_addr_t dst,
 | 
					 | 
				
			||||||
+		int        dst_stride,
 | 
					 | 
				
			||||||
+		dma_addr_t src,
 | 
					 | 
				
			||||||
+		int        src_stride,
 | 
					 | 
				
			||||||
+		int        w,
 | 
					 | 
				
			||||||
+		int        h)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	cb->info = BCM2708_DMA_BURST(burst_size) | BCM2708_DMA_S_WIDTH |
 | 
					 | 
				
			||||||
-		   BCM2708_DMA_S_INC | BCM2708_DMA_D_WIDTH |
 | 
					 | 
				
			||||||
-		   BCM2708_DMA_D_INC | BCM2708_DMA_TDMODE;
 | 
					 | 
				
			||||||
+		BCM2708_DMA_S_INC | BCM2708_DMA_D_WIDTH |
 | 
					 | 
				
			||||||
+		BCM2708_DMA_D_INC | BCM2708_DMA_TDMODE;
 | 
					 | 
				
			||||||
 	cb->dst = dst;
 | 
					 | 
				
			||||||
 	cb->src = src;
 | 
					 | 
				
			||||||
 	/*
 | 
					 | 
				
			||||||
@@ -560,15 +665,19 @@ static void bcm2708_fb_copyarea(struct f
 | 
					 | 
				
			||||||
 				const struct fb_copyarea *region)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct bcm2708_fb *fb = to_bcm2708(info);
 | 
					 | 
				
			||||||
-	struct bcm2708_dma_cb *cb = fb->cb_base;
 | 
					 | 
				
			||||||
+	struct bcm2708_fb_dev *fbdev = fb->fbdev;
 | 
					 | 
				
			||||||
+	struct bcm2708_dma_cb *cb = fbdev->cb_base;
 | 
					 | 
				
			||||||
 	int bytes_per_pixel = (info->var.bits_per_pixel + 7) >> 3;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* Channel 0 supports larger bursts and is a bit faster */
 | 
					 | 
				
			||||||
-	int burst_size = (fb->dma_chan == 0) ? 8 : 2;
 | 
					 | 
				
			||||||
+	int burst_size = (fbdev->dma_chan == 0) ? 8 : 2;
 | 
					 | 
				
			||||||
 	int pixels = region->width * region->height;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	/* Fallback to cfb_copyarea() if we don't like something */
 | 
					 | 
				
			||||||
-	if (bytes_per_pixel > 4 ||
 | 
					 | 
				
			||||||
+	/* If DMA is currently in use (ie being used on another FB), then
 | 
					 | 
				
			||||||
+	 * rather than wait for it to finish, just use the cfb_copyarea
 | 
					 | 
				
			||||||
+	 */
 | 
					 | 
				
			||||||
+	if (!mutex_trylock(&fbdev->dma_mutex) ||
 | 
					 | 
				
			||||||
+	    bytes_per_pixel > 4 ||
 | 
					 | 
				
			||||||
 	    info->var.xres * info->var.yres > 1920 * 1200 ||
 | 
					 | 
				
			||||||
 	    region->width <= 0 || region->width > info->var.xres ||
 | 
					 | 
				
			||||||
 	    region->height <= 0 || region->height > info->var.yres ||
 | 
					 | 
				
			||||||
@@ -595,8 +704,8 @@ static void bcm2708_fb_copyarea(struct f
 | 
					 | 
				
			||||||
 		 * 1920x1200 resolution at 32bpp pixel depth.
 | 
					 | 
				
			||||||
 		 */
 | 
					 | 
				
			||||||
 		int y;
 | 
					 | 
				
			||||||
-		dma_addr_t control_block_pa = fb->cb_handle;
 | 
					 | 
				
			||||||
-		dma_addr_t scratchbuf = fb->cb_handle + 16 * 1024;
 | 
					 | 
				
			||||||
+		dma_addr_t control_block_pa = fbdev->cb_handle;
 | 
					 | 
				
			||||||
+		dma_addr_t scratchbuf = fbdev->cb_handle + 16 * 1024;
 | 
					 | 
				
			||||||
 		int scanline_size = bytes_per_pixel * region->width;
 | 
					 | 
				
			||||||
 		int scanlines_per_cb = (64 * 1024 - 16 * 1024) / scanline_size;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -646,10 +755,10 @@ static void bcm2708_fb_copyarea(struct f
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
 		set_dma_cb(cb, burst_size,
 | 
					 | 
				
			||||||
 			   fb->fb_bus_address + dy * fb->fb.fix.line_length +
 | 
					 | 
				
			||||||
-						   bytes_per_pixel * region->dx,
 | 
					 | 
				
			||||||
+			   bytes_per_pixel * region->dx,
 | 
					 | 
				
			||||||
 			   stride,
 | 
					 | 
				
			||||||
 			   fb->fb_bus_address + sy * fb->fb.fix.line_length +
 | 
					 | 
				
			||||||
-						   bytes_per_pixel * region->sx,
 | 
					 | 
				
			||||||
+			   bytes_per_pixel * region->sx,
 | 
					 | 
				
			||||||
 			   stride,
 | 
					 | 
				
			||||||
 			   region->width * bytes_per_pixel,
 | 
					 | 
				
			||||||
 			   region->height);
 | 
					 | 
				
			||||||
@@ -659,32 +768,33 @@ static void bcm2708_fb_copyarea(struct f
 | 
					 | 
				
			||||||
 	cb->next = 0;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	if (pixels < dma_busy_wait_threshold) {
 | 
					 | 
				
			||||||
-		bcm_dma_start(fb->dma_chan_base, fb->cb_handle);
 | 
					 | 
				
			||||||
-		bcm_dma_wait_idle(fb->dma_chan_base);
 | 
					 | 
				
			||||||
+		bcm_dma_start(fbdev->dma_chan_base, fbdev->cb_handle);
 | 
					 | 
				
			||||||
+		bcm_dma_wait_idle(fbdev->dma_chan_base);
 | 
					 | 
				
			||||||
 	} else {
 | 
					 | 
				
			||||||
-		void __iomem *dma_chan = fb->dma_chan_base;
 | 
					 | 
				
			||||||
+		void __iomem *local_dma_chan = fbdev->dma_chan_base;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 		cb->info |= BCM2708_DMA_INT_EN;
 | 
					 | 
				
			||||||
-		bcm_dma_start(fb->dma_chan_base, fb->cb_handle);
 | 
					 | 
				
			||||||
-		while (bcm_dma_is_busy(dma_chan)) {
 | 
					 | 
				
			||||||
-			wait_event_interruptible(fb->dma_waitq,
 | 
					 | 
				
			||||||
-						 !bcm_dma_is_busy(dma_chan));
 | 
					 | 
				
			||||||
+		bcm_dma_start(fbdev->dma_chan_base, fbdev->cb_handle);
 | 
					 | 
				
			||||||
+		while (bcm_dma_is_busy(local_dma_chan)) {
 | 
					 | 
				
			||||||
+			wait_event_interruptible(fbdev->dma_waitq,
 | 
					 | 
				
			||||||
+						 !bcm_dma_is_busy(local_dma_chan));
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
-		fb->stats.dma_irqs++;
 | 
					 | 
				
			||||||
+		fbdev->dma_stats.dma_irqs++;
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
-	fb->stats.dma_copies++;
 | 
					 | 
				
			||||||
+	fbdev->dma_stats.dma_copies++;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	mutex_unlock(&fbdev->dma_mutex);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static void bcm2708_fb_imageblit(struct fb_info *info,
 | 
					 | 
				
			||||||
 				 const struct fb_image *image)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
-	/* (is called) print_debug("bcm2708_fb_imageblit\n"); */
 | 
					 | 
				
			||||||
 	cfb_imageblit(info, image);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static irqreturn_t bcm2708_fb_dma_irq(int irq, void *cxt)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
-	struct bcm2708_fb *fb = cxt;
 | 
					 | 
				
			||||||
+	struct bcm2708_fb_dev *fbdev = cxt;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* FIXME: should read status register to check if this is
 | 
					 | 
				
			||||||
 	 * actually interrupting us or not, in case this interrupt
 | 
					 | 
				
			||||||
@@ -694,9 +804,9 @@ static irqreturn_t bcm2708_fb_dma_irq(in
 | 
					 | 
				
			||||||
 	 */
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* acknowledge the interrupt */
 | 
					 | 
				
			||||||
-	writel(BCM2708_DMA_INT, fb->dma_chan_base + BCM2708_DMA_CS);
 | 
					 | 
				
			||||||
+	writel(BCM2708_DMA_INT, fbdev->dma_chan_base + BCM2708_DMA_CS);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	wake_up(&fb->dma_waitq);
 | 
					 | 
				
			||||||
+	wake_up(&fbdev->dma_waitq);
 | 
					 | 
				
			||||||
 	return IRQ_HANDLED;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -729,11 +839,23 @@ static int bcm2708_fb_register(struct bc
 | 
					 | 
				
			||||||
 	fb->fb.fix.ywrapstep = 0;
 | 
					 | 
				
			||||||
 	fb->fb.fix.accel = FB_ACCEL_NONE;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	fb->fb.var.xres = fbwidth;
 | 
					 | 
				
			||||||
-	fb->fb.var.yres = fbheight;
 | 
					 | 
				
			||||||
-	fb->fb.var.xres_virtual = fbwidth;
 | 
					 | 
				
			||||||
-	fb->fb.var.yres_virtual = fbheight;
 | 
					 | 
				
			||||||
-	fb->fb.var.bits_per_pixel = fbdepth;
 | 
					 | 
				
			||||||
+	/* If we have data from the VC4 on FB's, use that, otherwise use the
 | 
					 | 
				
			||||||
+	 * module parameters
 | 
					 | 
				
			||||||
+	 */
 | 
					 | 
				
			||||||
+	if (fb->display_settings.width) {
 | 
					 | 
				
			||||||
+		fb->fb.var.xres = fb->display_settings.width;
 | 
					 | 
				
			||||||
+		fb->fb.var.yres = fb->display_settings.height;
 | 
					 | 
				
			||||||
+		fb->fb.var.xres_virtual = fb->fb.var.xres;
 | 
					 | 
				
			||||||
+		fb->fb.var.yres_virtual = fb->fb.var.yres;
 | 
					 | 
				
			||||||
+		fb->fb.var.bits_per_pixel = fb->display_settings.depth;
 | 
					 | 
				
			||||||
+	} else {
 | 
					 | 
				
			||||||
+		fb->fb.var.xres = fbwidth;
 | 
					 | 
				
			||||||
+		fb->fb.var.yres = fbheight;
 | 
					 | 
				
			||||||
+		fb->fb.var.xres_virtual = fbwidth;
 | 
					 | 
				
			||||||
+		fb->fb.var.yres_virtual = fbheight;
 | 
					 | 
				
			||||||
+		fb->fb.var.bits_per_pixel = fbdepth;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	fb->fb.var.vmode = FB_VMODE_NONINTERLACED;
 | 
					 | 
				
			||||||
 	fb->fb.var.activate = FB_ACTIVATE_NOW;
 | 
					 | 
				
			||||||
 	fb->fb.var.nonstd = 0;
 | 
					 | 
				
			||||||
@@ -749,26 +871,23 @@ static int bcm2708_fb_register(struct bc
 | 
					 | 
				
			||||||
 	fb->fb.monspecs.dclkmax = 100000000;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	bcm2708_fb_set_bitfields(&fb->fb.var);
 | 
					 | 
				
			||||||
-	init_waitqueue_head(&fb->dma_waitq);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/*
 | 
					 | 
				
			||||||
 	 * Allocate colourmap.
 | 
					 | 
				
			||||||
 	 */
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 	fb_set_var(&fb->fb, &fb->fb.var);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	ret = bcm2708_fb_set_par(&fb->fb);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	if (ret)
 | 
					 | 
				
			||||||
 		return ret;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	print_debug("BCM2708FB: registering framebuffer (%dx%d@%d) (%d)\n",
 | 
					 | 
				
			||||||
-		    fbwidth, fbheight, fbdepth, fbswap);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 	ret = register_framebuffer(&fb->fb);
 | 
					 | 
				
			||||||
-	print_debug("BCM2708FB: register framebuffer (%d)\n", ret);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	if (ret == 0)
 | 
					 | 
				
			||||||
 		goto out;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	print_debug("BCM2708FB: cannot register framebuffer (%d)\n", ret);
 | 
					 | 
				
			||||||
+	dev_warn(fb->fb.dev, "Unable to register framebuffer (%d)\n", ret);
 | 
					 | 
				
			||||||
 out:
 | 
					 | 
				
			||||||
 	return ret;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
@@ -777,10 +896,18 @@ static int bcm2708_fb_probe(struct platf
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct device_node *fw_np;
 | 
					 | 
				
			||||||
 	struct rpi_firmware *fw;
 | 
					 | 
				
			||||||
-	struct bcm2708_fb *fb;
 | 
					 | 
				
			||||||
-	int ret;
 | 
					 | 
				
			||||||
+	int ret, i;
 | 
					 | 
				
			||||||
+	u32 num_displays;
 | 
					 | 
				
			||||||
+	struct bcm2708_fb_dev *fbdev;
 | 
					 | 
				
			||||||
+	struct { u32 base, length; } gpu_mem;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	fbdev = devm_kzalloc(&dev->dev, sizeof(*fbdev), GFP_KERNEL);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (!fbdev)
 | 
					 | 
				
			||||||
+		return -ENOMEM;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	fw_np = of_parse_phandle(dev->dev.of_node, "firmware", 0);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 /* Remove comment when booting without Device Tree is no longer supported
 | 
					 | 
				
			||||||
  *	if (!fw_np) {
 | 
					 | 
				
			||||||
  *		dev_err(&dev->dev, "Missing firmware node\n");
 | 
					 | 
				
			||||||
@@ -788,90 +915,154 @@ static int bcm2708_fb_probe(struct platf
 | 
					 | 
				
			||||||
  *	}
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
 	fw = rpi_firmware_get(fw_np);
 | 
					 | 
				
			||||||
+	fbdev->fw = fw;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	if (!fw)
 | 
					 | 
				
			||||||
 		return -EPROBE_DEFER;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	fb = kzalloc(sizeof(*fb), GFP_KERNEL);
 | 
					 | 
				
			||||||
-	if (!fb) {
 | 
					 | 
				
			||||||
-		ret = -ENOMEM;
 | 
					 | 
				
			||||||
-		goto free_region;
 | 
					 | 
				
			||||||
+	ret = rpi_firmware_property(fw,
 | 
					 | 
				
			||||||
+				    RPI_FIRMWARE_FRAMEBUFFER_GET_NUM_DISPLAYS,
 | 
					 | 
				
			||||||
+				    &num_displays, sizeof(u32));
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* If we fail to get the number of displays, or it returns 0, then
 | 
					 | 
				
			||||||
+	 * assume old firmware that doesn't have the mailbox call, so just
 | 
					 | 
				
			||||||
+	 * set one display
 | 
					 | 
				
			||||||
+	 */
 | 
					 | 
				
			||||||
+	if (ret || num_displays == 0) {
 | 
					 | 
				
			||||||
+		num_displays = 1;
 | 
					 | 
				
			||||||
+		dev_err(&dev->dev,
 | 
					 | 
				
			||||||
+			"Unable to determine number of FB's. Assuming 1\n");
 | 
					 | 
				
			||||||
+		ret = 0;
 | 
					 | 
				
			||||||
+	} else {
 | 
					 | 
				
			||||||
+		fbdev->firmware_supports_multifb = 1;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (num_displays > MAX_FRAMEBUFFERS) {
 | 
					 | 
				
			||||||
+		dev_warn(&dev->dev,
 | 
					 | 
				
			||||||
+			 "More displays reported from firmware than supported in driver (%u vs %u)",
 | 
					 | 
				
			||||||
+			 num_displays, MAX_FRAMEBUFFERS);
 | 
					 | 
				
			||||||
+		num_displays = MAX_FRAMEBUFFERS;
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	fb->fw = fw;
 | 
					 | 
				
			||||||
-	bcm2708_fb_debugfs_init(fb);
 | 
					 | 
				
			||||||
+	dev_info(&dev->dev, "FB found %d display(s)\n", num_displays);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* Set up the DMA information. Note we have just one set of DMA
 | 
					 | 
				
			||||||
+	 * parameters to work with all the FB's so requires synchronising when
 | 
					 | 
				
			||||||
+	 * being used
 | 
					 | 
				
			||||||
+	 */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	mutex_init(&fbdev->dma_mutex);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	fb->cb_base = dma_alloc_wc(&dev->dev, SZ_64K,
 | 
					 | 
				
			||||||
-					     &fb->cb_handle, GFP_KERNEL);
 | 
					 | 
				
			||||||
-	if (!fb->cb_base) {
 | 
					 | 
				
			||||||
+	fbdev->cb_base = dma_alloc_wc(&dev->dev, SZ_64K,
 | 
					 | 
				
			||||||
+						&fbdev->cb_handle,
 | 
					 | 
				
			||||||
+						GFP_KERNEL);
 | 
					 | 
				
			||||||
+	if (!fbdev->cb_base) {
 | 
					 | 
				
			||||||
 		dev_err(&dev->dev, "cannot allocate DMA CBs\n");
 | 
					 | 
				
			||||||
 		ret = -ENOMEM;
 | 
					 | 
				
			||||||
 		goto free_fb;
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	pr_info("BCM2708FB: allocated DMA memory %pad\n", &fb->cb_handle);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 	ret = bcm_dma_chan_alloc(BCM_DMA_FEATURE_BULK,
 | 
					 | 
				
			||||||
-				 &fb->dma_chan_base, &fb->dma_irq);
 | 
					 | 
				
			||||||
+				 &fbdev->dma_chan_base,
 | 
					 | 
				
			||||||
+				 &fbdev->dma_irq);
 | 
					 | 
				
			||||||
 	if (ret < 0) {
 | 
					 | 
				
			||||||
-		dev_err(&dev->dev, "couldn't allocate a DMA channel\n");
 | 
					 | 
				
			||||||
+		dev_err(&dev->dev, "Couldn't allocate a DMA channel\n");
 | 
					 | 
				
			||||||
 		goto free_cb;
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
-	fb->dma_chan = ret;
 | 
					 | 
				
			||||||
+	fbdev->dma_chan = ret;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	ret = request_irq(fb->dma_irq, bcm2708_fb_dma_irq,
 | 
					 | 
				
			||||||
-			  0, "bcm2708_fb dma", fb);
 | 
					 | 
				
			||||||
+	ret = request_irq(fbdev->dma_irq, bcm2708_fb_dma_irq,
 | 
					 | 
				
			||||||
+			  0, "bcm2708_fb DMA", fbdev);
 | 
					 | 
				
			||||||
 	if (ret) {
 | 
					 | 
				
			||||||
-		pr_err("%s: failed to request DMA irq\n", __func__);
 | 
					 | 
				
			||||||
+		dev_err(&dev->dev,
 | 
					 | 
				
			||||||
+			"Failed to request DMA irq\n");
 | 
					 | 
				
			||||||
 		goto free_dma_chan;
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	pr_info("BCM2708FB: allocated DMA channel %d\n", fb->dma_chan);
 | 
					 | 
				
			||||||
+	rpi_firmware_property(fbdev->fw,
 | 
					 | 
				
			||||||
+			      RPI_FIRMWARE_GET_VC_MEMORY,
 | 
					 | 
				
			||||||
+			      &gpu_mem, sizeof(gpu_mem));
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	for (i = 0; i < num_displays; i++) {
 | 
					 | 
				
			||||||
+		struct bcm2708_fb *fb = &fbdev->displays[i];
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		fb->display_settings.display_num = i;
 | 
					 | 
				
			||||||
+		fb->dev = dev;
 | 
					 | 
				
			||||||
+		fb->fb.device = &dev->dev;
 | 
					 | 
				
			||||||
+		fb->fbdev = fbdev;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		fb->gpu.base = gpu_mem.base;
 | 
					 | 
				
			||||||
+		fb->gpu.length = gpu_mem.length;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		if (fbdev->firmware_supports_multifb) {
 | 
					 | 
				
			||||||
+			ret = rpi_firmware_property(fw,
 | 
					 | 
				
			||||||
+						    RPI_FIRMWARE_FRAMEBUFFER_GET_DISPLAY_SETTINGS,
 | 
					 | 
				
			||||||
+						    &fb->display_settings,
 | 
					 | 
				
			||||||
+						    GET_DISPLAY_SETTINGS_PAYLOAD_SIZE);
 | 
					 | 
				
			||||||
+		} else {
 | 
					 | 
				
			||||||
+			memset(&fb->display_settings, 0,
 | 
					 | 
				
			||||||
+			       sizeof(fb->display_settings));
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	fb->dev = dev;
 | 
					 | 
				
			||||||
-	fb->fb.device = &dev->dev;
 | 
					 | 
				
			||||||
+		ret = bcm2708_fb_register(fb);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	/* failure here isn't fatal, but we'll fail in vc_mem_copy if
 | 
					 | 
				
			||||||
-	 * fb->gpu is not valid
 | 
					 | 
				
			||||||
-	 */
 | 
					 | 
				
			||||||
-	rpi_firmware_property(fb->fw, RPI_FIRMWARE_GET_VC_MEMORY, &fb->gpu,
 | 
					 | 
				
			||||||
-			      sizeof(fb->gpu));
 | 
					 | 
				
			||||||
+		if (ret == 0) {
 | 
					 | 
				
			||||||
+			bcm2708_fb_debugfs_init(fb);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	ret = bcm2708_fb_register(fb);
 | 
					 | 
				
			||||||
-	if (ret == 0) {
 | 
					 | 
				
			||||||
-		platform_set_drvdata(dev, fb);
 | 
					 | 
				
			||||||
-		goto out;
 | 
					 | 
				
			||||||
+			fbdev->num_displays++;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+			dev_info(&dev->dev,
 | 
					 | 
				
			||||||
+				 "Registered framebuffer for display %u, size %ux%u\n",
 | 
					 | 
				
			||||||
+				 fb->display_settings.display_num,
 | 
					 | 
				
			||||||
+				 fb->fb.var.xres,
 | 
					 | 
				
			||||||
+				 fb->fb.var.yres);
 | 
					 | 
				
			||||||
+		} else {
 | 
					 | 
				
			||||||
+			// Use this to flag if this FB entry is in use.
 | 
					 | 
				
			||||||
+			fb->fbdev = NULL;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	// Did we actually successfully create any FB's?
 | 
					 | 
				
			||||||
+	if (fbdev->num_displays) {
 | 
					 | 
				
			||||||
+		init_waitqueue_head(&fbdev->dma_waitq);
 | 
					 | 
				
			||||||
+		platform_set_drvdata(dev, fbdev);
 | 
					 | 
				
			||||||
+		return ret;
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 free_dma_chan:
 | 
					 | 
				
			||||||
-	bcm_dma_chan_free(fb->dma_chan);
 | 
					 | 
				
			||||||
+	bcm_dma_chan_free(fbdev->dma_chan);
 | 
					 | 
				
			||||||
 free_cb:
 | 
					 | 
				
			||||||
-	dma_free_wc(&dev->dev, SZ_64K, fb->cb_base, fb->cb_handle);
 | 
					 | 
				
			||||||
+	dma_free_wc(&dev->dev, SZ_64K, fbdev->cb_base,
 | 
					 | 
				
			||||||
+			      fbdev->cb_handle);
 | 
					 | 
				
			||||||
 free_fb:
 | 
					 | 
				
			||||||
-	kfree(fb);
 | 
					 | 
				
			||||||
-free_region:
 | 
					 | 
				
			||||||
 	dev_err(&dev->dev, "probe failed, err %d\n", ret);
 | 
					 | 
				
			||||||
-out:
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	return ret;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static int bcm2708_fb_remove(struct platform_device *dev)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
-	struct bcm2708_fb *fb = platform_get_drvdata(dev);
 | 
					 | 
				
			||||||
+	struct bcm2708_fb_dev *fbdev = platform_get_drvdata(dev);
 | 
					 | 
				
			||||||
+	int i;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	platform_set_drvdata(dev, NULL);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if (fb->fb.screen_base)
 | 
					 | 
				
			||||||
-		iounmap(fb->fb.screen_base);
 | 
					 | 
				
			||||||
-	unregister_framebuffer(&fb->fb);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	dma_free_wc(&dev->dev, SZ_64K, fb->cb_base, fb->cb_handle);
 | 
					 | 
				
			||||||
-	bcm_dma_chan_free(fb->dma_chan);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	bcm2708_fb_debugfs_deinit(fb);
 | 
					 | 
				
			||||||
+	for (i = 0; i < fbdev->num_displays; i++) {
 | 
					 | 
				
			||||||
+		if (fbdev->displays[i].fb.screen_base)
 | 
					 | 
				
			||||||
+			iounmap(fbdev->displays[i].fb.screen_base);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		if (fbdev->displays[i].fbdev) {
 | 
					 | 
				
			||||||
+			unregister_framebuffer(&fbdev->displays[i].fb);
 | 
					 | 
				
			||||||
+			bcm2708_fb_debugfs_deinit(&fbdev->displays[i]);
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	free_irq(fb->dma_irq, fb);
 | 
					 | 
				
			||||||
+	dma_free_wc(&dev->dev, SZ_64K, fbdev->cb_base,
 | 
					 | 
				
			||||||
+			      fbdev->cb_handle);
 | 
					 | 
				
			||||||
+	bcm_dma_chan_free(fbdev->dma_chan);
 | 
					 | 
				
			||||||
+	free_irq(fbdev->dma_irq, fbdev);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	kfree(fb);
 | 
					 | 
				
			||||||
+	mutex_destroy(&fbdev->dma_mutex);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	return 0;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
@@ -886,10 +1077,10 @@ static struct platform_driver bcm2708_fb
 | 
					 | 
				
			||||||
 	.probe = bcm2708_fb_probe,
 | 
					 | 
				
			||||||
 	.remove = bcm2708_fb_remove,
 | 
					 | 
				
			||||||
 	.driver = {
 | 
					 | 
				
			||||||
-		   .name = DRIVER_NAME,
 | 
					 | 
				
			||||||
-		   .owner = THIS_MODULE,
 | 
					 | 
				
			||||||
-		   .of_match_table = bcm2708_fb_of_match_table,
 | 
					 | 
				
			||||||
-		   },
 | 
					 | 
				
			||||||
+		  .name = DRIVER_NAME,
 | 
					 | 
				
			||||||
+		  .owner = THIS_MODULE,
 | 
					 | 
				
			||||||
+		  .of_match_table = bcm2708_fb_of_match_table,
 | 
					 | 
				
			||||||
+		  },
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static int __init bcm2708_fb_init(void)
 | 
					 | 
				
			||||||
--- a/include/soc/bcm2835/raspberrypi-firmware.h
 | 
					 | 
				
			||||||
+++ b/include/soc/bcm2835/raspberrypi-firmware.h
 | 
					 | 
				
			||||||
@@ -106,9 +106,15 @@ enum rpi_firmware_property_tag {
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_FRAMEBUFFER_GET_VIRTUAL_OFFSET =         0x00040009,
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_FRAMEBUFFER_GET_OVERSCAN =               0x0004000a,
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_FRAMEBUFFER_GET_PALETTE =                0x0004000b,
 | 
					 | 
				
			||||||
+	RPI_FIRMWARE_FRAMEBUFFER_GET_LAYER =                  0x0004000c,
 | 
					 | 
				
			||||||
+	RPI_FIRMWARE_FRAMEBUFFER_GET_TRANSFORM =              0x0004000d,
 | 
					 | 
				
			||||||
+	RPI_FIRMWARE_FRAMEBUFFER_GET_VSYNC =                  0x0004000e,
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_FRAMEBUFFER_GET_TOUCHBUF =               0x0004000f,
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_FRAMEBUFFER_GET_GPIOVIRTBUF =            0x00040010,
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_FRAMEBUFFER_RELEASE =                    0x00048001,
 | 
					 | 
				
			||||||
+	RPI_FIRMWARE_FRAMEBUFFER_SET_DISPLAY_NUM =            0x00048013,
 | 
					 | 
				
			||||||
+	RPI_FIRMWARE_FRAMEBUFFER_GET_NUM_DISPLAYS =           0x00040013,
 | 
					 | 
				
			||||||
+	RPI_FIRMWARE_FRAMEBUFFER_GET_DISPLAY_SETTINGS =       0x00040014,
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_FRAMEBUFFER_TEST_PHYSICAL_WIDTH_HEIGHT = 0x00044003,
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_FRAMEBUFFER_TEST_VIRTUAL_WIDTH_HEIGHT =  0x00044004,
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_FRAMEBUFFER_TEST_DEPTH =                 0x00044005,
 | 
					 | 
				
			||||||
@@ -117,6 +123,8 @@ enum rpi_firmware_property_tag {
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_FRAMEBUFFER_TEST_VIRTUAL_OFFSET =        0x00044009,
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_FRAMEBUFFER_TEST_OVERSCAN =              0x0004400a,
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_FRAMEBUFFER_TEST_PALETTE =               0x0004400b,
 | 
					 | 
				
			||||||
+	RPI_FIRMWARE_FRAMEBUFFER_TEST_LAYER =                 0x0004400c,
 | 
					 | 
				
			||||||
+	RPI_FIRMWARE_FRAMEBUFFER_TEST_TRANSFORM =             0x0004400d,
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_FRAMEBUFFER_TEST_VSYNC =                 0x0004400e,
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_FRAMEBUFFER_SET_PHYSICAL_WIDTH_HEIGHT =  0x00048003,
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_FRAMEBUFFER_SET_VIRTUAL_WIDTH_HEIGHT =   0x00048004,
 | 
					 | 
				
			||||||
@@ -127,9 +135,12 @@ enum rpi_firmware_property_tag {
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_FRAMEBUFFER_SET_VIRTUAL_OFFSET =         0x00048009,
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_FRAMEBUFFER_SET_OVERSCAN =               0x0004800a,
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_FRAMEBUFFER_SET_PALETTE =                0x0004800b,
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_FRAMEBUFFER_SET_TOUCHBUF =               0x0004801f,
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_FRAMEBUFFER_SET_GPIOVIRTBUF =            0x00048020,
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_FRAMEBUFFER_SET_VSYNC =                  0x0004800e,
 | 
					 | 
				
			||||||
+	RPI_FIRMWARE_FRAMEBUFFER_SET_LAYER =                  0x0004800c,
 | 
					 | 
				
			||||||
+	RPI_FIRMWARE_FRAMEBUFFER_SET_TRANSFORM =              0x0004800d,
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_FRAMEBUFFER_SET_BACKLIGHT =              0x0004800f,
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_VCHIQ_INIT =                             0x00048010,
 | 
					 | 
				
			||||||
@@ -138,6 +149,8 @@ enum rpi_firmware_property_tag {
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_GET_DMA_CHANNELS =                       0x00060001,
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+#define GET_DISPLAY_SETTINGS_PAYLOAD_SIZE 64
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 #if IS_ENABLED(CONFIG_RASPBERRYPI_FIRMWARE)
 | 
					 | 
				
			||||||
 int rpi_firmware_property(struct rpi_firmware *fw,
 | 
					 | 
				
			||||||
 			  u32 tag, void *data, size_t len);
 | 
					 | 
				
			||||||
@ -1,337 +0,0 @@
 | 
				
			|||||||
From 6005247285065235845f233077bbeedb19a484c7 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Siarhei Siamashka <siarhei.siamashka@gmail.com>
 | 
					 | 
				
			||||||
Date: Mon, 17 Jun 2013 13:32:11 +0300
 | 
					 | 
				
			||||||
Subject: [PATCH] fbdev: add FBIOCOPYAREA ioctl
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Based on the patch authored by Ali Gholami Rudi at
 | 
					 | 
				
			||||||
    https://lkml.org/lkml/2009/7/13/153
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Provide an ioctl for userspace applications, but only if this operation
 | 
					 | 
				
			||||||
is hardware accelerated (otherwide it does not make any sense).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Siarhei Siamashka <siarhei.siamashka@gmail.com>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bcm2708_fb: Add ioctl for reading gpu memory through dma
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
video: bcm2708_fb: Add compat_ioctl support.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
When using a 64 bit kernel with 32 bit userspace we need
 | 
					 | 
				
			||||||
compat ioctl handling for FBIODMACOPY as one of the
 | 
					 | 
				
			||||||
parameters is a pointer.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/video/fbdev/bcm2708_fb.c | 170 ++++++++++++++++++++++++++++++-
 | 
					 | 
				
			||||||
 drivers/video/fbdev/core/fbmem.c |  35 +++++++
 | 
					 | 
				
			||||||
 include/uapi/linux/fb.h          |  12 +++
 | 
					 | 
				
			||||||
 3 files changed, 213 insertions(+), 4 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/video/fbdev/bcm2708_fb.c
 | 
					 | 
				
			||||||
+++ b/drivers/video/fbdev/bcm2708_fb.c
 | 
					 | 
				
			||||||
@@ -32,8 +32,10 @@
 | 
					 | 
				
			||||||
 #include <linux/printk.h>
 | 
					 | 
				
			||||||
 #include <linux/console.h>
 | 
					 | 
				
			||||||
 #include <linux/debugfs.h>
 | 
					 | 
				
			||||||
+#include <linux/uaccess.h>
 | 
					 | 
				
			||||||
 #include <linux/io.h>
 | 
					 | 
				
			||||||
 #include <linux/dma-mapping.h>
 | 
					 | 
				
			||||||
+#include <linux/cred.h>
 | 
					 | 
				
			||||||
 #include <soc/bcm2835/raspberrypi-firmware.h>
 | 
					 | 
				
			||||||
 #include <linux/mutex.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -184,9 +186,6 @@ static int bcm2708_fb_debugfs_init(struc
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	fb->debugfs_subdir = debugfs_create_dir(buf, fb->debugfs_dir);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	debugfs_create_regset32("stats", 0444, fb->debugfs_dir,
 | 
					 | 
				
			||||||
-				&fb->stats.regset);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 	if (!fb->debugfs_subdir) {
 | 
					 | 
				
			||||||
 		dev_warn(fb->fb.dev, "%s: could not create debugfs entry %u\n",
 | 
					 | 
				
			||||||
 			 __func__, fb->display_settings.display_num);
 | 
					 | 
				
			||||||
@@ -603,7 +602,110 @@ static int bcm2708_fb_pan_display(struct
 | 
					 | 
				
			||||||
 	return result;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-static int bcm2708_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
 | 
					 | 
				
			||||||
+static void dma_memcpy(struct bcm2708_fb *fb, dma_addr_t dst, dma_addr_t src,
 | 
					 | 
				
			||||||
+		       int size)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct bcm2708_fb_dev *fbdev = fb->fbdev;
 | 
					 | 
				
			||||||
+	struct bcm2708_dma_cb *cb = fbdev->cb_base;
 | 
					 | 
				
			||||||
+	int burst_size = (fbdev->dma_chan == 0) ? 8 : 2;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	cb->info = BCM2708_DMA_BURST(burst_size) | BCM2708_DMA_S_WIDTH |
 | 
					 | 
				
			||||||
+		   BCM2708_DMA_S_INC | BCM2708_DMA_D_WIDTH |
 | 
					 | 
				
			||||||
+		   BCM2708_DMA_D_INC;
 | 
					 | 
				
			||||||
+	cb->dst = dst;
 | 
					 | 
				
			||||||
+	cb->src = src;
 | 
					 | 
				
			||||||
+	cb->length = size;
 | 
					 | 
				
			||||||
+	cb->stride = 0;
 | 
					 | 
				
			||||||
+	cb->pad[0] = 0;
 | 
					 | 
				
			||||||
+	cb->pad[1] = 0;
 | 
					 | 
				
			||||||
+	cb->next = 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	// Not sure what to do if this gets a signal whilst waiting
 | 
					 | 
				
			||||||
+	if (mutex_lock_interruptible(&fbdev->dma_mutex))
 | 
					 | 
				
			||||||
+		return;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (size < dma_busy_wait_threshold) {
 | 
					 | 
				
			||||||
+		bcm_dma_start(fbdev->dma_chan_base, fbdev->cb_handle);
 | 
					 | 
				
			||||||
+		bcm_dma_wait_idle(fbdev->dma_chan_base);
 | 
					 | 
				
			||||||
+	} else {
 | 
					 | 
				
			||||||
+		void __iomem *local_dma_chan = fbdev->dma_chan_base;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		cb->info |= BCM2708_DMA_INT_EN;
 | 
					 | 
				
			||||||
+		bcm_dma_start(fbdev->dma_chan_base, fbdev->cb_handle);
 | 
					 | 
				
			||||||
+		while (bcm_dma_is_busy(local_dma_chan)) {
 | 
					 | 
				
			||||||
+			wait_event_interruptible(fbdev->dma_waitq,
 | 
					 | 
				
			||||||
+						 !bcm_dma_is_busy(local_dma_chan));
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+		fbdev->dma_stats.dma_irqs++;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	fbdev->dma_stats.dma_copies++;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	mutex_unlock(&fbdev->dma_mutex);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* address with no aliases */
 | 
					 | 
				
			||||||
+#define INTALIAS_NORMAL(x) ((x) & ~0xc0000000)
 | 
					 | 
				
			||||||
+/* cache coherent but non-allocating in L1 and L2 */
 | 
					 | 
				
			||||||
+#define INTALIAS_L1L2_NONALLOCATING(x) (((x) & ~0xc0000000) | 0x80000000)
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static long vc_mem_copy(struct bcm2708_fb *fb, struct fb_dmacopy *ioparam)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	size_t size = PAGE_SIZE;
 | 
					 | 
				
			||||||
+	u32 *buf = NULL;
 | 
					 | 
				
			||||||
+	dma_addr_t bus_addr;
 | 
					 | 
				
			||||||
+	long rc = 0;
 | 
					 | 
				
			||||||
+	size_t offset;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* restrict this to root user */
 | 
					 | 
				
			||||||
+	if (!uid_eq(current_euid(), GLOBAL_ROOT_UID)) {
 | 
					 | 
				
			||||||
+		rc = -EFAULT;
 | 
					 | 
				
			||||||
+		goto out;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (!fb->gpu.base || !fb->gpu.length) {
 | 
					 | 
				
			||||||
+		pr_err("[%s]: Unable to determine gpu memory (%x,%x)\n",
 | 
					 | 
				
			||||||
+		       __func__, fb->gpu.base, fb->gpu.length);
 | 
					 | 
				
			||||||
+		return -EFAULT;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (INTALIAS_NORMAL(ioparam->src) < fb->gpu.base ||
 | 
					 | 
				
			||||||
+	    INTALIAS_NORMAL(ioparam->src) >= fb->gpu.base + fb->gpu.length) {
 | 
					 | 
				
			||||||
+		pr_err("[%s]: Invalid memory access %x (%x-%x)", __func__,
 | 
					 | 
				
			||||||
+		       INTALIAS_NORMAL(ioparam->src), fb->gpu.base,
 | 
					 | 
				
			||||||
+		       fb->gpu.base + fb->gpu.length);
 | 
					 | 
				
			||||||
+		return -EFAULT;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	buf = dma_alloc_coherent(fb->fb.device, PAGE_ALIGN(size), &bus_addr,
 | 
					 | 
				
			||||||
+				 GFP_ATOMIC);
 | 
					 | 
				
			||||||
+	if (!buf) {
 | 
					 | 
				
			||||||
+		pr_err("[%s]: failed to dma_alloc_coherent(%zd)\n", __func__,
 | 
					 | 
				
			||||||
+		       size);
 | 
					 | 
				
			||||||
+		rc = -ENOMEM;
 | 
					 | 
				
			||||||
+		goto out;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	for (offset = 0; offset < ioparam->length; offset += size) {
 | 
					 | 
				
			||||||
+		size_t remaining = ioparam->length - offset;
 | 
					 | 
				
			||||||
+		size_t s = min(size, remaining);
 | 
					 | 
				
			||||||
+		u8 *p = (u8 *)((uintptr_t)ioparam->src + offset);
 | 
					 | 
				
			||||||
+		u8 *q = (u8 *)ioparam->dst + offset;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		dma_memcpy(fb, bus_addr,
 | 
					 | 
				
			||||||
+			   INTALIAS_L1L2_NONALLOCATING((dma_addr_t)p), size);
 | 
					 | 
				
			||||||
+		if (copy_to_user(q, buf, s) != 0) {
 | 
					 | 
				
			||||||
+			pr_err("[%s]: failed to copy-to-user\n", __func__);
 | 
					 | 
				
			||||||
+			rc = -EFAULT;
 | 
					 | 
				
			||||||
+			goto out;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+out:
 | 
					 | 
				
			||||||
+	if (buf)
 | 
					 | 
				
			||||||
+		dma_free_coherent(fb->fb.device, PAGE_ALIGN(size), buf,
 | 
					 | 
				
			||||||
+				  bus_addr);
 | 
					 | 
				
			||||||
+	return rc;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static int bcm2708_ioctl(struct fb_info *info, unsigned int cmd,
 | 
					 | 
				
			||||||
 			 unsigned long arg)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
@@ -619,6 +721,21 @@ static int bcm2708_ioctl(struct fb_info
 | 
					 | 
				
			||||||
 					    RPI_FIRMWARE_FRAMEBUFFER_SET_VSYNC,
 | 
					 | 
				
			||||||
 					    &dummy, sizeof(dummy));
 | 
					 | 
				
			||||||
 		break;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	case FBIODMACOPY:
 | 
					 | 
				
			||||||
+	{
 | 
					 | 
				
			||||||
+		struct fb_dmacopy ioparam;
 | 
					 | 
				
			||||||
+		/* Get the parameter data.
 | 
					 | 
				
			||||||
+		 */
 | 
					 | 
				
			||||||
+		if (copy_from_user
 | 
					 | 
				
			||||||
+		    (&ioparam, (void *)arg, sizeof(ioparam))) {
 | 
					 | 
				
			||||||
+			pr_err("[%s]: failed to copy-from-user\n", __func__);
 | 
					 | 
				
			||||||
+			ret = -EFAULT;
 | 
					 | 
				
			||||||
+			break;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+		ret = vc_mem_copy(fb, &ioparam);
 | 
					 | 
				
			||||||
+		break;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
 	default:
 | 
					 | 
				
			||||||
 		dev_dbg(info->device, "Unknown ioctl 0x%x\n", cmd);
 | 
					 | 
				
			||||||
 		return -ENOTTY;
 | 
					 | 
				
			||||||
@@ -629,6 +746,48 @@ static int bcm2708_ioctl(struct fb_info
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	return ret;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#ifdef CONFIG_COMPAT
 | 
					 | 
				
			||||||
+struct fb_dmacopy32 {
 | 
					 | 
				
			||||||
+	compat_uptr_t dst;
 | 
					 | 
				
			||||||
+	__u32 src;
 | 
					 | 
				
			||||||
+	__u32 length;
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define FBIODMACOPY32		_IOW('z', 0x22, struct fb_dmacopy32)
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int bcm2708_compat_ioctl(struct fb_info *info, unsigned int cmd,
 | 
					 | 
				
			||||||
+				unsigned long arg)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct bcm2708_fb *fb = to_bcm2708(info);
 | 
					 | 
				
			||||||
+	int ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	switch (cmd) {
 | 
					 | 
				
			||||||
+	case FBIODMACOPY32:
 | 
					 | 
				
			||||||
+	{
 | 
					 | 
				
			||||||
+		struct fb_dmacopy32 param32;
 | 
					 | 
				
			||||||
+		struct fb_dmacopy param;
 | 
					 | 
				
			||||||
+		/* Get the parameter data.
 | 
					 | 
				
			||||||
+		 */
 | 
					 | 
				
			||||||
+		if (copy_from_user(¶m32, (void *)arg, sizeof(param32))) {
 | 
					 | 
				
			||||||
+			pr_err("[%s]: failed to copy-from-user\n", __func__);
 | 
					 | 
				
			||||||
+			ret = -EFAULT;
 | 
					 | 
				
			||||||
+			break;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+		param.dst = compat_ptr(param32.dst);
 | 
					 | 
				
			||||||
+		param.src = param32.src;
 | 
					 | 
				
			||||||
+		param.length = param32.length;
 | 
					 | 
				
			||||||
+		ret = vc_mem_copy(fb, ¶m);
 | 
					 | 
				
			||||||
+		break;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	default:
 | 
					 | 
				
			||||||
+		ret = bcm2708_ioctl(info, cmd, arg);
 | 
					 | 
				
			||||||
+		break;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	return ret;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static void bcm2708_fb_fillrect(struct fb_info *info,
 | 
					 | 
				
			||||||
 				const struct fb_fillrect *rect)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
@@ -821,6 +980,9 @@ static struct fb_ops bcm2708_fb_ops = {
 | 
					 | 
				
			||||||
 	.fb_imageblit = bcm2708_fb_imageblit,
 | 
					 | 
				
			||||||
 	.fb_pan_display = bcm2708_fb_pan_display,
 | 
					 | 
				
			||||||
 	.fb_ioctl = bcm2708_ioctl,
 | 
					 | 
				
			||||||
+#ifdef CONFIG_COMPAT
 | 
					 | 
				
			||||||
+	.fb_compat_ioctl = bcm2708_compat_ioctl,
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static int bcm2708_fb_register(struct bcm2708_fb *fb)
 | 
					 | 
				
			||||||
--- a/drivers/video/fbdev/core/fbmem.c
 | 
					 | 
				
			||||||
+++ b/drivers/video/fbdev/core/fbmem.c
 | 
					 | 
				
			||||||
@@ -1085,6 +1085,30 @@ fb_blank(struct fb_info *info, int blank
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 EXPORT_SYMBOL(fb_blank);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static int fb_copyarea_user(struct fb_info *info,
 | 
					 | 
				
			||||||
+			    struct fb_copyarea *copy)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int ret = 0;
 | 
					 | 
				
			||||||
+	lock_fb_info(info);
 | 
					 | 
				
			||||||
+	if (copy->dx >= info->var.xres ||
 | 
					 | 
				
			||||||
+	    copy->sx >= info->var.xres ||
 | 
					 | 
				
			||||||
+	    copy->width > info->var.xres ||
 | 
					 | 
				
			||||||
+	    copy->dy >= info->var.yres ||
 | 
					 | 
				
			||||||
+	    copy->sy >= info->var.yres ||
 | 
					 | 
				
			||||||
+	    copy->height > info->var.yres ||
 | 
					 | 
				
			||||||
+	    copy->dx + copy->width > info->var.xres ||
 | 
					 | 
				
			||||||
+	    copy->sx + copy->width > info->var.xres ||
 | 
					 | 
				
			||||||
+	    copy->dy + copy->height > info->var.yres ||
 | 
					 | 
				
			||||||
+	    copy->sy + copy->height > info->var.yres) {
 | 
					 | 
				
			||||||
+		ret = -EINVAL;
 | 
					 | 
				
			||||||
+		goto out;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	info->fbops->fb_copyarea(info, copy);
 | 
					 | 
				
			||||||
+out:
 | 
					 | 
				
			||||||
+	unlock_fb_info(info);
 | 
					 | 
				
			||||||
+	return ret;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
 | 
					 | 
				
			||||||
 			unsigned long arg)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
@@ -1093,6 +1117,7 @@ static long do_fb_ioctl(struct fb_info *
 | 
					 | 
				
			||||||
 	struct fb_fix_screeninfo fix;
 | 
					 | 
				
			||||||
 	struct fb_cmap cmap_from;
 | 
					 | 
				
			||||||
 	struct fb_cmap_user cmap;
 | 
					 | 
				
			||||||
+	struct fb_copyarea copy;
 | 
					 | 
				
			||||||
 	void __user *argp = (void __user *)arg;
 | 
					 | 
				
			||||||
 	long ret = 0;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -1168,6 +1193,15 @@ static long do_fb_ioctl(struct fb_info *
 | 
					 | 
				
			||||||
 		unlock_fb_info(info);
 | 
					 | 
				
			||||||
 		console_unlock();
 | 
					 | 
				
			||||||
 		break;
 | 
					 | 
				
			||||||
+	case FBIOCOPYAREA:
 | 
					 | 
				
			||||||
+		if (info->flags & FBINFO_HWACCEL_COPYAREA) {
 | 
					 | 
				
			||||||
+			/* only provide this ioctl if it is accelerated */
 | 
					 | 
				
			||||||
+			if (copy_from_user(©, argp, sizeof(copy)))
 | 
					 | 
				
			||||||
+				return -EFAULT;
 | 
					 | 
				
			||||||
+			ret = fb_copyarea_user(info, ©);
 | 
					 | 
				
			||||||
+			break;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+		/* fall through */
 | 
					 | 
				
			||||||
 	default:
 | 
					 | 
				
			||||||
 		lock_fb_info(info);
 | 
					 | 
				
			||||||
 		fb = info->fbops;
 | 
					 | 
				
			||||||
@@ -1313,6 +1347,7 @@ static long fb_compat_ioctl(struct file
 | 
					 | 
				
			||||||
 	case FBIOPAN_DISPLAY:
 | 
					 | 
				
			||||||
 	case FBIOGET_CON2FBMAP:
 | 
					 | 
				
			||||||
 	case FBIOPUT_CON2FBMAP:
 | 
					 | 
				
			||||||
+	case FBIOCOPYAREA:
 | 
					 | 
				
			||||||
 		arg = (unsigned long) compat_ptr(arg);
 | 
					 | 
				
			||||||
 		fallthrough;
 | 
					 | 
				
			||||||
 	case FBIOBLANK:
 | 
					 | 
				
			||||||
--- a/include/uapi/linux/fb.h
 | 
					 | 
				
			||||||
+++ b/include/uapi/linux/fb.h
 | 
					 | 
				
			||||||
@@ -35,6 +35,12 @@
 | 
					 | 
				
			||||||
 #define FBIOPUT_MODEINFO        0x4617
 | 
					 | 
				
			||||||
 #define FBIOGET_DISPINFO        0x4618
 | 
					 | 
				
			||||||
 #define FBIO_WAITFORVSYNC	_IOW('F', 0x20, __u32)
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ * HACK: use 'z' in order not to clash with any other ioctl numbers which might
 | 
					 | 
				
			||||||
+ * be concurrently added to the mainline kernel
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+#define FBIOCOPYAREA		_IOW('z', 0x21, struct fb_copyarea)
 | 
					 | 
				
			||||||
+#define FBIODMACOPY 		_IOW('z', 0x22, struct fb_dmacopy)
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #define FB_TYPE_PACKED_PIXELS		0	/* Packed Pixels	*/
 | 
					 | 
				
			||||||
 #define FB_TYPE_PLANES			1	/* Non interleaved planes */
 | 
					 | 
				
			||||||
@@ -348,6 +354,12 @@ struct fb_copyarea {
 | 
					 | 
				
			||||||
 	__u32 sy;
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+struct fb_dmacopy {
 | 
					 | 
				
			||||||
+	void *dst;
 | 
					 | 
				
			||||||
+	__u32 src;
 | 
					 | 
				
			||||||
+	__u32 length;
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 struct fb_fillrect {
 | 
					 | 
				
			||||||
 	__u32 dx;	/* screen-relative */
 | 
					 | 
				
			||||||
 	__u32 dy;
 | 
					 | 
				
			||||||
@ -1,209 +0,0 @@
 | 
				
			|||||||
From cac14f80f1e41f9a977c3cd84b0b3b73740818f8 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Harm Hanemaaijer <fgenfb@yahoo.com>
 | 
					 | 
				
			||||||
Date: Thu, 20 Jun 2013 20:21:39 +0200
 | 
					 | 
				
			||||||
Subject: [PATCH] Speed up console framebuffer imageblit function
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Especially on platforms with a slower CPU but a relatively high
 | 
					 | 
				
			||||||
framebuffer fill bandwidth, like current ARM devices, the existing
 | 
					 | 
				
			||||||
console monochrome imageblit function used to draw console text is
 | 
					 | 
				
			||||||
suboptimal for common pixel depths such as 16bpp and 32bpp. The existing
 | 
					 | 
				
			||||||
code is quite general and can deal with several pixel depths. By creating
 | 
					 | 
				
			||||||
special case functions for 16bpp and 32bpp, by far the most common pixel
 | 
					 | 
				
			||||||
formats used on modern systems, a significant speed-up is attained
 | 
					 | 
				
			||||||
which can be readily felt on ARM-based devices like the Raspberry Pi
 | 
					 | 
				
			||||||
and the Allwinner platform, but should help any platform using the
 | 
					 | 
				
			||||||
fb layer.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The special case functions allow constant folding, eliminating a number
 | 
					 | 
				
			||||||
of instructions including divide operations, and allow the use of an
 | 
					 | 
				
			||||||
unrolled loop, eliminating instructions with a variable shift size,
 | 
					 | 
				
			||||||
reducing source memory access instructions, and eliminating excessive
 | 
					 | 
				
			||||||
branching. These unrolled loops also allow much better code optimization
 | 
					 | 
				
			||||||
by the C compiler. The code that selects which optimized variant is used
 | 
					 | 
				
			||||||
is also simplified, eliminating integer divide instructions.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The speed-up, measured by timing 'cat file.txt' in the console, varies
 | 
					 | 
				
			||||||
between 40% and 70%, when testing on the Raspberry Pi and Allwinner
 | 
					 | 
				
			||||||
ARM-based platforms, depending on font size and the pixel depth, with
 | 
					 | 
				
			||||||
the greater benefit for 32bpp.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Harm Hanemaaijer <fgenfb@yahoo.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/video/fbdev/core/cfbimgblt.c | 152 ++++++++++++++++++++++++++-
 | 
					 | 
				
			||||||
 1 file changed, 147 insertions(+), 5 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/video/fbdev/core/cfbimgblt.c
 | 
					 | 
				
			||||||
+++ b/drivers/video/fbdev/core/cfbimgblt.c
 | 
					 | 
				
			||||||
@@ -28,6 +28,11 @@
 | 
					 | 
				
			||||||
  *
 | 
					 | 
				
			||||||
  *  Also need to add code to deal with cards endians that are different than
 | 
					 | 
				
			||||||
  *  the native cpu endians. I also need to deal with MSB position in the word.
 | 
					 | 
				
			||||||
+ *  Modified by Harm Hanemaaijer (fgenfb@yahoo.com) 2013:
 | 
					 | 
				
			||||||
+ *  - Provide optimized versions of fast_imageblit for 16 and 32bpp that are
 | 
					 | 
				
			||||||
+ *    significantly faster than the previous implementation.
 | 
					 | 
				
			||||||
+ *  - Simplify the fast/slow_imageblit selection code, avoiding integer
 | 
					 | 
				
			||||||
+ *    divides.
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
 #include <linux/module.h>
 | 
					 | 
				
			||||||
 #include <linux/string.h>
 | 
					 | 
				
			||||||
@@ -262,6 +267,133 @@ static inline void fast_imageblit(const
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 }	
 | 
					 | 
				
			||||||
 	
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ * Optimized fast_imageblit for bpp == 16. ppw = 2, bit_mask = 3 folded
 | 
					 | 
				
			||||||
+ * into the code, main loop unrolled.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static inline void fast_imageblit16(const struct fb_image *image,
 | 
					 | 
				
			||||||
+				    struct fb_info *p, u8 __iomem * dst1,
 | 
					 | 
				
			||||||
+				    u32 fgcolor, u32 bgcolor)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	u32 fgx = fgcolor, bgx = bgcolor;
 | 
					 | 
				
			||||||
+	u32 spitch = (image->width + 7) / 8;
 | 
					 | 
				
			||||||
+	u32 end_mask, eorx;
 | 
					 | 
				
			||||||
+	const char *s = image->data, *src;
 | 
					 | 
				
			||||||
+	u32 __iomem *dst;
 | 
					 | 
				
			||||||
+	const u32 *tab = NULL;
 | 
					 | 
				
			||||||
+	int i, j, k;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	tab = fb_be_math(p) ? cfb_tab16_be : cfb_tab16_le;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	fgx <<= 16;
 | 
					 | 
				
			||||||
+	bgx <<= 16;
 | 
					 | 
				
			||||||
+	fgx |= fgcolor;
 | 
					 | 
				
			||||||
+	bgx |= bgcolor;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	eorx = fgx ^ bgx;
 | 
					 | 
				
			||||||
+	k = image->width / 2;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	for (i = image->height; i--;) {
 | 
					 | 
				
			||||||
+		dst = (u32 __iomem *) dst1;
 | 
					 | 
				
			||||||
+		src = s;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		j = k;
 | 
					 | 
				
			||||||
+		while (j >= 4) {
 | 
					 | 
				
			||||||
+			u8 bits = *src;
 | 
					 | 
				
			||||||
+			end_mask = tab[(bits >> 6) & 3];
 | 
					 | 
				
			||||||
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
 | 
					 | 
				
			||||||
+			end_mask = tab[(bits >> 4) & 3];
 | 
					 | 
				
			||||||
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
 | 
					 | 
				
			||||||
+			end_mask = tab[(bits >> 2) & 3];
 | 
					 | 
				
			||||||
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
 | 
					 | 
				
			||||||
+			end_mask = tab[bits & 3];
 | 
					 | 
				
			||||||
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
 | 
					 | 
				
			||||||
+			src++;
 | 
					 | 
				
			||||||
+			j -= 4;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+		if (j != 0) {
 | 
					 | 
				
			||||||
+			u8 bits = *src;
 | 
					 | 
				
			||||||
+			end_mask = tab[(bits >> 6) & 3];
 | 
					 | 
				
			||||||
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
 | 
					 | 
				
			||||||
+			if (j >= 2) {
 | 
					 | 
				
			||||||
+				end_mask = tab[(bits >> 4) & 3];
 | 
					 | 
				
			||||||
+				FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
 | 
					 | 
				
			||||||
+				if (j == 3) {
 | 
					 | 
				
			||||||
+					end_mask = tab[(bits >> 2) & 3];
 | 
					 | 
				
			||||||
+					FB_WRITEL((end_mask & eorx) ^ bgx, dst);
 | 
					 | 
				
			||||||
+				}
 | 
					 | 
				
			||||||
+			}
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+		dst1 += p->fix.line_length;
 | 
					 | 
				
			||||||
+		s += spitch;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ * Optimized fast_imageblit for bpp == 32. ppw = 1, bit_mask = 1 folded
 | 
					 | 
				
			||||||
+ * into the code, main loop unrolled.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static inline void fast_imageblit32(const struct fb_image *image,
 | 
					 | 
				
			||||||
+				    struct fb_info *p, u8 __iomem * dst1,
 | 
					 | 
				
			||||||
+				    u32 fgcolor, u32 bgcolor)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	u32 fgx = fgcolor, bgx = bgcolor;
 | 
					 | 
				
			||||||
+	u32 spitch = (image->width + 7) / 8;
 | 
					 | 
				
			||||||
+	u32 end_mask, eorx;
 | 
					 | 
				
			||||||
+	const char *s = image->data, *src;
 | 
					 | 
				
			||||||
+	u32 __iomem *dst;
 | 
					 | 
				
			||||||
+	const u32 *tab = NULL;
 | 
					 | 
				
			||||||
+	int i, j, k;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	tab = cfb_tab32;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	eorx = fgx ^ bgx;
 | 
					 | 
				
			||||||
+	k = image->width;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	for (i = image->height; i--;) {
 | 
					 | 
				
			||||||
+		dst = (u32 __iomem *) dst1;
 | 
					 | 
				
			||||||
+		src = s;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		j = k;
 | 
					 | 
				
			||||||
+		while (j >= 8) {
 | 
					 | 
				
			||||||
+			u8 bits = *src;
 | 
					 | 
				
			||||||
+			end_mask = tab[(bits >> 7) & 1];
 | 
					 | 
				
			||||||
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
 | 
					 | 
				
			||||||
+			end_mask = tab[(bits >> 6) & 1];
 | 
					 | 
				
			||||||
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
 | 
					 | 
				
			||||||
+			end_mask = tab[(bits >> 5) & 1];
 | 
					 | 
				
			||||||
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
 | 
					 | 
				
			||||||
+			end_mask = tab[(bits >> 4) & 1];
 | 
					 | 
				
			||||||
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
 | 
					 | 
				
			||||||
+			end_mask = tab[(bits >> 3) & 1];
 | 
					 | 
				
			||||||
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
 | 
					 | 
				
			||||||
+			end_mask = tab[(bits >> 2) & 1];
 | 
					 | 
				
			||||||
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
 | 
					 | 
				
			||||||
+			end_mask = tab[(bits >> 1) & 1];
 | 
					 | 
				
			||||||
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
 | 
					 | 
				
			||||||
+			end_mask = tab[bits & 1];
 | 
					 | 
				
			||||||
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
 | 
					 | 
				
			||||||
+			src++;
 | 
					 | 
				
			||||||
+			j -= 8;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+		if (j != 0) {
 | 
					 | 
				
			||||||
+			u32 bits = (u32) * src;
 | 
					 | 
				
			||||||
+			while (j > 1) {
 | 
					 | 
				
			||||||
+				end_mask = tab[(bits >> 7) & 1];
 | 
					 | 
				
			||||||
+				FB_WRITEL((end_mask & eorx) ^ bgx, dst++);
 | 
					 | 
				
			||||||
+				bits <<= 1;
 | 
					 | 
				
			||||||
+				j--;
 | 
					 | 
				
			||||||
+			}
 | 
					 | 
				
			||||||
+			end_mask = tab[(bits >> 7) & 1];
 | 
					 | 
				
			||||||
+			FB_WRITEL((end_mask & eorx) ^ bgx, dst);
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+		dst1 += p->fix.line_length;
 | 
					 | 
				
			||||||
+		s += spitch;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 void cfb_imageblit(struct fb_info *p, const struct fb_image *image)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0;
 | 
					 | 
				
			||||||
@@ -294,11 +426,21 @@ void cfb_imageblit(struct fb_info *p, co
 | 
					 | 
				
			||||||
 			bgcolor = image->bg_color;
 | 
					 | 
				
			||||||
 		}	
 | 
					 | 
				
			||||||
 		
 | 
					 | 
				
			||||||
-		if (32 % bpp == 0 && !start_index && !pitch_index && 
 | 
					 | 
				
			||||||
-		    ((width & (32/bpp-1)) == 0) &&
 | 
					 | 
				
			||||||
-		    bpp >= 8 && bpp <= 32) 			
 | 
					 | 
				
			||||||
-			fast_imageblit(image, p, dst1, fgcolor, bgcolor);
 | 
					 | 
				
			||||||
-		else 
 | 
					 | 
				
			||||||
+		if (!start_index && !pitch_index) {
 | 
					 | 
				
			||||||
+			if (bpp == 32)
 | 
					 | 
				
			||||||
+				fast_imageblit32(image, p, dst1, fgcolor,
 | 
					 | 
				
			||||||
+						 bgcolor);
 | 
					 | 
				
			||||||
+			else if (bpp == 16 && (width & 1) == 0)
 | 
					 | 
				
			||||||
+				fast_imageblit16(image, p, dst1, fgcolor,
 | 
					 | 
				
			||||||
+						 bgcolor);
 | 
					 | 
				
			||||||
+			else if (bpp == 8 && (width & 3) == 0)
 | 
					 | 
				
			||||||
+				fast_imageblit(image, p, dst1, fgcolor,
 | 
					 | 
				
			||||||
+					       bgcolor);
 | 
					 | 
				
			||||||
+			else
 | 
					 | 
				
			||||||
+				slow_imageblit(image, p, dst1, fgcolor,
 | 
					 | 
				
			||||||
+					       bgcolor,
 | 
					 | 
				
			||||||
+					       start_index, pitch_index);
 | 
					 | 
				
			||||||
+		} else
 | 
					 | 
				
			||||||
 			slow_imageblit(image, p, dst1, fgcolor, bgcolor,
 | 
					 | 
				
			||||||
 					start_index, pitch_index);
 | 
					 | 
				
			||||||
 	} else
 | 
					 | 
				
			||||||
@ -1,640 +0,0 @@
 | 
				
			|||||||
From 9ac0f4ce3beaad7700af1e771776d12ae7813f97 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Florian Meier <florian.meier@koalo.de>
 | 
					 | 
				
			||||||
Date: Fri, 22 Nov 2013 14:22:53 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] dmaengine: Add support for BCM2708
 | 
					 | 
				
			||||||
MIME-Version: 1.0
 | 
					 | 
				
			||||||
Content-Type: text/plain; charset=UTF-8
 | 
					 | 
				
			||||||
Content-Transfer-Encoding: 8bit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add support for DMA controller of BCM2708 as used in the Raspberry Pi.
 | 
					 | 
				
			||||||
Currently it only supports cyclic DMA.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Florian Meier <florian.meier@koalo.de>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
dmaengine: expand functionality by supporting scatter/gather transfers sdhci-bcm2708 and dma.c: fix for LITE channels
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DMA: fix cyclic LITE length overflow bug
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
dmaengine: bcm2708: Remove chancnt affectations
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Mirror bcm2835-dma.c commit 9eba5536a7434c69d8c185d4bd1c70734d92287d:
 | 
					 | 
				
			||||||
chancnt is already filled by dma_async_device_register, which uses the channel
 | 
					 | 
				
			||||||
list to know how much channels there is.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Since it's already filled, we can safely remove it from the drivers' probe
 | 
					 | 
				
			||||||
function.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
dmaengine: bcm2708: overwrite dreq only if it is not set
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
dreq is set when the DMA channel is fetched from Device Tree.
 | 
					 | 
				
			||||||
slave_id is set using dmaengine_slave_config().
 | 
					 | 
				
			||||||
Only overwrite dreq with slave_id if it is not set.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
dreq/slave_id in the cyclic DMA case is not touched, because I don't
 | 
					 | 
				
			||||||
have hardware to test with.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
dmaengine: bcm2708: do device registration in the board file
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Don't register the device in the driver. Do it in the board file.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
dmaengine: bcm2708: don't restrict DT support to ARCH_BCM2835
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Both ARCH_BCM2835 and ARCH_BCM270x are built with OF now.
 | 
					 | 
				
			||||||
Add Device Tree support to the non ARCH_BCM2835 case.
 | 
					 | 
				
			||||||
Use the same driver name regardless of architecture.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
BCM270x_DT: add bcm2835-dma entry
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add Device Tree entry for bcm2835-dma.
 | 
					 | 
				
			||||||
The entry doesn't contain any resources since they are handled
 | 
					 | 
				
			||||||
by the arch/arm/mach-bcm270x/dma.c driver.
 | 
					 | 
				
			||||||
In non-DT mode, don't add the device in the board file.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bcm2708-dmaengine: Add debug options
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
BCM270x: Add memory and irq resources to dmaengine device and DT
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Prepare for merging of the legacy DMA API arch driver dma.c
 | 
					 | 
				
			||||||
with bcm2708-dmaengine by adding memory and irq resources both
 | 
					 | 
				
			||||||
to platform file device and Device Tree node.
 | 
					 | 
				
			||||||
Don't use BCM_DMAMAN_DRIVER_NAME so we don't have to include mach/dma.h
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
dmaengine: bcm2708: Merge with arch dma.c driver and disable dma.c
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Merge the legacy DMA API driver with bcm2708-dmaengine.
 | 
					 | 
				
			||||||
This is done so we can use bcm2708_fb on ARCH_BCM2835 (mailbox
 | 
					 | 
				
			||||||
driver is also needed).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Changes to the dma.c code:
 | 
					 | 
				
			||||||
- Use BIT() macro.
 | 
					 | 
				
			||||||
- Cutdown some comments to one line.
 | 
					 | 
				
			||||||
- Add mutex to vc_dmaman and use this, since the dev lock is locked
 | 
					 | 
				
			||||||
  during probing of the engine part.
 | 
					 | 
				
			||||||
- Add global g_dmaman variable since drvdata is used by the engine part.
 | 
					 | 
				
			||||||
- Restructure for readability:
 | 
					 | 
				
			||||||
  vc_dmaman_chan_alloc()
 | 
					 | 
				
			||||||
  vc_dmaman_chan_free()
 | 
					 | 
				
			||||||
  bcm_dma_chan_free()
 | 
					 | 
				
			||||||
- Restructure bcm_dma_chan_alloc() to simplify error handling.
 | 
					 | 
				
			||||||
- Use device irq resources instead of hardcoded bcm_dma_irqs table.
 | 
					 | 
				
			||||||
- Remove dev_dmaman_register() and code it directly.
 | 
					 | 
				
			||||||
- Remove dev_dmaman_deregister() and code it directly.
 | 
					 | 
				
			||||||
- Simplify bcm_dmaman_probe() using devm_* functions.
 | 
					 | 
				
			||||||
- Get dmachans from DT if available.
 | 
					 | 
				
			||||||
- Keep 'dma.dmachans' module argument name for backwards compatibility.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Make it available on ARCH_BCM2835 as well.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
dmaengine: bcm2708: set residue_granularity field
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bcm2708-dmaengine supports residue reporting at burst level
 | 
					 | 
				
			||||||
but didn't report this via the residue_granularity field.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Without this field set properly we get playback issues with I2S cards.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
dmaengine: bcm2708-dmaengine: Fix memory leak when stopping a running transfer
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bcm2708-dmaengine: Use more DMA channels (but not 12)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
1) Only the bcm2708_fb drivers uses the legacy DMA API, and
 | 
					 | 
				
			||||||
it requires a BULK-capable channel, so all other types
 | 
					 | 
				
			||||||
(FAST, NORMAL and LITE) can be made available to the regular
 | 
					 | 
				
			||||||
DMA API.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
2) DMA channels 11-14 share an interrupt. The driver can't
 | 
					 | 
				
			||||||
handle this, so don't use channels 12-14 (12 was used, probably
 | 
					 | 
				
			||||||
because it appears to have an interrupt, but in reality that
 | 
					 | 
				
			||||||
interrupt is for activity on ANY channel). This may explain
 | 
					 | 
				
			||||||
a lockup encountered when running out of DMA channels.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The combined effect of this patch is to leave 7 DMA channels
 | 
					 | 
				
			||||||
available + channel 0 for bcm2708_fb via the legacy API.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
See: https://github.com/raspberrypi/linux/issues/1110
 | 
					 | 
				
			||||||
     https://github.com/raspberrypi/linux/issues/1108
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
dmaengine: bcm2708: Make legacy API available for bcm2835-dma
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bcm2708_fb uses the legacy DMA API, so in order to start using
 | 
					 | 
				
			||||||
bcm2835-dma, bcm2835-dma has to support the legacy API. Make this
 | 
					 | 
				
			||||||
possible by exporting bcm_dmaman_probe() and bcm_dmaman_remove().
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
dmaengine: bcm2708: Change DT compatible string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Both bcm2835-dma and bcm2708-dmaengine have the same compatible string.
 | 
					 | 
				
			||||||
So change compatible to "brcm,bcm2708-dma".
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
dmaengine: bcm2708: Remove driver but keep legacy API
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Dropping non-DT support means we don't need this driver,
 | 
					 | 
				
			||||||
but we still need the legacy DMA API.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bcm2708-dmaengine - Fix arm64 portability/build issues
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
dma-bcm2708: Fix module compilation of CONFIG_DMA_BCM2708
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bcm2708-dmaengine.c defines functions like bcm_dma_start which are
 | 
					 | 
				
			||||||
defined as well in dma-bcm2708.h as inline versions when
 | 
					 | 
				
			||||||
CONFIG_DMA_BCM2708 is not defined. This works fine when
 | 
					 | 
				
			||||||
CONFIG_DMA_BCM2708 is built in, but when it is selected as module build
 | 
					 | 
				
			||||||
fails with redefinition errors because in the build system when
 | 
					 | 
				
			||||||
CONFIG_DMA_BCM2708 is selected as module, the macro becomes
 | 
					 | 
				
			||||||
CONFIG_DMA_BCM2708_MODULE.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This patch makes the header use CONFIG_DMA_BCM2708_MODULE too when
 | 
					 | 
				
			||||||
available.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Fixes https://github.com/raspberrypi/linux/issues/2056
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Andrei Gherzan <andrei@gherzan.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/dma/Kconfig                       |   6 +-
 | 
					 | 
				
			||||||
 drivers/dma/Makefile                      |   1 +
 | 
					 | 
				
			||||||
 drivers/dma/bcm2708-dmaengine.c           | 281 ++++++++++++++++++++++
 | 
					 | 
				
			||||||
 include/linux/platform_data/dma-bcm2708.h | 143 +++++++++++
 | 
					 | 
				
			||||||
 4 files changed, 430 insertions(+), 1 deletion(-)
 | 
					 | 
				
			||||||
 create mode 100644 drivers/dma/bcm2708-dmaengine.c
 | 
					 | 
				
			||||||
 create mode 100644 include/linux/platform_data/dma-bcm2708.h
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/dma/Kconfig
 | 
					 | 
				
			||||||
+++ b/drivers/dma/Kconfig
 | 
					 | 
				
			||||||
@@ -134,7 +134,7 @@ config COH901318
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 config DMA_BCM2835
 | 
					 | 
				
			||||||
 	tristate "BCM2835 DMA engine support"
 | 
					 | 
				
			||||||
-	depends on ARCH_BCM2835 || ARCH_BCM2708 || ARCH_BCM2709
 | 
					 | 
				
			||||||
+	depends on ARCH_BCM2835
 | 
					 | 
				
			||||||
 	select DMA_ENGINE
 | 
					 | 
				
			||||||
 	select DMA_VIRTUAL_CHANNELS
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -652,6 +652,10 @@ config UNIPHIER_XDMAC
 | 
					 | 
				
			||||||
 	  UniPhier platform. This DMA controller can transfer data from
 | 
					 | 
				
			||||||
 	  memory to memory, memory to peripheral and peripheral to memory.
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+config DMA_BCM2708
 | 
					 | 
				
			||||||
+	tristate "BCM2708 DMA legacy API support"
 | 
					 | 
				
			||||||
+	depends on DMA_BCM2835
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 config XGENE_DMA
 | 
					 | 
				
			||||||
 	tristate "APM X-Gene DMA support"
 | 
					 | 
				
			||||||
 	depends on ARCH_XGENE || COMPILE_TEST
 | 
					 | 
				
			||||||
--- a/drivers/dma/Makefile
 | 
					 | 
				
			||||||
+++ b/drivers/dma/Makefile
 | 
					 | 
				
			||||||
@@ -21,6 +21,7 @@ obj-$(CONFIG_AT_XDMAC) += at_xdmac.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_AXI_DMAC) += dma-axi-dmac.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_BCM_SBA_RAID) += bcm-sba-raid.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_COH901318) += coh901318.o coh901318_lli.o
 | 
					 | 
				
			||||||
+obj-$(CONFIG_DMA_BCM2708) += bcm2708-dmaengine.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_DMA_BCM2835) += bcm2835-dma.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_DMA_JZ4780) += dma-jz4780.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/drivers/dma/bcm2708-dmaengine.c
 | 
					 | 
				
			||||||
@@ -0,0 +1,281 @@
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ * BCM2708 legacy DMA API
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * This program is free software; you can redistribute it and/or modify
 | 
					 | 
				
			||||||
+ * it under the terms of the GNU General Public License as published by
 | 
					 | 
				
			||||||
+ * the Free Software Foundation; either version 2 of the License, or
 | 
					 | 
				
			||||||
+ * (at your option) any later version.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * This program is distributed in the hope that it will be useful,
 | 
					 | 
				
			||||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					 | 
				
			||||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					 | 
				
			||||||
+ * GNU General Public License for more details.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#include <linux/init.h>
 | 
					 | 
				
			||||||
+#include <linux/interrupt.h>
 | 
					 | 
				
			||||||
+#include <linux/list.h>
 | 
					 | 
				
			||||||
+#include <linux/module.h>
 | 
					 | 
				
			||||||
+#include <linux/platform_data/dma-bcm2708.h>
 | 
					 | 
				
			||||||
+#include <linux/platform_device.h>
 | 
					 | 
				
			||||||
+#include <linux/slab.h>
 | 
					 | 
				
			||||||
+#include <linux/io.h>
 | 
					 | 
				
			||||||
+#include <linux/spinlock.h>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#include "virt-dma.h"
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define CACHE_LINE_MASK 31
 | 
					 | 
				
			||||||
+#define DEFAULT_DMACHAN_BITMAP 0x10  /* channel 4 only */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* valid only for channels 0 - 14, 15 has its own base address */
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_CHAN(n)	((n) << 8) /* base address */
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_CHANIO(dma_base, n) \
 | 
					 | 
				
			||||||
+	((void __iomem *)((char *)(dma_base) + BCM2708_DMA_CHAN(n)))
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+struct vc_dmaman {
 | 
					 | 
				
			||||||
+	void __iomem *dma_base;
 | 
					 | 
				
			||||||
+	u32 chan_available; /* bitmap of available channels */
 | 
					 | 
				
			||||||
+	u32 has_feature[BCM_DMA_FEATURE_COUNT]; /* bitmap of feature presence */
 | 
					 | 
				
			||||||
+	struct mutex lock;
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct device *dmaman_dev;	/* we assume there's only one! */
 | 
					 | 
				
			||||||
+static struct vc_dmaman *g_dmaman;	/* DMA manager */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* DMA Auxiliary Functions */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* A DMA buffer on an arbitrary boundary may separate a cache line into a
 | 
					 | 
				
			||||||
+   section inside the DMA buffer and another section outside it.
 | 
					 | 
				
			||||||
+   Even if we flush DMA buffers from the cache there is always the chance that
 | 
					 | 
				
			||||||
+   during a DMA someone will access the part of a cache line that is outside
 | 
					 | 
				
			||||||
+   the DMA buffer - which will then bring in unwelcome data.
 | 
					 | 
				
			||||||
+   Without being able to dictate our own buffer pools we must insist that
 | 
					 | 
				
			||||||
+   DMA buffers consist of a whole number of cache lines.
 | 
					 | 
				
			||||||
+*/
 | 
					 | 
				
			||||||
+extern int bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr, int sg_len)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int i;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	for (i = 0; i < sg_len; i++) {
 | 
					 | 
				
			||||||
+		if (sg_ptr[i].offset & CACHE_LINE_MASK ||
 | 
					 | 
				
			||||||
+		    sg_ptr[i].length & CACHE_LINE_MASK)
 | 
					 | 
				
			||||||
+			return 0;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 1;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL_GPL(bcm_sg_suitable_for_dma);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+extern void bcm_dma_start(void __iomem *dma_chan_base,
 | 
					 | 
				
			||||||
+			  dma_addr_t control_block)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	dsb(sy);	/* ARM data synchronization (push) operation */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	writel(control_block, dma_chan_base + BCM2708_DMA_ADDR);
 | 
					 | 
				
			||||||
+	writel(BCM2708_DMA_ACTIVE, dma_chan_base + BCM2708_DMA_CS);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL_GPL(bcm_dma_start);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+extern void bcm_dma_wait_idle(void __iomem *dma_chan_base)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	dsb(sy);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* ugly busy wait only option for now */
 | 
					 | 
				
			||||||
+	while (readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_ACTIVE)
 | 
					 | 
				
			||||||
+		cpu_relax();
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL_GPL(bcm_dma_wait_idle);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+extern bool bcm_dma_is_busy(void __iomem *dma_chan_base)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	dsb(sy);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_ACTIVE;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL_GPL(bcm_dma_is_busy);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* Complete an ongoing DMA (assuming its results are to be ignored)
 | 
					 | 
				
			||||||
+   Does nothing if there is no DMA in progress.
 | 
					 | 
				
			||||||
+   This routine waits for the current AXI transfer to complete before
 | 
					 | 
				
			||||||
+   terminating the current DMA. If the current transfer is hung on a DREQ used
 | 
					 | 
				
			||||||
+   by an uncooperative peripheral the AXI transfer may never complete.	In this
 | 
					 | 
				
			||||||
+   case the routine times out and return a non-zero error code.
 | 
					 | 
				
			||||||
+   Use of this routine doesn't guarantee that the ongoing or aborted DMA
 | 
					 | 
				
			||||||
+   does not produce an interrupt.
 | 
					 | 
				
			||||||
+*/
 | 
					 | 
				
			||||||
+extern int bcm_dma_abort(void __iomem *dma_chan_base)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	unsigned long int cs;
 | 
					 | 
				
			||||||
+	int rc = 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	cs = readl(dma_chan_base + BCM2708_DMA_CS);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (BCM2708_DMA_ACTIVE & cs) {
 | 
					 | 
				
			||||||
+		long int timeout = 10000;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		/* write 0 to the active bit - pause the DMA */
 | 
					 | 
				
			||||||
+		writel(0, dma_chan_base + BCM2708_DMA_CS);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		/* wait for any current AXI transfer to complete */
 | 
					 | 
				
			||||||
+		while (0 != (cs & BCM2708_DMA_ISPAUSED) && --timeout >= 0)
 | 
					 | 
				
			||||||
+			cs = readl(dma_chan_base + BCM2708_DMA_CS);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		if (0 != (cs & BCM2708_DMA_ISPAUSED)) {
 | 
					 | 
				
			||||||
+			/* we'll un-pause when we set of our next DMA */
 | 
					 | 
				
			||||||
+			rc = -ETIMEDOUT;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		} else if (BCM2708_DMA_ACTIVE & cs) {
 | 
					 | 
				
			||||||
+			/* terminate the control block chain */
 | 
					 | 
				
			||||||
+			writel(0, dma_chan_base + BCM2708_DMA_NEXTCB);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+			/* abort the whole DMA */
 | 
					 | 
				
			||||||
+			writel(BCM2708_DMA_ABORT | BCM2708_DMA_ACTIVE,
 | 
					 | 
				
			||||||
+			       dma_chan_base + BCM2708_DMA_CS);
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return rc;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL_GPL(bcm_dma_abort);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+ /* DMA Manager Device Methods */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void vc_dmaman_init(struct vc_dmaman *dmaman, void __iomem *dma_base,
 | 
					 | 
				
			||||||
+			   u32 chans_available)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	dmaman->dma_base = dma_base;
 | 
					 | 
				
			||||||
+	dmaman->chan_available = chans_available;
 | 
					 | 
				
			||||||
+	dmaman->has_feature[BCM_DMA_FEATURE_FAST_ORD] = 0x0c;  /* 2 & 3 */
 | 
					 | 
				
			||||||
+	dmaman->has_feature[BCM_DMA_FEATURE_BULK_ORD] = 0x01;  /* 0 */
 | 
					 | 
				
			||||||
+	dmaman->has_feature[BCM_DMA_FEATURE_NORMAL_ORD] = 0xfe;  /* 1 to 7 */
 | 
					 | 
				
			||||||
+	dmaman->has_feature[BCM_DMA_FEATURE_LITE_ORD] = 0x7f00;  /* 8 to 14 */
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int vc_dmaman_chan_alloc(struct vc_dmaman *dmaman,
 | 
					 | 
				
			||||||
+				unsigned required_feature_set)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	u32 chans;
 | 
					 | 
				
			||||||
+	int chan = 0;
 | 
					 | 
				
			||||||
+	int feature;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	chans = dmaman->chan_available;
 | 
					 | 
				
			||||||
+	for (feature = 0; feature < BCM_DMA_FEATURE_COUNT; feature++)
 | 
					 | 
				
			||||||
+		/* select the subset of available channels with the desired
 | 
					 | 
				
			||||||
+		   features */
 | 
					 | 
				
			||||||
+		if (required_feature_set & (1 << feature))
 | 
					 | 
				
			||||||
+			chans &= dmaman->has_feature[feature];
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (!chans)
 | 
					 | 
				
			||||||
+		return -ENOENT;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* return the ordinal of the first channel in the bitmap */
 | 
					 | 
				
			||||||
+	while (chans != 0 && (chans & 1) == 0) {
 | 
					 | 
				
			||||||
+		chans >>= 1;
 | 
					 | 
				
			||||||
+		chan++;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	/* claim the channel */
 | 
					 | 
				
			||||||
+	dmaman->chan_available &= ~(1 << chan);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return chan;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int vc_dmaman_chan_free(struct vc_dmaman *dmaman, int chan)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	if (chan < 0)
 | 
					 | 
				
			||||||
+		return -EINVAL;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if ((1 << chan) & dmaman->chan_available)
 | 
					 | 
				
			||||||
+		return -EIDRM;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	dmaman->chan_available |= (1 << chan);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* DMA Manager Monitor */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+extern int bcm_dma_chan_alloc(unsigned required_feature_set,
 | 
					 | 
				
			||||||
+			      void __iomem **out_dma_base, int *out_dma_irq)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct vc_dmaman *dmaman = g_dmaman;
 | 
					 | 
				
			||||||
+	struct platform_device *pdev = to_platform_device(dmaman_dev);
 | 
					 | 
				
			||||||
+	struct resource *r;
 | 
					 | 
				
			||||||
+	int chan;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (!dmaman_dev)
 | 
					 | 
				
			||||||
+		return -ENODEV;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	mutex_lock(&dmaman->lock);
 | 
					 | 
				
			||||||
+	chan = vc_dmaman_chan_alloc(dmaman, required_feature_set);
 | 
					 | 
				
			||||||
+	if (chan < 0)
 | 
					 | 
				
			||||||
+		goto out;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	r = platform_get_resource(pdev, IORESOURCE_IRQ, (unsigned int)chan);
 | 
					 | 
				
			||||||
+	if (!r) {
 | 
					 | 
				
			||||||
+		dev_err(dmaman_dev, "failed to get irq for DMA channel %d\n",
 | 
					 | 
				
			||||||
+			chan);
 | 
					 | 
				
			||||||
+		vc_dmaman_chan_free(dmaman, chan);
 | 
					 | 
				
			||||||
+		chan = -ENOENT;
 | 
					 | 
				
			||||||
+		goto out;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	*out_dma_base = BCM2708_DMA_CHANIO(dmaman->dma_base, chan);
 | 
					 | 
				
			||||||
+	*out_dma_irq = r->start;
 | 
					 | 
				
			||||||
+	dev_dbg(dmaman_dev,
 | 
					 | 
				
			||||||
+		"Legacy API allocated channel=%d, base=%p, irq=%i\n",
 | 
					 | 
				
			||||||
+		chan, *out_dma_base, *out_dma_irq);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+out:
 | 
					 | 
				
			||||||
+	mutex_unlock(&dmaman->lock);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return chan;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL_GPL(bcm_dma_chan_alloc);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+extern int bcm_dma_chan_free(int channel)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct vc_dmaman *dmaman = g_dmaman;
 | 
					 | 
				
			||||||
+	int rc;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (!dmaman_dev)
 | 
					 | 
				
			||||||
+		return -ENODEV;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	mutex_lock(&dmaman->lock);
 | 
					 | 
				
			||||||
+	rc = vc_dmaman_chan_free(dmaman, channel);
 | 
					 | 
				
			||||||
+	mutex_unlock(&dmaman->lock);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return rc;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL_GPL(bcm_dma_chan_free);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+int bcm_dmaman_probe(struct platform_device *pdev, void __iomem *base,
 | 
					 | 
				
			||||||
+		     u32 chans_available)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct device *dev = &pdev->dev;
 | 
					 | 
				
			||||||
+	struct vc_dmaman *dmaman;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	dmaman = devm_kzalloc(dev, sizeof(*dmaman), GFP_KERNEL);
 | 
					 | 
				
			||||||
+	if (!dmaman)
 | 
					 | 
				
			||||||
+		return -ENOMEM;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	mutex_init(&dmaman->lock);
 | 
					 | 
				
			||||||
+	vc_dmaman_init(dmaman, base, chans_available);
 | 
					 | 
				
			||||||
+	g_dmaman = dmaman;
 | 
					 | 
				
			||||||
+	dmaman_dev = dev;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	dev_info(dev, "DMA legacy API manager, dmachans=0x%x\n",
 | 
					 | 
				
			||||||
+		 chans_available);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL(bcm_dmaman_probe);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+int bcm_dmaman_remove(struct platform_device *pdev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	dmaman_dev = NULL;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL(bcm_dmaman_remove);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+MODULE_LICENSE("GPL");
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/include/linux/platform_data/dma-bcm2708.h
 | 
					 | 
				
			||||||
@@ -0,0 +1,143 @@
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ *  Copyright (C) 2010 Broadcom
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * 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.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#ifndef _PLAT_BCM2708_DMA_H
 | 
					 | 
				
			||||||
+#define _PLAT_BCM2708_DMA_H
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* DMA CS Control and Status bits */
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_ACTIVE	BIT(0)
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_INT		BIT(2)
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_ISPAUSED	BIT(4)  /* Pause requested or not active */
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_ISHELD	BIT(5)  /* Is held by DREQ flow control */
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_ERR		BIT(8)
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_ABORT	BIT(30) /* stop current CB, go to next, WO */
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_RESET	BIT(31) /* WO, self clearing */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* DMA control block "info" field bits */
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_INT_EN	BIT(0)
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_TDMODE	BIT(1)
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_WAIT_RESP	BIT(3)
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_D_INC	BIT(4)
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_D_WIDTH	BIT(5)
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_D_DREQ	BIT(6)
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_S_INC	BIT(8)
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_S_WIDTH	BIT(9)
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_S_DREQ	BIT(10)
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define	BCM2708_DMA_BURST(x)	(((x) & 0xf) << 12)
 | 
					 | 
				
			||||||
+#define	BCM2708_DMA_PER_MAP(x)	((x) << 16)
 | 
					 | 
				
			||||||
+#define	BCM2708_DMA_WAITS(x)	(((x) & 0x1f) << 21)
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_DREQ_EMMC	11
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_DREQ_SDHOST	13
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_CS		0x00 /* Control and Status */
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_ADDR	0x04
 | 
					 | 
				
			||||||
+/* the current control block appears in the following registers - read only */
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_INFO	0x08
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_SOURCE_AD	0x0c
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_DEST_AD	0x10
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_NEXTCB	0x1C
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_DEBUG	0x20
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define BCM2708_DMA4_CS		(BCM2708_DMA_CHAN(4) + BCM2708_DMA_CS)
 | 
					 | 
				
			||||||
+#define BCM2708_DMA4_ADDR	(BCM2708_DMA_CHAN(4) + BCM2708_DMA_ADDR)
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define BCM2708_DMA_TDMODE_LEN(w, h) ((h) << 16 | (w))
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* When listing features we can ask for when allocating DMA channels give
 | 
					 | 
				
			||||||
+   those with higher priority smaller ordinal numbers */
 | 
					 | 
				
			||||||
+#define BCM_DMA_FEATURE_FAST_ORD	0
 | 
					 | 
				
			||||||
+#define BCM_DMA_FEATURE_BULK_ORD	1
 | 
					 | 
				
			||||||
+#define BCM_DMA_FEATURE_NORMAL_ORD	2
 | 
					 | 
				
			||||||
+#define BCM_DMA_FEATURE_LITE_ORD	3
 | 
					 | 
				
			||||||
+#define BCM_DMA_FEATURE_FAST		BIT(BCM_DMA_FEATURE_FAST_ORD)
 | 
					 | 
				
			||||||
+#define BCM_DMA_FEATURE_BULK		BIT(BCM_DMA_FEATURE_BULK_ORD)
 | 
					 | 
				
			||||||
+#define BCM_DMA_FEATURE_NORMAL		BIT(BCM_DMA_FEATURE_NORMAL_ORD)
 | 
					 | 
				
			||||||
+#define BCM_DMA_FEATURE_LITE		BIT(BCM_DMA_FEATURE_LITE_ORD)
 | 
					 | 
				
			||||||
+#define BCM_DMA_FEATURE_COUNT		4
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+struct bcm2708_dma_cb {
 | 
					 | 
				
			||||||
+	u32 info;
 | 
					 | 
				
			||||||
+	u32 src;
 | 
					 | 
				
			||||||
+	u32 dst;
 | 
					 | 
				
			||||||
+	u32 length;
 | 
					 | 
				
			||||||
+	u32 stride;
 | 
					 | 
				
			||||||
+	u32 next;
 | 
					 | 
				
			||||||
+	u32 pad[2];
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+struct scatterlist;
 | 
					 | 
				
			||||||
+struct platform_device;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#if defined(CONFIG_DMA_BCM2708) || defined(CONFIG_DMA_BCM2708_MODULE)
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+int bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr, int sg_len);
 | 
					 | 
				
			||||||
+void bcm_dma_start(void __iomem *dma_chan_base, dma_addr_t control_block);
 | 
					 | 
				
			||||||
+void bcm_dma_wait_idle(void __iomem *dma_chan_base);
 | 
					 | 
				
			||||||
+bool bcm_dma_is_busy(void __iomem *dma_chan_base);
 | 
					 | 
				
			||||||
+int bcm_dma_abort(void __iomem *dma_chan_base);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* return channel no or -ve error */
 | 
					 | 
				
			||||||
+int bcm_dma_chan_alloc(unsigned preferred_feature_set,
 | 
					 | 
				
			||||||
+		       void __iomem **out_dma_base, int *out_dma_irq);
 | 
					 | 
				
			||||||
+int bcm_dma_chan_free(int channel);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+int bcm_dmaman_probe(struct platform_device *pdev, void __iomem *base,
 | 
					 | 
				
			||||||
+		     u32 chans_available);
 | 
					 | 
				
			||||||
+int bcm_dmaman_remove(struct platform_device *pdev);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#else /* CONFIG_DMA_BCM2708 */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static inline int bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr,
 | 
					 | 
				
			||||||
+					  int sg_len)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static inline void bcm_dma_start(void __iomem *dma_chan_base,
 | 
					 | 
				
			||||||
+				 dma_addr_t control_block) { }
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static inline void bcm_dma_wait_idle(void __iomem *dma_chan_base) { }
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static inline bool bcm_dma_is_busy(void __iomem *dma_chan_base)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return false;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static inline int bcm_dma_abort(void __iomem *dma_chan_base)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return -EINVAL;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static inline int bcm_dma_chan_alloc(unsigned preferred_feature_set,
 | 
					 | 
				
			||||||
+				     void __iomem **out_dma_base,
 | 
					 | 
				
			||||||
+				     int *out_dma_irq)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return -EINVAL;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static inline int bcm_dma_chan_free(int channel)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return -EINVAL;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static inline int bcm_dmaman_probe(struct platform_device *pdev,
 | 
					 | 
				
			||||||
+				   void __iomem *base, u32 chans_available)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static inline int bcm_dmaman_remove(struct platform_device *pdev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#endif /* CONFIG_DMA_BCM2708 || CONFIG_DMA_BCM2708_MODULE */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#endif /* _PLAT_BCM2708_DMA_H */
 | 
					 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,488 +0,0 @@
 | 
				
			|||||||
From 156d50d2fb05f132d53927cbf9fad3c9c37e8937 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: popcornmix <popcornmix@gmail.com>
 | 
					 | 
				
			||||||
Date: Fri, 28 Oct 2016 15:36:43 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] vc_mem: Add vc_mem driver for querying firmware
 | 
					 | 
				
			||||||
 memory addresses
 | 
					 | 
				
			||||||
MIME-Version: 1.0
 | 
					 | 
				
			||||||
Content-Type: text/plain; charset=UTF-8
 | 
					 | 
				
			||||||
Content-Transfer-Encoding: 8bit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: popcornmix <popcornmix@gmail.com>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
BCM270x: Move vc_mem
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Make the vc_mem module available for ARCH_BCM2835 by moving it.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
char: vc_mem: Fix up compat ioctls for 64bit kernel
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
compat_ioctl wasn't defined, so 32bit user/64bit kernel
 | 
					 | 
				
			||||||
always failed.
 | 
					 | 
				
			||||||
VC_MEM_IOC_MEM_PHYS_ADDR was defined with parameter size
 | 
					 | 
				
			||||||
unsigned long, so the ioctl cmd changes between sizes.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
char: vc_mem: Fix all coding style issues.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Cleans up all checkpatch errors in vc_mem.c and vc_mem.h
 | 
					 | 
				
			||||||
No functional change to the code.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/char/broadcom/Kconfig   |  18 ++
 | 
					 | 
				
			||||||
 drivers/char/broadcom/Makefile  |   1 +
 | 
					 | 
				
			||||||
 drivers/char/broadcom/vc_mem.c  | 375 ++++++++++++++++++++++++++++++++
 | 
					 | 
				
			||||||
 include/linux/broadcom/vc_mem.h |  39 ++++
 | 
					 | 
				
			||||||
 4 files changed, 433 insertions(+)
 | 
					 | 
				
			||||||
 create mode 100644 drivers/char/broadcom/Kconfig
 | 
					 | 
				
			||||||
 create mode 100644 drivers/char/broadcom/Makefile
 | 
					 | 
				
			||||||
 create mode 100644 drivers/char/broadcom/vc_mem.c
 | 
					 | 
				
			||||||
 create mode 100644 include/linux/broadcom/vc_mem.h
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/drivers/char/broadcom/Kconfig
 | 
					 | 
				
			||||||
@@ -0,0 +1,18 @@
 | 
					 | 
				
			||||||
+#
 | 
					 | 
				
			||||||
+# Broadcom char driver config
 | 
					 | 
				
			||||||
+#
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+menuconfig BRCM_CHAR_DRIVERS
 | 
					 | 
				
			||||||
+	bool "Broadcom Char Drivers"
 | 
					 | 
				
			||||||
+	help
 | 
					 | 
				
			||||||
+	  Broadcom's char drivers
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+if BRCM_CHAR_DRIVERS
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+config BCM2708_VCMEM
 | 
					 | 
				
			||||||
+	bool "Videocore Memory"
 | 
					 | 
				
			||||||
+        default y
 | 
					 | 
				
			||||||
+        help
 | 
					 | 
				
			||||||
+          Helper for videocore memory access and total size allocation.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+endif
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/drivers/char/broadcom/Makefile
 | 
					 | 
				
			||||||
@@ -0,0 +1 @@
 | 
					 | 
				
			||||||
+obj-$(CONFIG_BCM2708_VCMEM)	+= vc_mem.o
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/drivers/char/broadcom/vc_mem.c
 | 
					 | 
				
			||||||
@@ -0,0 +1,375 @@
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ * Copyright 2010 - 2011 Broadcom Corporation.  All rights reserved.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Unless you and Broadcom execute a separate written software license
 | 
					 | 
				
			||||||
+ * agreement governing use of this software, this software is licensed to you
 | 
					 | 
				
			||||||
+ * under the terms of the GNU General Public License version 2, available at
 | 
					 | 
				
			||||||
+ * http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Notwithstanding the above, under no circumstances may you combine this
 | 
					 | 
				
			||||||
+ * software in any way with any other Broadcom software provided under a
 | 
					 | 
				
			||||||
+ * license other than the GPL, without Broadcom's express prior written
 | 
					 | 
				
			||||||
+ * consent.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#include <linux/kernel.h>
 | 
					 | 
				
			||||||
+#include <linux/module.h>
 | 
					 | 
				
			||||||
+#include <linux/fs.h>
 | 
					 | 
				
			||||||
+#include <linux/device.h>
 | 
					 | 
				
			||||||
+#include <linux/cdev.h>
 | 
					 | 
				
			||||||
+#include <linux/mm.h>
 | 
					 | 
				
			||||||
+#include <linux/slab.h>
 | 
					 | 
				
			||||||
+#include <linux/debugfs.h>
 | 
					 | 
				
			||||||
+#include <linux/uaccess.h>
 | 
					 | 
				
			||||||
+#include <linux/dma-mapping.h>
 | 
					 | 
				
			||||||
+#include <linux/broadcom/vc_mem.h>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define DRIVER_NAME  "vc-mem"
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* Device (/dev) related variables */
 | 
					 | 
				
			||||||
+static dev_t vc_mem_devnum;
 | 
					 | 
				
			||||||
+static struct class *vc_mem_class;
 | 
					 | 
				
			||||||
+static struct cdev vc_mem_cdev;
 | 
					 | 
				
			||||||
+static int vc_mem_inited;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#ifdef CONFIG_DEBUG_FS
 | 
					 | 
				
			||||||
+static struct dentry *vc_mem_debugfs_entry;
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ * Videocore memory addresses and size
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Drivers that wish to know the videocore memory addresses and sizes should
 | 
					 | 
				
			||||||
+ * use these variables instead of the MM_IO_BASE and MM_ADDR_IO defines in
 | 
					 | 
				
			||||||
+ * headers. This allows the other drivers to not be tied down to a a certain
 | 
					 | 
				
			||||||
+ * address/size at compile time.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * In the future, the goal is to have the videocore memory virtual address and
 | 
					 | 
				
			||||||
+ * size be calculated at boot time rather than at compile time. The decision of
 | 
					 | 
				
			||||||
+ * where the videocore memory resides and its size would be in the hands of the
 | 
					 | 
				
			||||||
+ * bootloader (and/or kernel). When that happens, the values of these variables
 | 
					 | 
				
			||||||
+ * would be calculated and assigned in the init function.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+/* In the 2835 VC in mapped above ARM, but ARM has full access to VC space */
 | 
					 | 
				
			||||||
+unsigned long mm_vc_mem_phys_addr;
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL(mm_vc_mem_phys_addr);
 | 
					 | 
				
			||||||
+unsigned int mm_vc_mem_size;
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL(mm_vc_mem_size);
 | 
					 | 
				
			||||||
+unsigned int mm_vc_mem_base;
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL(mm_vc_mem_base);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static uint phys_addr;
 | 
					 | 
				
			||||||
+static uint mem_size;
 | 
					 | 
				
			||||||
+static uint mem_base;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int
 | 
					 | 
				
			||||||
+vc_mem_open(struct inode *inode, struct file *file)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	(void)inode;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	pr_debug("%s: called file = 0x%p\n", __func__, file);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int
 | 
					 | 
				
			||||||
+vc_mem_release(struct inode *inode, struct file *file)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	(void)inode;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	pr_debug("%s: called file = 0x%p\n", __func__, file);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void
 | 
					 | 
				
			||||||
+vc_mem_get_size(void)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void
 | 
					 | 
				
			||||||
+vc_mem_get_base(void)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+int
 | 
					 | 
				
			||||||
+vc_mem_get_current_size(void)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return mm_vc_mem_size;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL_GPL(vc_mem_get_current_size);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static long
 | 
					 | 
				
			||||||
+vc_mem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int rc = 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	(void) cmd;
 | 
					 | 
				
			||||||
+	(void) arg;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	pr_debug("%s: called file = 0x%p, cmd %08x\n", __func__, file, cmd);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	switch (cmd) {
 | 
					 | 
				
			||||||
+	case VC_MEM_IOC_MEM_PHYS_ADDR:
 | 
					 | 
				
			||||||
+		{
 | 
					 | 
				
			||||||
+			pr_debug("%s: VC_MEM_IOC_MEM_PHYS_ADDR=0x%p\n",
 | 
					 | 
				
			||||||
+				__func__, (void *)mm_vc_mem_phys_addr);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+			if (copy_to_user((void *)arg, &mm_vc_mem_phys_addr,
 | 
					 | 
				
			||||||
+					 sizeof(mm_vc_mem_phys_addr))) {
 | 
					 | 
				
			||||||
+				rc = -EFAULT;
 | 
					 | 
				
			||||||
+			}
 | 
					 | 
				
			||||||
+			break;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+	case VC_MEM_IOC_MEM_SIZE:
 | 
					 | 
				
			||||||
+		{
 | 
					 | 
				
			||||||
+			/* Get the videocore memory size first */
 | 
					 | 
				
			||||||
+			vc_mem_get_size();
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+			pr_debug("%s: VC_MEM_IOC_MEM_SIZE=%x\n", __func__,
 | 
					 | 
				
			||||||
+				 mm_vc_mem_size);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+			if (copy_to_user((void *)arg, &mm_vc_mem_size,
 | 
					 | 
				
			||||||
+					 sizeof(mm_vc_mem_size))) {
 | 
					 | 
				
			||||||
+				rc = -EFAULT;
 | 
					 | 
				
			||||||
+			}
 | 
					 | 
				
			||||||
+			break;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+	case VC_MEM_IOC_MEM_BASE:
 | 
					 | 
				
			||||||
+		{
 | 
					 | 
				
			||||||
+			/* Get the videocore memory base */
 | 
					 | 
				
			||||||
+			vc_mem_get_base();
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+			pr_debug("%s: VC_MEM_IOC_MEM_BASE=%x\n", __func__,
 | 
					 | 
				
			||||||
+				 mm_vc_mem_base);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+			if (copy_to_user((void *)arg, &mm_vc_mem_base,
 | 
					 | 
				
			||||||
+					 sizeof(mm_vc_mem_base))) {
 | 
					 | 
				
			||||||
+				rc = -EFAULT;
 | 
					 | 
				
			||||||
+			}
 | 
					 | 
				
			||||||
+			break;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+	case VC_MEM_IOC_MEM_LOAD:
 | 
					 | 
				
			||||||
+		{
 | 
					 | 
				
			||||||
+			/* Get the videocore memory base */
 | 
					 | 
				
			||||||
+			vc_mem_get_base();
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+			pr_debug("%s: VC_MEM_IOC_MEM_LOAD=%x\n", __func__,
 | 
					 | 
				
			||||||
+				mm_vc_mem_base);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+			if (copy_to_user((void *)arg, &mm_vc_mem_base,
 | 
					 | 
				
			||||||
+					 sizeof(mm_vc_mem_base))) {
 | 
					 | 
				
			||||||
+				rc = -EFAULT;
 | 
					 | 
				
			||||||
+			}
 | 
					 | 
				
			||||||
+			break;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+	default:
 | 
					 | 
				
			||||||
+		{
 | 
					 | 
				
			||||||
+			return -ENOTTY;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	pr_debug("%s: file = 0x%p returning %d\n", __func__, file, rc);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return rc;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#ifdef CONFIG_COMPAT
 | 
					 | 
				
			||||||
+static long
 | 
					 | 
				
			||||||
+vc_mem_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int rc = 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	switch (cmd) {
 | 
					 | 
				
			||||||
+	case VC_MEM_IOC_MEM_PHYS_ADDR32:
 | 
					 | 
				
			||||||
+		pr_debug("%s: VC_MEM_IOC_MEM_PHYS_ADDR32=0x%p\n",
 | 
					 | 
				
			||||||
+			 __func__, (void *)mm_vc_mem_phys_addr);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		/* This isn't correct, but will cover us for now as
 | 
					 | 
				
			||||||
+		 * VideoCore is 32bit only.
 | 
					 | 
				
			||||||
+		 */
 | 
					 | 
				
			||||||
+		if (copy_to_user((void *)arg, &mm_vc_mem_phys_addr,
 | 
					 | 
				
			||||||
+				 sizeof(compat_ulong_t)))
 | 
					 | 
				
			||||||
+			rc = -EFAULT;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		break;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	default:
 | 
					 | 
				
			||||||
+		rc = vc_mem_ioctl(file, cmd, arg);
 | 
					 | 
				
			||||||
+		break;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return rc;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int
 | 
					 | 
				
			||||||
+vc_mem_mmap(struct file *filp, struct vm_area_struct *vma)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int rc = 0;
 | 
					 | 
				
			||||||
+	unsigned long length = vma->vm_end - vma->vm_start;
 | 
					 | 
				
			||||||
+	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	pr_debug("%s: vm_start = 0x%08lx vm_end = 0x%08lx vm_pgoff = 0x%08lx\n",
 | 
					 | 
				
			||||||
+		 __func__, (long)vma->vm_start, (long)vma->vm_end,
 | 
					 | 
				
			||||||
+		 (long)vma->vm_pgoff);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (offset + length > mm_vc_mem_size) {
 | 
					 | 
				
			||||||
+		pr_err("%s: length %ld is too big\n", __func__, length);
 | 
					 | 
				
			||||||
+		return -EINVAL;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	/* Do not cache the memory map */
 | 
					 | 
				
			||||||
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	rc = remap_pfn_range(vma, vma->vm_start,
 | 
					 | 
				
			||||||
+			     (mm_vc_mem_phys_addr >> PAGE_SHIFT) +
 | 
					 | 
				
			||||||
+			     vma->vm_pgoff, length, vma->vm_page_prot);
 | 
					 | 
				
			||||||
+	if (rc)
 | 
					 | 
				
			||||||
+		pr_err("%s: remap_pfn_range failed (rc=%d)\n", __func__, rc);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return rc;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* File Operations for the driver. */
 | 
					 | 
				
			||||||
+static const struct file_operations vc_mem_fops = {
 | 
					 | 
				
			||||||
+	.owner = THIS_MODULE,
 | 
					 | 
				
			||||||
+	.open = vc_mem_open,
 | 
					 | 
				
			||||||
+	.release = vc_mem_release,
 | 
					 | 
				
			||||||
+	.unlocked_ioctl = vc_mem_ioctl,
 | 
					 | 
				
			||||||
+#ifdef CONFIG_COMPAT
 | 
					 | 
				
			||||||
+	.compat_ioctl = vc_mem_compat_ioctl,
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
+	.mmap = vc_mem_mmap,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#ifdef CONFIG_DEBUG_FS
 | 
					 | 
				
			||||||
+static void vc_mem_debugfs_deinit(void)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	debugfs_remove_recursive(vc_mem_debugfs_entry);
 | 
					 | 
				
			||||||
+	vc_mem_debugfs_entry = NULL;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int vc_mem_debugfs_init(
 | 
					 | 
				
			||||||
+	struct device *dev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	vc_mem_debugfs_entry = debugfs_create_dir(DRIVER_NAME, NULL);
 | 
					 | 
				
			||||||
+	if (!vc_mem_debugfs_entry) {
 | 
					 | 
				
			||||||
+		dev_warn(dev, "could not create debugfs entry\n");
 | 
					 | 
				
			||||||
+		return -EFAULT;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	debugfs_create_x32("vc_mem_phys_addr",
 | 
					 | 
				
			||||||
+				0444,
 | 
					 | 
				
			||||||
+				vc_mem_debugfs_entry,
 | 
					 | 
				
			||||||
+				(u32 *)&mm_vc_mem_phys_addr);
 | 
					 | 
				
			||||||
+	debugfs_create_x32("vc_mem_size",
 | 
					 | 
				
			||||||
+				0444,
 | 
					 | 
				
			||||||
+				vc_mem_debugfs_entry,
 | 
					 | 
				
			||||||
+				(u32 *)&mm_vc_mem_size);
 | 
					 | 
				
			||||||
+	debugfs_create_x32("vc_mem_base",
 | 
					 | 
				
			||||||
+				0444,
 | 
					 | 
				
			||||||
+				vc_mem_debugfs_entry,
 | 
					 | 
				
			||||||
+				(u32 *)&mm_vc_mem_base);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#endif /* CONFIG_DEBUG_FS */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* Module load/unload functions */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int __init
 | 
					 | 
				
			||||||
+vc_mem_init(void)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int rc = -EFAULT;
 | 
					 | 
				
			||||||
+	struct device *dev;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	pr_debug("%s: called\n", __func__);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	mm_vc_mem_phys_addr = phys_addr;
 | 
					 | 
				
			||||||
+	mm_vc_mem_size = mem_size;
 | 
					 | 
				
			||||||
+	mm_vc_mem_base = mem_base;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	vc_mem_get_size();
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	pr_info("vc-mem: phys_addr:0x%08lx mem_base=0x%08x mem_size:0x%08x(%u MiB)\n",
 | 
					 | 
				
			||||||
+		mm_vc_mem_phys_addr, mm_vc_mem_base, mm_vc_mem_size,
 | 
					 | 
				
			||||||
+		mm_vc_mem_size / (1024 * 1024));
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	rc = alloc_chrdev_region(&vc_mem_devnum, 0, 1, DRIVER_NAME);
 | 
					 | 
				
			||||||
+	if (rc < 0) {
 | 
					 | 
				
			||||||
+		pr_err("%s: alloc_chrdev_region failed (rc=%d)\n",
 | 
					 | 
				
			||||||
+		       __func__, rc);
 | 
					 | 
				
			||||||
+		goto out_err;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	cdev_init(&vc_mem_cdev, &vc_mem_fops);
 | 
					 | 
				
			||||||
+	rc = cdev_add(&vc_mem_cdev, vc_mem_devnum, 1);
 | 
					 | 
				
			||||||
+	if (rc) {
 | 
					 | 
				
			||||||
+		pr_err("%s: cdev_add failed (rc=%d)\n", __func__, rc);
 | 
					 | 
				
			||||||
+		goto out_unregister;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	vc_mem_class = class_create(THIS_MODULE, DRIVER_NAME);
 | 
					 | 
				
			||||||
+	if (IS_ERR(vc_mem_class)) {
 | 
					 | 
				
			||||||
+		rc = PTR_ERR(vc_mem_class);
 | 
					 | 
				
			||||||
+		pr_err("%s: class_create failed (rc=%d)\n", __func__, rc);
 | 
					 | 
				
			||||||
+		goto out_cdev_del;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	dev = device_create(vc_mem_class, NULL, vc_mem_devnum, NULL,
 | 
					 | 
				
			||||||
+			    DRIVER_NAME);
 | 
					 | 
				
			||||||
+	if (IS_ERR(dev)) {
 | 
					 | 
				
			||||||
+		rc = PTR_ERR(dev);
 | 
					 | 
				
			||||||
+		pr_err("%s: device_create failed (rc=%d)\n", __func__, rc);
 | 
					 | 
				
			||||||
+		goto out_class_destroy;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#ifdef CONFIG_DEBUG_FS
 | 
					 | 
				
			||||||
+	/* don't fail if the debug entries cannot be created */
 | 
					 | 
				
			||||||
+	vc_mem_debugfs_init(dev);
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	vc_mem_inited = 1;
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	device_destroy(vc_mem_class, vc_mem_devnum);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+out_class_destroy:
 | 
					 | 
				
			||||||
+	class_destroy(vc_mem_class);
 | 
					 | 
				
			||||||
+	vc_mem_class = NULL;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+out_cdev_del:
 | 
					 | 
				
			||||||
+	cdev_del(&vc_mem_cdev);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+out_unregister:
 | 
					 | 
				
			||||||
+	unregister_chrdev_region(vc_mem_devnum, 1);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+out_err:
 | 
					 | 
				
			||||||
+	return -1;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void __exit
 | 
					 | 
				
			||||||
+vc_mem_exit(void)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	pr_debug("%s: called\n", __func__);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (vc_mem_inited) {
 | 
					 | 
				
			||||||
+#if CONFIG_DEBUG_FS
 | 
					 | 
				
			||||||
+		vc_mem_debugfs_deinit();
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
+		device_destroy(vc_mem_class, vc_mem_devnum);
 | 
					 | 
				
			||||||
+		class_destroy(vc_mem_class);
 | 
					 | 
				
			||||||
+		cdev_del(&vc_mem_cdev);
 | 
					 | 
				
			||||||
+		unregister_chrdev_region(vc_mem_devnum, 1);
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+module_init(vc_mem_init);
 | 
					 | 
				
			||||||
+module_exit(vc_mem_exit);
 | 
					 | 
				
			||||||
+MODULE_LICENSE("GPL");
 | 
					 | 
				
			||||||
+MODULE_AUTHOR("Broadcom Corporation");
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+module_param(phys_addr, uint, 0644);
 | 
					 | 
				
			||||||
+module_param(mem_size, uint, 0644);
 | 
					 | 
				
			||||||
+module_param(mem_base, uint, 0644);
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/include/linux/broadcom/vc_mem.h
 | 
					 | 
				
			||||||
@@ -0,0 +1,39 @@
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ * Copyright 2010 - 2011 Broadcom Corporation.  All rights reserved.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Unless you and Broadcom execute a separate written software license
 | 
					 | 
				
			||||||
+ * agreement governing use of this software, this software is licensed to you
 | 
					 | 
				
			||||||
+ * under the terms of the GNU General Public License version 2, available at
 | 
					 | 
				
			||||||
+ * http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Notwithstanding the above, under no circumstances may you combine this
 | 
					 | 
				
			||||||
+ * software in any way with any other Broadcom software provided under a
 | 
					 | 
				
			||||||
+ * license other than the GPL, without Broadcom's express prior written
 | 
					 | 
				
			||||||
+ * consent.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#ifndef _VC_MEM_H
 | 
					 | 
				
			||||||
+#define _VC_MEM_H
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#include <linux/ioctl.h>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define VC_MEM_IOC_MAGIC  'v'
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define VC_MEM_IOC_MEM_PHYS_ADDR    _IOR(VC_MEM_IOC_MAGIC, 0, unsigned long)
 | 
					 | 
				
			||||||
+#define VC_MEM_IOC_MEM_SIZE         _IOR(VC_MEM_IOC_MAGIC, 1, unsigned int)
 | 
					 | 
				
			||||||
+#define VC_MEM_IOC_MEM_BASE         _IOR(VC_MEM_IOC_MAGIC, 2, unsigned int)
 | 
					 | 
				
			||||||
+#define VC_MEM_IOC_MEM_LOAD         _IOR(VC_MEM_IOC_MAGIC, 3, unsigned int)
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#ifdef __KERNEL__
 | 
					 | 
				
			||||||
+#define VC_MEM_TO_ARM_ADDR_MASK 0x3FFFFFFF
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+extern unsigned long mm_vc_mem_phys_addr;
 | 
					 | 
				
			||||||
+extern unsigned int  mm_vc_mem_size;
 | 
					 | 
				
			||||||
+extern int vc_mem_get_current_size(void);
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#ifdef CONFIG_COMPAT
 | 
					 | 
				
			||||||
+#define VC_MEM_IOC_MEM_PHYS_ADDR32  _IOR(VC_MEM_IOC_MAGIC, 0, compat_ulong_t)
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#endif  /* _VC_MEM_H */
 | 
					 | 
				
			||||||
@ -1,299 +0,0 @@
 | 
				
			|||||||
From 09897ac552d99d483131bcf107866ac2dc51694f Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Luke Wren <luke@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Fri, 21 Aug 2015 23:14:48 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] Add /dev/gpiomem device for rootless user GPIO access
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Luke Wren <luke@raspberrypi.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bcm2835-gpiomem: Fix for ARCH_BCM2835 builds
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Build on ARCH_BCM2835, and fail to probe if no IO resource.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
See: https://github.com/raspberrypi/linux/issues/1154
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/char/broadcom/Kconfig           |   8 +
 | 
					 | 
				
			||||||
 drivers/char/broadcom/Makefile          |   1 +
 | 
					 | 
				
			||||||
 drivers/char/broadcom/bcm2835-gpiomem.c | 258 ++++++++++++++++++++++++
 | 
					 | 
				
			||||||
 3 files changed, 267 insertions(+)
 | 
					 | 
				
			||||||
 create mode 100644 drivers/char/broadcom/bcm2835-gpiomem.c
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/char/broadcom/Kconfig
 | 
					 | 
				
			||||||
+++ b/drivers/char/broadcom/Kconfig
 | 
					 | 
				
			||||||
@@ -16,3 +16,11 @@ config BCM2708_VCMEM
 | 
					 | 
				
			||||||
           Helper for videocore memory access and total size allocation.
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 endif
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+config BCM2835_DEVGPIOMEM
 | 
					 | 
				
			||||||
+	tristate "/dev/gpiomem rootless GPIO access via mmap() on the BCM2835"
 | 
					 | 
				
			||||||
+	default m
 | 
					 | 
				
			||||||
+	help
 | 
					 | 
				
			||||||
+		Provides users with root-free access to the GPIO registers
 | 
					 | 
				
			||||||
+		on the 2835. Calling mmap(/dev/gpiomem) will map the GPIO
 | 
					 | 
				
			||||||
+		register page to the user's pointer.
 | 
					 | 
				
			||||||
--- a/drivers/char/broadcom/Makefile
 | 
					 | 
				
			||||||
+++ b/drivers/char/broadcom/Makefile
 | 
					 | 
				
			||||||
@@ -1 +1,2 @@
 | 
					 | 
				
			||||||
 obj-$(CONFIG_BCM2708_VCMEM)	+= vc_mem.o
 | 
					 | 
				
			||||||
+obj-$(CONFIG_BCM2835_DEVGPIOMEM)+= bcm2835-gpiomem.o
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/drivers/char/broadcom/bcm2835-gpiomem.c
 | 
					 | 
				
			||||||
@@ -0,0 +1,258 @@
 | 
					 | 
				
			||||||
+/**
 | 
					 | 
				
			||||||
+ * GPIO memory device driver
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Creates a chardev /dev/gpiomem which will provide user access to
 | 
					 | 
				
			||||||
+ * the BCM2835's GPIO registers when it is mmap()'d.
 | 
					 | 
				
			||||||
+ * No longer need root for user GPIO access, but without relaxing permissions
 | 
					 | 
				
			||||||
+ * on /dev/mem.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Written by Luke Wren <luke@raspberrypi.org>
 | 
					 | 
				
			||||||
+ * Copyright (c) 2015, Raspberry Pi (Trading) Ltd.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Redistribution and use in source and binary forms, with or without
 | 
					 | 
				
			||||||
+ * modification, are permitted provided that the following conditions
 | 
					 | 
				
			||||||
+ * are met:
 | 
					 | 
				
			||||||
+ * 1. Redistributions of source code must retain the above copyright
 | 
					 | 
				
			||||||
+ *    notice, this list of conditions, and the following disclaimer,
 | 
					 | 
				
			||||||
+ *    without modification.
 | 
					 | 
				
			||||||
+ * 2. Redistributions in binary form must reproduce the above copyright
 | 
					 | 
				
			||||||
+ *    notice, this list of conditions and the following disclaimer in the
 | 
					 | 
				
			||||||
+ *    documentation and/or other materials provided with the distribution.
 | 
					 | 
				
			||||||
+ * 3. The names of the above-listed copyright holders may not be used
 | 
					 | 
				
			||||||
+ *    to endorse or promote products derived from this software without
 | 
					 | 
				
			||||||
+ *    specific prior written permission.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * ALTERNATIVELY, this software may be distributed under the terms of the
 | 
					 | 
				
			||||||
+ * GNU General Public License ("GPL") version 2, as published by the Free
 | 
					 | 
				
			||||||
+ * Software Foundation.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 | 
					 | 
				
			||||||
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 | 
					 | 
				
			||||||
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | 
					 | 
				
			||||||
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 | 
					 | 
				
			||||||
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 | 
					 | 
				
			||||||
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | 
					 | 
				
			||||||
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
					 | 
				
			||||||
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | 
					 | 
				
			||||||
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
					 | 
				
			||||||
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
					 | 
				
			||||||
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#include <linux/kernel.h>
 | 
					 | 
				
			||||||
+#include <linux/module.h>
 | 
					 | 
				
			||||||
+#include <linux/of.h>
 | 
					 | 
				
			||||||
+#include <linux/platform_device.h>
 | 
					 | 
				
			||||||
+#include <linux/mm.h>
 | 
					 | 
				
			||||||
+#include <linux/slab.h>
 | 
					 | 
				
			||||||
+#include <linux/cdev.h>
 | 
					 | 
				
			||||||
+#include <linux/pagemap.h>
 | 
					 | 
				
			||||||
+#include <linux/io.h>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define DEVICE_NAME "bcm2835-gpiomem"
 | 
					 | 
				
			||||||
+#define DRIVER_NAME "gpiomem-bcm2835"
 | 
					 | 
				
			||||||
+#define DEVICE_MINOR 0
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+struct bcm2835_gpiomem_instance {
 | 
					 | 
				
			||||||
+	unsigned long gpio_regs_phys;
 | 
					 | 
				
			||||||
+	struct device *dev;
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct cdev bcm2835_gpiomem_cdev;
 | 
					 | 
				
			||||||
+static dev_t bcm2835_gpiomem_devid;
 | 
					 | 
				
			||||||
+static struct class *bcm2835_gpiomem_class;
 | 
					 | 
				
			||||||
+static struct device *bcm2835_gpiomem_dev;
 | 
					 | 
				
			||||||
+static struct bcm2835_gpiomem_instance *inst;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/****************************************************************************
 | 
					 | 
				
			||||||
+*
 | 
					 | 
				
			||||||
+*   GPIO mem chardev file ops
 | 
					 | 
				
			||||||
+*
 | 
					 | 
				
			||||||
+***************************************************************************/
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int bcm2835_gpiomem_open(struct inode *inode, struct file *file)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int dev = iminor(inode);
 | 
					 | 
				
			||||||
+	int ret = 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (dev != DEVICE_MINOR) {
 | 
					 | 
				
			||||||
+		dev_err(inst->dev, "Unknown minor device: %d", dev);
 | 
					 | 
				
			||||||
+		ret = -ENXIO;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	return ret;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int bcm2835_gpiomem_release(struct inode *inode, struct file *file)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int dev = iminor(inode);
 | 
					 | 
				
			||||||
+	int ret = 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (dev != DEVICE_MINOR) {
 | 
					 | 
				
			||||||
+		dev_err(inst->dev, "Unknown minor device %d", dev);
 | 
					 | 
				
			||||||
+		ret = -ENXIO;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	return ret;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static const struct vm_operations_struct bcm2835_gpiomem_vm_ops = {
 | 
					 | 
				
			||||||
+#ifdef CONFIG_HAVE_IOREMAP_PROT
 | 
					 | 
				
			||||||
+	.access = generic_access_phys
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int bcm2835_gpiomem_mmap(struct file *file, struct vm_area_struct *vma)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	/* Ignore what the user says - they're getting the GPIO regs
 | 
					 | 
				
			||||||
+	   whether they like it or not! */
 | 
					 | 
				
			||||||
+	unsigned long gpio_page = inst->gpio_regs_phys >> PAGE_SHIFT;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	vma->vm_page_prot = phys_mem_access_prot(file, gpio_page,
 | 
					 | 
				
			||||||
+						 PAGE_SIZE,
 | 
					 | 
				
			||||||
+						 vma->vm_page_prot);
 | 
					 | 
				
			||||||
+	vma->vm_ops = &bcm2835_gpiomem_vm_ops;
 | 
					 | 
				
			||||||
+	if (remap_pfn_range(vma, vma->vm_start,
 | 
					 | 
				
			||||||
+			gpio_page,
 | 
					 | 
				
			||||||
+			PAGE_SIZE,
 | 
					 | 
				
			||||||
+			vma->vm_page_prot)) {
 | 
					 | 
				
			||||||
+		return -EAGAIN;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static const struct file_operations
 | 
					 | 
				
			||||||
+bcm2835_gpiomem_fops = {
 | 
					 | 
				
			||||||
+	.owner = THIS_MODULE,
 | 
					 | 
				
			||||||
+	.open = bcm2835_gpiomem_open,
 | 
					 | 
				
			||||||
+	.release = bcm2835_gpiomem_release,
 | 
					 | 
				
			||||||
+	.mmap = bcm2835_gpiomem_mmap,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+ /****************************************************************************
 | 
					 | 
				
			||||||
+*
 | 
					 | 
				
			||||||
+*   Probe and remove functions
 | 
					 | 
				
			||||||
+*
 | 
					 | 
				
			||||||
+***************************************************************************/
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int bcm2835_gpiomem_probe(struct platform_device *pdev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int err;
 | 
					 | 
				
			||||||
+	void *ptr_err;
 | 
					 | 
				
			||||||
+	struct device *dev = &pdev->dev;
 | 
					 | 
				
			||||||
+	struct resource *ioresource;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* Allocate buffers and instance data */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	inst = kzalloc(sizeof(struct bcm2835_gpiomem_instance), GFP_KERNEL);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (!inst) {
 | 
					 | 
				
			||||||
+		err = -ENOMEM;
 | 
					 | 
				
			||||||
+		goto failed_inst_alloc;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	inst->dev = dev;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 | 
					 | 
				
			||||||
+	if (ioresource) {
 | 
					 | 
				
			||||||
+		inst->gpio_regs_phys = ioresource->start;
 | 
					 | 
				
			||||||
+	} else {
 | 
					 | 
				
			||||||
+		dev_err(inst->dev, "failed to get IO resource");
 | 
					 | 
				
			||||||
+		err = -ENOENT;
 | 
					 | 
				
			||||||
+		goto failed_get_resource;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* Create character device entries */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	err = alloc_chrdev_region(&bcm2835_gpiomem_devid,
 | 
					 | 
				
			||||||
+				  DEVICE_MINOR, 1, DEVICE_NAME);
 | 
					 | 
				
			||||||
+	if (err != 0) {
 | 
					 | 
				
			||||||
+		dev_err(inst->dev, "unable to allocate device number");
 | 
					 | 
				
			||||||
+		goto failed_alloc_chrdev;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	cdev_init(&bcm2835_gpiomem_cdev, &bcm2835_gpiomem_fops);
 | 
					 | 
				
			||||||
+	bcm2835_gpiomem_cdev.owner = THIS_MODULE;
 | 
					 | 
				
			||||||
+	err = cdev_add(&bcm2835_gpiomem_cdev, bcm2835_gpiomem_devid, 1);
 | 
					 | 
				
			||||||
+	if (err != 0) {
 | 
					 | 
				
			||||||
+		dev_err(inst->dev, "unable to register device");
 | 
					 | 
				
			||||||
+		goto failed_cdev_add;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* Create sysfs entries */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	bcm2835_gpiomem_class = class_create(THIS_MODULE, DEVICE_NAME);
 | 
					 | 
				
			||||||
+	ptr_err = bcm2835_gpiomem_class;
 | 
					 | 
				
			||||||
+	if (IS_ERR(ptr_err))
 | 
					 | 
				
			||||||
+		goto failed_class_create;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	bcm2835_gpiomem_dev = device_create(bcm2835_gpiomem_class, NULL,
 | 
					 | 
				
			||||||
+					bcm2835_gpiomem_devid, NULL,
 | 
					 | 
				
			||||||
+					"gpiomem");
 | 
					 | 
				
			||||||
+	ptr_err = bcm2835_gpiomem_dev;
 | 
					 | 
				
			||||||
+	if (IS_ERR(ptr_err))
 | 
					 | 
				
			||||||
+		goto failed_device_create;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	dev_info(inst->dev, "Initialised: Registers at 0x%08lx",
 | 
					 | 
				
			||||||
+		inst->gpio_regs_phys);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+failed_device_create:
 | 
					 | 
				
			||||||
+	class_destroy(bcm2835_gpiomem_class);
 | 
					 | 
				
			||||||
+failed_class_create:
 | 
					 | 
				
			||||||
+	cdev_del(&bcm2835_gpiomem_cdev);
 | 
					 | 
				
			||||||
+	err = PTR_ERR(ptr_err);
 | 
					 | 
				
			||||||
+failed_cdev_add:
 | 
					 | 
				
			||||||
+	unregister_chrdev_region(bcm2835_gpiomem_devid, 1);
 | 
					 | 
				
			||||||
+failed_alloc_chrdev:
 | 
					 | 
				
			||||||
+failed_get_resource:
 | 
					 | 
				
			||||||
+	kfree(inst);
 | 
					 | 
				
			||||||
+failed_inst_alloc:
 | 
					 | 
				
			||||||
+	dev_err(inst->dev, "could not load bcm2835_gpiomem");
 | 
					 | 
				
			||||||
+	return err;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int bcm2835_gpiomem_remove(struct platform_device *pdev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct device *dev = inst->dev;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	kfree(inst);
 | 
					 | 
				
			||||||
+	device_destroy(bcm2835_gpiomem_class, bcm2835_gpiomem_devid);
 | 
					 | 
				
			||||||
+	class_destroy(bcm2835_gpiomem_class);
 | 
					 | 
				
			||||||
+	cdev_del(&bcm2835_gpiomem_cdev);
 | 
					 | 
				
			||||||
+	unregister_chrdev_region(bcm2835_gpiomem_devid, 1);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	dev_info(dev, "GPIO mem driver removed - OK");
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+ /****************************************************************************
 | 
					 | 
				
			||||||
+*
 | 
					 | 
				
			||||||
+*   Register the driver with device tree
 | 
					 | 
				
			||||||
+*
 | 
					 | 
				
			||||||
+***************************************************************************/
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static const struct of_device_id bcm2835_gpiomem_of_match[] = {
 | 
					 | 
				
			||||||
+	{.compatible = "brcm,bcm2835-gpiomem",},
 | 
					 | 
				
			||||||
+	{ /* sentinel */ },
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+MODULE_DEVICE_TABLE(of, bcm2835_gpiomem_of_match);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct platform_driver bcm2835_gpiomem_driver = {
 | 
					 | 
				
			||||||
+	.probe = bcm2835_gpiomem_probe,
 | 
					 | 
				
			||||||
+	.remove = bcm2835_gpiomem_remove,
 | 
					 | 
				
			||||||
+	.driver = {
 | 
					 | 
				
			||||||
+		   .name = DRIVER_NAME,
 | 
					 | 
				
			||||||
+		   .owner = THIS_MODULE,
 | 
					 | 
				
			||||||
+		   .of_match_table = bcm2835_gpiomem_of_match,
 | 
					 | 
				
			||||||
+		   },
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+module_platform_driver(bcm2835_gpiomem_driver);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+MODULE_ALIAS("platform:gpiomem-bcm2835");
 | 
					 | 
				
			||||||
+MODULE_LICENSE("GPL");
 | 
					 | 
				
			||||||
+MODULE_DESCRIPTION("gpiomem driver for accessing GPIO from userspace");
 | 
					 | 
				
			||||||
+MODULE_AUTHOR("Luke Wren <luke@raspberrypi.org>");
 | 
					 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,660 +0,0 @@
 | 
				
			|||||||
From 8e6989f4270ffc86166cbab697b1f539f4cdeb03 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: popcornmix <popcornmix@gmail.com>
 | 
					 | 
				
			||||||
Date: Wed, 17 Jun 2015 15:44:08 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] Add Chris Boot's i2c driver
 | 
					 | 
				
			||||||
MIME-Version: 1.0
 | 
					 | 
				
			||||||
Content-Type: text/plain; charset=UTF-8
 | 
					 | 
				
			||||||
Content-Transfer-Encoding: 8bit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
i2c-bcm2708: fixed baudrate
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Fixed issue where the wrong CDIV value was set for baudrates below 3815 Hz (for 250MHz bus clock).
 | 
					 | 
				
			||||||
In that case the computed CDIV value was more than 0xffff. However the CDIV register width is only 16 bits.
 | 
					 | 
				
			||||||
This resulted in incorrect setting of CDIV and higher baudrate than intended.
 | 
					 | 
				
			||||||
Example: 3500Hz -> CDIV=0x11704 -> CDIV(16bit)=0x1704 -> 42430Hz
 | 
					 | 
				
			||||||
After correction: 3500Hz -> CDIV=0x11704 -> CDIV(16bit)=0xffff -> 3815Hz
 | 
					 | 
				
			||||||
The correct baudrate is shown in the log after the cdiv > 0xffff correction.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Perform I2C combined transactions when possible
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Perform I2C combined transactions whenever possible, within the
 | 
					 | 
				
			||||||
restrictions of the Broadcomm Serial Controller.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Disable DONE interrupt during TA poll
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Prevent interrupt from being triggered if poll is missed and transfer
 | 
					 | 
				
			||||||
starts and finishes.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
i2c: Make combined transactions optional and disabled by default
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
i2c: bcm2708: add device tree support
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add DT support to driver and add to .dtsi file.
 | 
					 | 
				
			||||||
Setup pins in .dts file.
 | 
					 | 
				
			||||||
i2c is disabled by default.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Noralf Tronnes <notro@tronnes.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bcm2708: don't register i2c controllers when using DT
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The devices for the i2c controllers are in the Device Tree.
 | 
					 | 
				
			||||||
Only register devices when not using DT.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Noralf Tronnes <notro@tronnes.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
I2C: Only register the I2C device for the current board revision
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
i2c_bcm2708: Fix clock reference counting
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Fix grabbing lock from atomic context in i2c driver
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
2 main changes:
 | 
					 | 
				
			||||||
- check for timeouts in the bcm2708_bsc_setup function as indicated by this comment:
 | 
					 | 
				
			||||||
      /* poll for transfer start bit (should only take 1-20 polls) */
 | 
					 | 
				
			||||||
  This implies that the setup function can now fail so account for this everywhere it's called
 | 
					 | 
				
			||||||
- Removed the clk_get_rate call from inside the setup function as it locks a mutex and that's not ok since we call it from under a spin lock.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
i2c-bcm2708: When using DT, leave the GPIO setup to pinctrl
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
i2c-bcm2708: Increase timeouts to allow larger transfers
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Use the timeout value provided by the I2C_TIMEOUT ioctl when waiting
 | 
					 | 
				
			||||||
for completion. The default timeout is 1 second.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
See: https://github.com/raspberrypi/linux/issues/260
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
i2c-bcm2708/BCM270X_DT: Add support for I2C2
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The third I2C bus (I2C2) is normally reserved for HDMI use. Careless
 | 
					 | 
				
			||||||
use of this bus can break an attached display - use with caution.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
It is recommended to disable accesses by VideoCore by setting
 | 
					 | 
				
			||||||
hdmi_ignore_edid=1 or hdmi_edid_file=1 in config.txt.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The interface is disabled by default - enable using the
 | 
					 | 
				
			||||||
i2c2_iknowwhatimdoing DT parameter.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bcm2708-spi: Don't use static pin configuration with DT
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Also remove superfluous error checking - the SPI framework ensures the
 | 
					 | 
				
			||||||
validity of the chip_select value.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
i2c-bcm2708: Remove non-DT support
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Set the BSC_CLKT clock streching timeout to 35ms as per SMBus specs.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Fixes i2c_bcm2708: Write to FIFO correctly - v2 (#1574)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
* i2c: fix i2c_bcm2708: Clear FIFO before sending data
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Make sure FIFO gets cleared before trying to send
 | 
					 | 
				
			||||||
data in case of a repeated start (COMBINED=Y).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
* i2c: fix i2c_bcm2708: Only write to FIFO when not full
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Check if FIFO can accept data before writing.
 | 
					 | 
				
			||||||
To avoid a peripheral read on the last iteration of a loop,
 | 
					 | 
				
			||||||
both bcm2708_bsc_fifo_fill and ~drain are changed as well.
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/i2c/busses/Kconfig       |  19 ++
 | 
					 | 
				
			||||||
 drivers/i2c/busses/Makefile      |   2 +
 | 
					 | 
				
			||||||
 drivers/i2c/busses/i2c-bcm2708.c | 512 +++++++++++++++++++++++++++++++
 | 
					 | 
				
			||||||
 3 files changed, 533 insertions(+)
 | 
					 | 
				
			||||||
 create mode 100644 drivers/i2c/busses/i2c-bcm2708.c
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/i2c/busses/Kconfig
 | 
					 | 
				
			||||||
+++ b/drivers/i2c/busses/Kconfig
 | 
					 | 
				
			||||||
@@ -9,6 +9,25 @@ menu "I2C Hardware Bus support"
 | 
					 | 
				
			||||||
 comment "PC SMBus host controller drivers"
 | 
					 | 
				
			||||||
 	depends on PCI
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+config I2C_BCM2708
 | 
					 | 
				
			||||||
+	tristate "BCM2708 BSC"
 | 
					 | 
				
			||||||
+	depends on ARCH_BCM2835
 | 
					 | 
				
			||||||
+	help
 | 
					 | 
				
			||||||
+	  Enabling this option will add BSC (Broadcom Serial Controller)
 | 
					 | 
				
			||||||
+	  support for the BCM2708. BSC is a Broadcom proprietary bus compatible
 | 
					 | 
				
			||||||
+	  with I2C/TWI/SMBus.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+config I2C_BCM2708_BAUDRATE
 | 
					 | 
				
			||||||
+	prompt "BCM2708 I2C baudrate"
 | 
					 | 
				
			||||||
+	depends on I2C_BCM2708
 | 
					 | 
				
			||||||
+	int
 | 
					 | 
				
			||||||
+	default 100000
 | 
					 | 
				
			||||||
+	help
 | 
					 | 
				
			||||||
+	  Set the I2C baudrate. This will alter the default value. A
 | 
					 | 
				
			||||||
+	  different baudrate can be set by using a module parameter as well. If
 | 
					 | 
				
			||||||
+	  no parameter is provided when loading, this is the value that will be
 | 
					 | 
				
			||||||
+	  used.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 config I2C_ALI1535
 | 
					 | 
				
			||||||
 	tristate "ALI 1535"
 | 
					 | 
				
			||||||
 	depends on PCI
 | 
					 | 
				
			||||||
--- a/drivers/i2c/busses/Makefile
 | 
					 | 
				
			||||||
+++ b/drivers/i2c/busses/Makefile
 | 
					 | 
				
			||||||
@@ -3,6 +3,8 @@
 | 
					 | 
				
			||||||
 # Makefile for the i2c bus drivers.
 | 
					 | 
				
			||||||
 #
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+obj-$(CONFIG_I2C_BCM2708)	+= i2c-bcm2708.o
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 # ACPI drivers
 | 
					 | 
				
			||||||
 obj-$(CONFIG_I2C_SCMI)		+= i2c-scmi.o
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/drivers/i2c/busses/i2c-bcm2708.c
 | 
					 | 
				
			||||||
@@ -0,0 +1,512 @@
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ * Driver for Broadcom BCM2708 BSC Controllers
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Copyright (C) 2012 Chris Boot & Frank Buss
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * This driver is inspired by:
 | 
					 | 
				
			||||||
+ * i2c-ocores.c, by Peter Korsgaard <jacmet@sunsite.dk>
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * This program is free software; you can redistribute it and/or modify
 | 
					 | 
				
			||||||
+ * it under the terms of the GNU General Public License as published by
 | 
					 | 
				
			||||||
+ * the Free Software Foundation; either version 2 of the License, or
 | 
					 | 
				
			||||||
+ * (at your option) any later version.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * This program is distributed in the hope that it will be useful,
 | 
					 | 
				
			||||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					 | 
				
			||||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					 | 
				
			||||||
+ * GNU General Public License for more details.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * You should have received a copy of the GNU General Public License
 | 
					 | 
				
			||||||
+ * along with this program; if not, write to the Free Software
 | 
					 | 
				
			||||||
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#include <linux/kernel.h>
 | 
					 | 
				
			||||||
+#include <linux/module.h>
 | 
					 | 
				
			||||||
+#include <linux/spinlock.h>
 | 
					 | 
				
			||||||
+#include <linux/clk.h>
 | 
					 | 
				
			||||||
+#include <linux/err.h>
 | 
					 | 
				
			||||||
+#include <linux/of.h>
 | 
					 | 
				
			||||||
+#include <linux/platform_device.h>
 | 
					 | 
				
			||||||
+#include <linux/io.h>
 | 
					 | 
				
			||||||
+#include <linux/slab.h>
 | 
					 | 
				
			||||||
+#include <linux/i2c.h>
 | 
					 | 
				
			||||||
+#include <linux/interrupt.h>
 | 
					 | 
				
			||||||
+#include <linux/sched.h>
 | 
					 | 
				
			||||||
+#include <linux/wait.h>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* BSC register offsets */
 | 
					 | 
				
			||||||
+#define BSC_C			0x00
 | 
					 | 
				
			||||||
+#define BSC_S			0x04
 | 
					 | 
				
			||||||
+#define BSC_DLEN		0x08
 | 
					 | 
				
			||||||
+#define BSC_A			0x0c
 | 
					 | 
				
			||||||
+#define BSC_FIFO		0x10
 | 
					 | 
				
			||||||
+#define BSC_DIV			0x14
 | 
					 | 
				
			||||||
+#define BSC_DEL			0x18
 | 
					 | 
				
			||||||
+#define BSC_CLKT		0x1c
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* Bitfields in BSC_C */
 | 
					 | 
				
			||||||
+#define BSC_C_I2CEN		0x00008000
 | 
					 | 
				
			||||||
+#define BSC_C_INTR		0x00000400
 | 
					 | 
				
			||||||
+#define BSC_C_INTT		0x00000200
 | 
					 | 
				
			||||||
+#define BSC_C_INTD		0x00000100
 | 
					 | 
				
			||||||
+#define BSC_C_ST		0x00000080
 | 
					 | 
				
			||||||
+#define BSC_C_CLEAR_1		0x00000020
 | 
					 | 
				
			||||||
+#define BSC_C_CLEAR_2		0x00000010
 | 
					 | 
				
			||||||
+#define BSC_C_READ		0x00000001
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* Bitfields in BSC_S */
 | 
					 | 
				
			||||||
+#define BSC_S_CLKT		0x00000200
 | 
					 | 
				
			||||||
+#define BSC_S_ERR		0x00000100
 | 
					 | 
				
			||||||
+#define BSC_S_RXF		0x00000080
 | 
					 | 
				
			||||||
+#define BSC_S_TXE		0x00000040
 | 
					 | 
				
			||||||
+#define BSC_S_RXD		0x00000020
 | 
					 | 
				
			||||||
+#define BSC_S_TXD		0x00000010
 | 
					 | 
				
			||||||
+#define BSC_S_RXR		0x00000008
 | 
					 | 
				
			||||||
+#define BSC_S_TXW		0x00000004
 | 
					 | 
				
			||||||
+#define BSC_S_DONE		0x00000002
 | 
					 | 
				
			||||||
+#define BSC_S_TA		0x00000001
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define I2C_WAIT_LOOP_COUNT	200
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define DRV_NAME		"bcm2708_i2c"
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static unsigned int baudrate;
 | 
					 | 
				
			||||||
+module_param(baudrate, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
 | 
					 | 
				
			||||||
+MODULE_PARM_DESC(baudrate, "The I2C baudrate");
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static bool combined = false;
 | 
					 | 
				
			||||||
+module_param(combined, bool, 0644);
 | 
					 | 
				
			||||||
+MODULE_PARM_DESC(combined, "Use combined transactions");
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+struct bcm2708_i2c {
 | 
					 | 
				
			||||||
+	struct i2c_adapter adapter;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	spinlock_t lock;
 | 
					 | 
				
			||||||
+	void __iomem *base;
 | 
					 | 
				
			||||||
+	int irq;
 | 
					 | 
				
			||||||
+	struct clk *clk;
 | 
					 | 
				
			||||||
+	u32 cdiv;
 | 
					 | 
				
			||||||
+	u32 clk_tout;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	struct completion done;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	struct i2c_msg *msg;
 | 
					 | 
				
			||||||
+	int pos;
 | 
					 | 
				
			||||||
+	int nmsgs;
 | 
					 | 
				
			||||||
+	bool error;
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static inline u32 bcm2708_rd(struct bcm2708_i2c *bi, unsigned reg)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return readl(bi->base + reg);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static inline void bcm2708_wr(struct bcm2708_i2c *bi, unsigned reg, u32 val)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	writel(val, bi->base + reg);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static inline void bcm2708_bsc_reset(struct bcm2708_i2c *bi)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	bcm2708_wr(bi, BSC_C, 0);
 | 
					 | 
				
			||||||
+	bcm2708_wr(bi, BSC_S, BSC_S_CLKT | BSC_S_ERR | BSC_S_DONE);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static inline void bcm2708_bsc_fifo_drain(struct bcm2708_i2c *bi)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	while ((bi->pos < bi->msg->len) && (bcm2708_rd(bi, BSC_S) & BSC_S_RXD))
 | 
					 | 
				
			||||||
+		bi->msg->buf[bi->pos++] = bcm2708_rd(bi, BSC_FIFO);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static inline void bcm2708_bsc_fifo_fill(struct bcm2708_i2c *bi)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	while ((bi->pos < bi->msg->len) && (bcm2708_rd(bi, BSC_S) & BSC_S_TXD))
 | 
					 | 
				
			||||||
+		bcm2708_wr(bi, BSC_FIFO, bi->msg->buf[bi->pos++]);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static inline int bcm2708_bsc_setup(struct bcm2708_i2c *bi)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	u32 cdiv, s, clk_tout;
 | 
					 | 
				
			||||||
+	u32 c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_ST | BSC_C_CLEAR_1;
 | 
					 | 
				
			||||||
+	int wait_loops = I2C_WAIT_LOOP_COUNT;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* Can't call clk_get_rate as it locks a mutex and here we are spinlocked.
 | 
					 | 
				
			||||||
+	 * Use the value that we cached in the probe.
 | 
					 | 
				
			||||||
+	 */
 | 
					 | 
				
			||||||
+	cdiv = bi->cdiv;
 | 
					 | 
				
			||||||
+	clk_tout = bi->clk_tout;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (bi->msg->flags & I2C_M_RD)
 | 
					 | 
				
			||||||
+		c |= BSC_C_INTR | BSC_C_READ;
 | 
					 | 
				
			||||||
+	else
 | 
					 | 
				
			||||||
+		c |= BSC_C_INTT;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	bcm2708_wr(bi, BSC_CLKT, clk_tout);
 | 
					 | 
				
			||||||
+	bcm2708_wr(bi, BSC_DIV, cdiv);
 | 
					 | 
				
			||||||
+	bcm2708_wr(bi, BSC_A, bi->msg->addr);
 | 
					 | 
				
			||||||
+	bcm2708_wr(bi, BSC_DLEN, bi->msg->len);
 | 
					 | 
				
			||||||
+	if (combined)
 | 
					 | 
				
			||||||
+	{
 | 
					 | 
				
			||||||
+		/* Do the next two messages meet combined transaction criteria?
 | 
					 | 
				
			||||||
+		   - Current message is a write, next message is a read
 | 
					 | 
				
			||||||
+		   - Both messages to same slave address
 | 
					 | 
				
			||||||
+		   - Write message can fit inside FIFO (16 bytes or less) */
 | 
					 | 
				
			||||||
+		if ( (bi->nmsgs > 1) &&
 | 
					 | 
				
			||||||
+			!(bi->msg[0].flags & I2C_M_RD) && (bi->msg[1].flags & I2C_M_RD) &&
 | 
					 | 
				
			||||||
+			 (bi->msg[0].addr == bi->msg[1].addr) && (bi->msg[0].len <= 16)) {
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+			/* Clear FIFO */
 | 
					 | 
				
			||||||
+			bcm2708_wr(bi, BSC_C, BSC_C_CLEAR_1);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+			/* Fill FIFO with entire write message (16 byte FIFO) */
 | 
					 | 
				
			||||||
+			while (bi->pos < bi->msg->len) {
 | 
					 | 
				
			||||||
+				bcm2708_wr(bi, BSC_FIFO, bi->msg->buf[bi->pos++]);
 | 
					 | 
				
			||||||
+			}
 | 
					 | 
				
			||||||
+			/* Start write transfer (no interrupts, don't clear FIFO) */
 | 
					 | 
				
			||||||
+			bcm2708_wr(bi, BSC_C, BSC_C_I2CEN | BSC_C_ST);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+			/* poll for transfer start bit (should only take 1-20 polls) */
 | 
					 | 
				
			||||||
+			do {
 | 
					 | 
				
			||||||
+				s = bcm2708_rd(bi, BSC_S);
 | 
					 | 
				
			||||||
+			} while (!(s & (BSC_S_TA | BSC_S_ERR | BSC_S_CLKT | BSC_S_DONE)) && --wait_loops >= 0);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+			/* did we time out or some error occured? */
 | 
					 | 
				
			||||||
+			if (wait_loops < 0 || (s & (BSC_S_ERR | BSC_S_CLKT))) {
 | 
					 | 
				
			||||||
+				return -1;
 | 
					 | 
				
			||||||
+			}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+			/* Send next read message before the write transfer finishes. */
 | 
					 | 
				
			||||||
+			bi->nmsgs--;
 | 
					 | 
				
			||||||
+			bi->msg++;
 | 
					 | 
				
			||||||
+			bi->pos = 0;
 | 
					 | 
				
			||||||
+			bcm2708_wr(bi, BSC_DLEN, bi->msg->len);
 | 
					 | 
				
			||||||
+			c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_INTR | BSC_C_ST | BSC_C_READ;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	bcm2708_wr(bi, BSC_C, c);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static irqreturn_t bcm2708_i2c_interrupt(int irq, void *dev_id)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct bcm2708_i2c *bi = dev_id;
 | 
					 | 
				
			||||||
+	bool handled = true;
 | 
					 | 
				
			||||||
+	u32 s;
 | 
					 | 
				
			||||||
+	int ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	spin_lock(&bi->lock);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* we may see camera interrupts on the "other" I2C channel
 | 
					 | 
				
			||||||
+		   Just return if we've not sent anything */
 | 
					 | 
				
			||||||
+	if (!bi->nmsgs || !bi->msg) {
 | 
					 | 
				
			||||||
+		goto early_exit;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	s = bcm2708_rd(bi, BSC_S);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (s & (BSC_S_CLKT | BSC_S_ERR)) {
 | 
					 | 
				
			||||||
+		bcm2708_bsc_reset(bi);
 | 
					 | 
				
			||||||
+		bi->error = true;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		bi->msg = 0; /* to inform the that all work is done */
 | 
					 | 
				
			||||||
+		bi->nmsgs = 0;
 | 
					 | 
				
			||||||
+		/* wake up our bh */
 | 
					 | 
				
			||||||
+		complete(&bi->done);
 | 
					 | 
				
			||||||
+	} else if (s & BSC_S_DONE) {
 | 
					 | 
				
			||||||
+		bi->nmsgs--;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		if (bi->msg->flags & I2C_M_RD) {
 | 
					 | 
				
			||||||
+			bcm2708_bsc_fifo_drain(bi);
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		bcm2708_bsc_reset(bi);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		if (bi->nmsgs) {
 | 
					 | 
				
			||||||
+			/* advance to next message */
 | 
					 | 
				
			||||||
+			bi->msg++;
 | 
					 | 
				
			||||||
+			bi->pos = 0;
 | 
					 | 
				
			||||||
+			ret = bcm2708_bsc_setup(bi);
 | 
					 | 
				
			||||||
+			if (ret < 0) {
 | 
					 | 
				
			||||||
+				bcm2708_bsc_reset(bi);
 | 
					 | 
				
			||||||
+				bi->error = true;
 | 
					 | 
				
			||||||
+				bi->msg = 0; /* to inform the that all work is done */
 | 
					 | 
				
			||||||
+				bi->nmsgs = 0;
 | 
					 | 
				
			||||||
+				/* wake up our bh */
 | 
					 | 
				
			||||||
+				complete(&bi->done);
 | 
					 | 
				
			||||||
+				goto early_exit;
 | 
					 | 
				
			||||||
+			}
 | 
					 | 
				
			||||||
+		} else {
 | 
					 | 
				
			||||||
+			bi->msg = 0; /* to inform the that all work is done */
 | 
					 | 
				
			||||||
+			bi->nmsgs = 0;
 | 
					 | 
				
			||||||
+			/* wake up our bh */
 | 
					 | 
				
			||||||
+			complete(&bi->done);
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+	} else if (s & BSC_S_TXW) {
 | 
					 | 
				
			||||||
+		bcm2708_bsc_fifo_fill(bi);
 | 
					 | 
				
			||||||
+	} else if (s & BSC_S_RXR) {
 | 
					 | 
				
			||||||
+		bcm2708_bsc_fifo_drain(bi);
 | 
					 | 
				
			||||||
+	} else {
 | 
					 | 
				
			||||||
+		handled = false;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+early_exit:
 | 
					 | 
				
			||||||
+	spin_unlock(&bi->lock);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return handled ? IRQ_HANDLED : IRQ_NONE;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int bcm2708_i2c_master_xfer(struct i2c_adapter *adap,
 | 
					 | 
				
			||||||
+	struct i2c_msg *msgs, int num)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct bcm2708_i2c *bi = adap->algo_data;
 | 
					 | 
				
			||||||
+	unsigned long flags;
 | 
					 | 
				
			||||||
+	int ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	spin_lock_irqsave(&bi->lock, flags);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	reinit_completion(&bi->done);
 | 
					 | 
				
			||||||
+	bi->msg = msgs;
 | 
					 | 
				
			||||||
+	bi->pos = 0;
 | 
					 | 
				
			||||||
+	bi->nmsgs = num;
 | 
					 | 
				
			||||||
+	bi->error = false;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ret = bcm2708_bsc_setup(bi);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	spin_unlock_irqrestore(&bi->lock, flags);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* check the result of the setup */
 | 
					 | 
				
			||||||
+	if (ret < 0)
 | 
					 | 
				
			||||||
+	{
 | 
					 | 
				
			||||||
+		dev_err(&adap->dev, "transfer setup timed out\n");
 | 
					 | 
				
			||||||
+		goto error_timeout;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ret = wait_for_completion_timeout(&bi->done, adap->timeout);
 | 
					 | 
				
			||||||
+	if (ret == 0) {
 | 
					 | 
				
			||||||
+		dev_err(&adap->dev, "transfer timed out\n");
 | 
					 | 
				
			||||||
+		goto error_timeout;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ret = bi->error ? -EIO : num;
 | 
					 | 
				
			||||||
+	return ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+error_timeout:
 | 
					 | 
				
			||||||
+	spin_lock_irqsave(&bi->lock, flags);
 | 
					 | 
				
			||||||
+	bcm2708_bsc_reset(bi);
 | 
					 | 
				
			||||||
+	bi->msg = 0; /* to inform the interrupt handler that there's nothing else to be done */
 | 
					 | 
				
			||||||
+	bi->nmsgs = 0;
 | 
					 | 
				
			||||||
+	spin_unlock_irqrestore(&bi->lock, flags);
 | 
					 | 
				
			||||||
+	return -ETIMEDOUT;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static u32 bcm2708_i2c_functionality(struct i2c_adapter *adap)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return I2C_FUNC_I2C | /*I2C_FUNC_10BIT_ADDR |*/ I2C_FUNC_SMBUS_EMUL;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct i2c_algorithm bcm2708_i2c_algorithm = {
 | 
					 | 
				
			||||||
+	.master_xfer = bcm2708_i2c_master_xfer,
 | 
					 | 
				
			||||||
+	.functionality = bcm2708_i2c_functionality,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int bcm2708_i2c_probe(struct platform_device *pdev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct resource *regs;
 | 
					 | 
				
			||||||
+	int irq, err = -ENOMEM;
 | 
					 | 
				
			||||||
+	struct clk *clk;
 | 
					 | 
				
			||||||
+	struct bcm2708_i2c *bi;
 | 
					 | 
				
			||||||
+	struct i2c_adapter *adap;
 | 
					 | 
				
			||||||
+	unsigned long bus_hz;
 | 
					 | 
				
			||||||
+	u32 cdiv, clk_tout;
 | 
					 | 
				
			||||||
+	u32 baud;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	baud = CONFIG_I2C_BCM2708_BAUDRATE;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (pdev->dev.of_node) {
 | 
					 | 
				
			||||||
+		u32 bus_clk_rate;
 | 
					 | 
				
			||||||
+		pdev->id = of_alias_get_id(pdev->dev.of_node, "i2c");
 | 
					 | 
				
			||||||
+		if (pdev->id < 0) {
 | 
					 | 
				
			||||||
+			dev_err(&pdev->dev, "alias is missing\n");
 | 
					 | 
				
			||||||
+			return -EINVAL;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+		if (!of_property_read_u32(pdev->dev.of_node,
 | 
					 | 
				
			||||||
+					"clock-frequency", &bus_clk_rate))
 | 
					 | 
				
			||||||
+			baud = bus_clk_rate;
 | 
					 | 
				
			||||||
+		else
 | 
					 | 
				
			||||||
+			dev_warn(&pdev->dev,
 | 
					 | 
				
			||||||
+				"Could not read clock-frequency property\n");
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (baudrate)
 | 
					 | 
				
			||||||
+		baud = baudrate;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 | 
					 | 
				
			||||||
+	if (!regs) {
 | 
					 | 
				
			||||||
+		dev_err(&pdev->dev, "could not get IO memory\n");
 | 
					 | 
				
			||||||
+		return -ENXIO;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	irq = platform_get_irq(pdev, 0);
 | 
					 | 
				
			||||||
+	if (irq < 0) {
 | 
					 | 
				
			||||||
+		dev_err(&pdev->dev, "could not get IRQ\n");
 | 
					 | 
				
			||||||
+		return irq;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	clk = clk_get(&pdev->dev, NULL);
 | 
					 | 
				
			||||||
+	if (IS_ERR(clk)) {
 | 
					 | 
				
			||||||
+		dev_err(&pdev->dev, "could not find clk: %ld\n", PTR_ERR(clk));
 | 
					 | 
				
			||||||
+		return PTR_ERR(clk);
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	err = clk_prepare_enable(clk);
 | 
					 | 
				
			||||||
+	if (err) {
 | 
					 | 
				
			||||||
+		dev_err(&pdev->dev, "could not enable clk: %d\n", err);
 | 
					 | 
				
			||||||
+		goto out_clk_put;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	bi = kzalloc(sizeof(*bi), GFP_KERNEL);
 | 
					 | 
				
			||||||
+	if (!bi)
 | 
					 | 
				
			||||||
+		goto out_clk_disable;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	platform_set_drvdata(pdev, bi);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	adap = &bi->adapter;
 | 
					 | 
				
			||||||
+	adap->class = I2C_CLASS_HWMON | I2C_CLASS_DDC;
 | 
					 | 
				
			||||||
+	adap->algo = &bcm2708_i2c_algorithm;
 | 
					 | 
				
			||||||
+	adap->algo_data = bi;
 | 
					 | 
				
			||||||
+	adap->dev.parent = &pdev->dev;
 | 
					 | 
				
			||||||
+	adap->nr = pdev->id;
 | 
					 | 
				
			||||||
+	strlcpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name));
 | 
					 | 
				
			||||||
+	adap->dev.of_node = pdev->dev.of_node;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	switch (pdev->id) {
 | 
					 | 
				
			||||||
+	case 0:
 | 
					 | 
				
			||||||
+		adap->class = I2C_CLASS_HWMON;
 | 
					 | 
				
			||||||
+		break;
 | 
					 | 
				
			||||||
+	case 1:
 | 
					 | 
				
			||||||
+		adap->class = I2C_CLASS_DDC;
 | 
					 | 
				
			||||||
+		break;
 | 
					 | 
				
			||||||
+	case 2:
 | 
					 | 
				
			||||||
+		adap->class = I2C_CLASS_DDC;
 | 
					 | 
				
			||||||
+		break;
 | 
					 | 
				
			||||||
+	default:
 | 
					 | 
				
			||||||
+		dev_err(&pdev->dev, "can only bind to BSC 0, 1 or 2\n");
 | 
					 | 
				
			||||||
+		err = -ENXIO;
 | 
					 | 
				
			||||||
+		goto out_free_bi;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	spin_lock_init(&bi->lock);
 | 
					 | 
				
			||||||
+	init_completion(&bi->done);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	bi->base = ioremap(regs->start, resource_size(regs));
 | 
					 | 
				
			||||||
+	if (!bi->base) {
 | 
					 | 
				
			||||||
+		dev_err(&pdev->dev, "could not remap memory\n");
 | 
					 | 
				
			||||||
+		goto out_free_bi;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	bi->irq = irq;
 | 
					 | 
				
			||||||
+	bi->clk = clk;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	err = request_irq(irq, bcm2708_i2c_interrupt, IRQF_SHARED,
 | 
					 | 
				
			||||||
+			dev_name(&pdev->dev), bi);
 | 
					 | 
				
			||||||
+	if (err) {
 | 
					 | 
				
			||||||
+		dev_err(&pdev->dev, "could not request IRQ: %d\n", err);
 | 
					 | 
				
			||||||
+		goto out_iounmap;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	bcm2708_bsc_reset(bi);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	err = i2c_add_numbered_adapter(adap);
 | 
					 | 
				
			||||||
+	if (err < 0) {
 | 
					 | 
				
			||||||
+		dev_err(&pdev->dev, "could not add I2C adapter: %d\n", err);
 | 
					 | 
				
			||||||
+		goto out_free_irq;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	bus_hz = clk_get_rate(bi->clk);
 | 
					 | 
				
			||||||
+	cdiv = bus_hz / baud;
 | 
					 | 
				
			||||||
+	if (cdiv > 0xffff) {
 | 
					 | 
				
			||||||
+		cdiv = 0xffff;
 | 
					 | 
				
			||||||
+		baud = bus_hz / cdiv;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	clk_tout = 35/1000*baud; //35ms timeout as per SMBus specs.
 | 
					 | 
				
			||||||
+	if (clk_tout > 0xffff)
 | 
					 | 
				
			||||||
+		clk_tout = 0xffff;
 | 
					 | 
				
			||||||
+	
 | 
					 | 
				
			||||||
+	bi->cdiv = cdiv;
 | 
					 | 
				
			||||||
+	bi->clk_tout = clk_tout;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	dev_info(&pdev->dev, "BSC%d Controller at 0x%08lx (irq %d) (baudrate %d)\n",
 | 
					 | 
				
			||||||
+		pdev->id, (unsigned long)regs->start, irq, baud);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+out_free_irq:
 | 
					 | 
				
			||||||
+	free_irq(bi->irq, bi);
 | 
					 | 
				
			||||||
+out_iounmap:
 | 
					 | 
				
			||||||
+	iounmap(bi->base);
 | 
					 | 
				
			||||||
+out_free_bi:
 | 
					 | 
				
			||||||
+	kfree(bi);
 | 
					 | 
				
			||||||
+out_clk_disable:
 | 
					 | 
				
			||||||
+	clk_disable_unprepare(clk);
 | 
					 | 
				
			||||||
+out_clk_put:
 | 
					 | 
				
			||||||
+	clk_put(clk);
 | 
					 | 
				
			||||||
+	return err;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int bcm2708_i2c_remove(struct platform_device *pdev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct bcm2708_i2c *bi = platform_get_drvdata(pdev);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	platform_set_drvdata(pdev, NULL);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	i2c_del_adapter(&bi->adapter);
 | 
					 | 
				
			||||||
+	free_irq(bi->irq, bi);
 | 
					 | 
				
			||||||
+	iounmap(bi->base);
 | 
					 | 
				
			||||||
+	clk_disable_unprepare(bi->clk);
 | 
					 | 
				
			||||||
+	clk_put(bi->clk);
 | 
					 | 
				
			||||||
+	kfree(bi);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static const struct of_device_id bcm2708_i2c_of_match[] = {
 | 
					 | 
				
			||||||
+        { .compatible = "brcm,bcm2708-i2c" },
 | 
					 | 
				
			||||||
+        {},
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+MODULE_DEVICE_TABLE(of, bcm2708_i2c_of_match);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct platform_driver bcm2708_i2c_driver = {
 | 
					 | 
				
			||||||
+	.driver		= {
 | 
					 | 
				
			||||||
+		.name	= DRV_NAME,
 | 
					 | 
				
			||||||
+		.owner	= THIS_MODULE,
 | 
					 | 
				
			||||||
+		.of_match_table = bcm2708_i2c_of_match,
 | 
					 | 
				
			||||||
+	},
 | 
					 | 
				
			||||||
+	.probe		= bcm2708_i2c_probe,
 | 
					 | 
				
			||||||
+	.remove		= bcm2708_i2c_remove,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+// module_platform_driver(bcm2708_i2c_driver);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int __init bcm2708_i2c_init(void)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return platform_driver_register(&bcm2708_i2c_driver);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void __exit bcm2708_i2c_exit(void)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	platform_driver_unregister(&bcm2708_i2c_driver);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+module_init(bcm2708_i2c_init);
 | 
					 | 
				
			||||||
+module_exit(bcm2708_i2c_exit);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+MODULE_DESCRIPTION("BSC controller driver for Broadcom BCM2708");
 | 
					 | 
				
			||||||
+MODULE_AUTHOR("Chris Boot <bootc@bootc.net>");
 | 
					 | 
				
			||||||
+MODULE_LICENSE("GPL v2");
 | 
					 | 
				
			||||||
+MODULE_ALIAS("platform:" DRV_NAME);
 | 
					 | 
				
			||||||
@ -1,254 +0,0 @@
 | 
				
			|||||||
From ee17bae9c9f8fbe02644f13c01c6dd06e9c4bbc3 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= <noralf@tronnes.org>
 | 
					 | 
				
			||||||
Date: Fri, 26 Jun 2015 14:27:06 +0200
 | 
					 | 
				
			||||||
Subject: [PATCH] char: broadcom: Add vcio module
 | 
					 | 
				
			||||||
MIME-Version: 1.0
 | 
					 | 
				
			||||||
Content-Type: text/plain; charset=UTF-8
 | 
					 | 
				
			||||||
Content-Transfer-Encoding: 8bit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add module for accessing the mailbox property channel through
 | 
					 | 
				
			||||||
/dev/vcio. Was previously in bcm2708-vcio.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
char: vcio: Add compat ioctl handling
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
There was no compat ioctl handler, so 32 bit userspace on a
 | 
					 | 
				
			||||||
64 bit kernel failed as IOCTL_MBOX_PROPERTY used the size
 | 
					 | 
				
			||||||
of char*.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
char: vcio: Fail probe if rpi_firmware is not found.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Device Tree is now the only supported config mechanism, therefore
 | 
					 | 
				
			||||||
uncomment the block of code that fails the probe if the
 | 
					 | 
				
			||||||
firmware node can't be found.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/char/broadcom/Kconfig  |   6 +
 | 
					 | 
				
			||||||
 drivers/char/broadcom/Makefile |   1 +
 | 
					 | 
				
			||||||
 drivers/char/broadcom/vcio.c   | 194 +++++++++++++++++++++++++++++++++
 | 
					 | 
				
			||||||
 3 files changed, 201 insertions(+)
 | 
					 | 
				
			||||||
 create mode 100644 drivers/char/broadcom/vcio.c
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/char/broadcom/Kconfig
 | 
					 | 
				
			||||||
+++ b/drivers/char/broadcom/Kconfig
 | 
					 | 
				
			||||||
@@ -15,6 +15,12 @@ config BCM2708_VCMEM
 | 
					 | 
				
			||||||
         help
 | 
					 | 
				
			||||||
           Helper for videocore memory access and total size allocation.
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+config BCM_VCIO
 | 
					 | 
				
			||||||
+	tristate "Mailbox userspace access"
 | 
					 | 
				
			||||||
+	depends on BCM2835_MBOX
 | 
					 | 
				
			||||||
+	help
 | 
					 | 
				
			||||||
+	  Gives access to the mailbox property channel from userspace.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 endif
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 config BCM2835_DEVGPIOMEM
 | 
					 | 
				
			||||||
--- a/drivers/char/broadcom/Makefile
 | 
					 | 
				
			||||||
+++ b/drivers/char/broadcom/Makefile
 | 
					 | 
				
			||||||
@@ -1,3 +1,4 @@
 | 
					 | 
				
			||||||
 obj-$(CONFIG_BCM2708_VCMEM)	+= vc_mem.o
 | 
					 | 
				
			||||||
+obj-$(CONFIG_BCM_VCIO)		+= vcio.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_BCM2835_DEVGPIOMEM)+= bcm2835-gpiomem.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_BCM2835_SMI_DEV)	+= bcm2835_smi_dev.o
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/drivers/char/broadcom/vcio.c
 | 
					 | 
				
			||||||
@@ -0,0 +1,194 @@
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ *  Copyright (C) 2010 Broadcom
 | 
					 | 
				
			||||||
+ *  Copyright (C) 2015 Noralf Trønnes
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * 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.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#include <linux/cdev.h>
 | 
					 | 
				
			||||||
+#include <linux/device.h>
 | 
					 | 
				
			||||||
+#include <linux/fs.h>
 | 
					 | 
				
			||||||
+#include <linux/init.h>
 | 
					 | 
				
			||||||
+#include <linux/ioctl.h>
 | 
					 | 
				
			||||||
+#include <linux/module.h>
 | 
					 | 
				
			||||||
+#include <linux/slab.h>
 | 
					 | 
				
			||||||
+#include <linux/uaccess.h>
 | 
					 | 
				
			||||||
+#include <soc/bcm2835/raspberrypi-firmware.h>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define MBOX_CHAN_PROPERTY 8
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define VCIO_IOC_MAGIC 100
 | 
					 | 
				
			||||||
+#define IOCTL_MBOX_PROPERTY _IOWR(VCIO_IOC_MAGIC, 0, char *)
 | 
					 | 
				
			||||||
+#ifdef CONFIG_COMPAT
 | 
					 | 
				
			||||||
+#define IOCTL_MBOX_PROPERTY32 _IOWR(VCIO_IOC_MAGIC, 0, compat_uptr_t)
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct {
 | 
					 | 
				
			||||||
+	dev_t devt;
 | 
					 | 
				
			||||||
+	struct cdev cdev;
 | 
					 | 
				
			||||||
+	struct class *class;
 | 
					 | 
				
			||||||
+	struct rpi_firmware *fw;
 | 
					 | 
				
			||||||
+} vcio;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int vcio_user_property_list(void *user)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	u32 *buf, size;
 | 
					 | 
				
			||||||
+	int ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* The first 32-bit is the size of the buffer */
 | 
					 | 
				
			||||||
+	if (copy_from_user(&size, user, sizeof(size)))
 | 
					 | 
				
			||||||
+		return -EFAULT;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	buf = kmalloc(size, GFP_KERNEL);
 | 
					 | 
				
			||||||
+	if (!buf)
 | 
					 | 
				
			||||||
+		return -ENOMEM;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (copy_from_user(buf, user, size)) {
 | 
					 | 
				
			||||||
+		kfree(buf);
 | 
					 | 
				
			||||||
+		return -EFAULT;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* Strip off protocol encapsulation */
 | 
					 | 
				
			||||||
+	ret = rpi_firmware_property_list(vcio.fw, &buf[2], size - 12);
 | 
					 | 
				
			||||||
+	if (ret) {
 | 
					 | 
				
			||||||
+		kfree(buf);
 | 
					 | 
				
			||||||
+		return ret;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	buf[1] = RPI_FIRMWARE_STATUS_SUCCESS;
 | 
					 | 
				
			||||||
+	if (copy_to_user(user, buf, size))
 | 
					 | 
				
			||||||
+		ret = -EFAULT;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	kfree(buf);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return ret;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int vcio_device_open(struct inode *inode, struct file *file)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	try_module_get(THIS_MODULE);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int vcio_device_release(struct inode *inode, struct file *file)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	module_put(THIS_MODULE);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static long vcio_device_ioctl(struct file *file, unsigned int ioctl_num,
 | 
					 | 
				
			||||||
+			      unsigned long ioctl_param)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	switch (ioctl_num) {
 | 
					 | 
				
			||||||
+	case IOCTL_MBOX_PROPERTY:
 | 
					 | 
				
			||||||
+		return vcio_user_property_list((void *)ioctl_param);
 | 
					 | 
				
			||||||
+	default:
 | 
					 | 
				
			||||||
+		pr_err("unknown ioctl: %x\n", ioctl_num);
 | 
					 | 
				
			||||||
+		return -EINVAL;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#ifdef CONFIG_COMPAT
 | 
					 | 
				
			||||||
+static long vcio_device_compat_ioctl(struct file *file, unsigned int ioctl_num,
 | 
					 | 
				
			||||||
+				     unsigned long ioctl_param)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	switch (ioctl_num) {
 | 
					 | 
				
			||||||
+	case IOCTL_MBOX_PROPERTY32:
 | 
					 | 
				
			||||||
+		return vcio_user_property_list(compat_ptr(ioctl_param));
 | 
					 | 
				
			||||||
+	default:
 | 
					 | 
				
			||||||
+		pr_err("unknown ioctl: %x\n", ioctl_num);
 | 
					 | 
				
			||||||
+		return -EINVAL;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+const struct file_operations vcio_fops = {
 | 
					 | 
				
			||||||
+	.unlocked_ioctl = vcio_device_ioctl,
 | 
					 | 
				
			||||||
+#ifdef CONFIG_COMPAT
 | 
					 | 
				
			||||||
+	.compat_ioctl = vcio_device_compat_ioctl,
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
+	.open = vcio_device_open,
 | 
					 | 
				
			||||||
+	.release = vcio_device_release,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int __init vcio_init(void)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct device_node *np;
 | 
					 | 
				
			||||||
+	static struct device *dev;
 | 
					 | 
				
			||||||
+	int ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	np = of_find_compatible_node(NULL, NULL,
 | 
					 | 
				
			||||||
+				     "raspberrypi,bcm2835-firmware");
 | 
					 | 
				
			||||||
+	if (!of_device_is_available(np))
 | 
					 | 
				
			||||||
+		return -ENODEV;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	vcio.fw = rpi_firmware_get(np);
 | 
					 | 
				
			||||||
+	if (!vcio.fw)
 | 
					 | 
				
			||||||
+		return -ENODEV;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ret = alloc_chrdev_region(&vcio.devt, 0, 1, "vcio");
 | 
					 | 
				
			||||||
+	if (ret) {
 | 
					 | 
				
			||||||
+		pr_err("failed to allocate device number\n");
 | 
					 | 
				
			||||||
+		return ret;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	cdev_init(&vcio.cdev, &vcio_fops);
 | 
					 | 
				
			||||||
+	vcio.cdev.owner = THIS_MODULE;
 | 
					 | 
				
			||||||
+	ret = cdev_add(&vcio.cdev, vcio.devt, 1);
 | 
					 | 
				
			||||||
+	if (ret) {
 | 
					 | 
				
			||||||
+		pr_err("failed to register device\n");
 | 
					 | 
				
			||||||
+		goto err_unregister_chardev;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/*
 | 
					 | 
				
			||||||
+	 * Create sysfs entries
 | 
					 | 
				
			||||||
+	 * 'bcm2708_vcio' is used for backwards compatibility so we don't break
 | 
					 | 
				
			||||||
+	 * userspace. Raspian has a udev rule that changes the permissions.
 | 
					 | 
				
			||||||
+	 */
 | 
					 | 
				
			||||||
+	vcio.class = class_create(THIS_MODULE, "bcm2708_vcio");
 | 
					 | 
				
			||||||
+	if (IS_ERR(vcio.class)) {
 | 
					 | 
				
			||||||
+		ret = PTR_ERR(vcio.class);
 | 
					 | 
				
			||||||
+		pr_err("failed to create class\n");
 | 
					 | 
				
			||||||
+		goto err_cdev_del;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	dev = device_create(vcio.class, NULL, vcio.devt, NULL, "vcio");
 | 
					 | 
				
			||||||
+	if (IS_ERR(dev)) {
 | 
					 | 
				
			||||||
+		ret = PTR_ERR(dev);
 | 
					 | 
				
			||||||
+		pr_err("failed to create device\n");
 | 
					 | 
				
			||||||
+		goto err_class_destroy;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+err_class_destroy:
 | 
					 | 
				
			||||||
+	class_destroy(vcio.class);
 | 
					 | 
				
			||||||
+err_cdev_del:
 | 
					 | 
				
			||||||
+	cdev_del(&vcio.cdev);
 | 
					 | 
				
			||||||
+err_unregister_chardev:
 | 
					 | 
				
			||||||
+	unregister_chrdev_region(vcio.devt, 1);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return ret;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+module_init(vcio_init);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void __exit vcio_exit(void)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	device_destroy(vcio.class, vcio.devt);
 | 
					 | 
				
			||||||
+	class_destroy(vcio.class);
 | 
					 | 
				
			||||||
+	cdev_del(&vcio.cdev);
 | 
					 | 
				
			||||||
+	unregister_chrdev_region(vcio.devt, 1);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+module_exit(vcio_exit);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+MODULE_AUTHOR("Gray Girling");
 | 
					 | 
				
			||||||
+MODULE_AUTHOR("Noralf Trønnes");
 | 
					 | 
				
			||||||
+MODULE_DESCRIPTION("Mailbox userspace access");
 | 
					 | 
				
			||||||
+MODULE_LICENSE("GPL");
 | 
					 | 
				
			||||||
@ -1,97 +0,0 @@
 | 
				
			|||||||
From 410ae9727ad520a140b88c7151e0432d58516bc3 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= <noralf@tronnes.org>
 | 
					 | 
				
			||||||
Date: Fri, 26 Jun 2015 14:25:01 +0200
 | 
					 | 
				
			||||||
Subject: [PATCH] firmware: bcm2835: Support ARCH_BCM270x
 | 
					 | 
				
			||||||
MIME-Version: 1.0
 | 
					 | 
				
			||||||
Content-Type: text/plain; charset=UTF-8
 | 
					 | 
				
			||||||
Content-Transfer-Encoding: 8bit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Support booting without Device Tree.
 | 
					 | 
				
			||||||
Turn on USB power.
 | 
					 | 
				
			||||||
Load driver early because of lacking support for deferred probing
 | 
					 | 
				
			||||||
in many drivers.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
firmware: bcm2835: Don't turn on USB power
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The raspberrypi-power driver is now used to turn on USB power.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This partly reverts commit:
 | 
					 | 
				
			||||||
firmware: bcm2835: Support ARCH_BCM270x
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/firmware/raspberrypi.c | 19 +++++++++++++++++--
 | 
					 | 
				
			||||||
 1 file changed, 17 insertions(+), 2 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/firmware/raspberrypi.c
 | 
					 | 
				
			||||||
+++ b/drivers/firmware/raspberrypi.c
 | 
					 | 
				
			||||||
@@ -32,6 +32,8 @@ struct rpi_firmware {
 | 
					 | 
				
			||||||
 	struct kref consumers;
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static struct platform_device *g_pdev;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static DEFINE_MUTEX(transaction_lock);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static void response_callback(struct mbox_client *cl, void *msg)
 | 
					 | 
				
			||||||
@@ -272,6 +274,7 @@ static int rpi_firmware_probe(struct pla
 | 
					 | 
				
			||||||
 	kref_init(&fw->consumers);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	platform_set_drvdata(pdev, fw);
 | 
					 | 
				
			||||||
+	g_pdev = pdev;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	rpi_firmware_print_firmware_revision(fw);
 | 
					 | 
				
			||||||
 	rpi_register_hwmon_driver(dev, fw);
 | 
					 | 
				
			||||||
@@ -301,6 +304,8 @@ static int rpi_firmware_remove(struct pl
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	rpi_firmware_put(fw);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	g_pdev = NULL;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	return 0;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -314,7 +319,7 @@ static int rpi_firmware_remove(struct pl
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
 struct rpi_firmware *rpi_firmware_get(struct device_node *firmware_node)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
-	struct platform_device *pdev = of_find_device_by_node(firmware_node);
 | 
					 | 
				
			||||||
+	struct platform_device *pdev = g_pdev;
 | 
					 | 
				
			||||||
 	struct rpi_firmware *fw;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	if (!pdev)
 | 
					 | 
				
			||||||
@@ -327,12 +332,9 @@ struct rpi_firmware *rpi_firmware_get(st
 | 
					 | 
				
			||||||
 	if (!kref_get_unless_zero(&fw->consumers))
 | 
					 | 
				
			||||||
 		goto err_put_device;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	put_device(&pdev->dev);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 	return fw;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 err_put_device:
 | 
					 | 
				
			||||||
-	put_device(&pdev->dev);
 | 
					 | 
				
			||||||
 	return NULL;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 EXPORT_SYMBOL_GPL(rpi_firmware_get);
 | 
					 | 
				
			||||||
@@ -352,7 +354,18 @@ static struct platform_driver rpi_firmwa
 | 
					 | 
				
			||||||
 	.shutdown	= rpi_firmware_shutdown,
 | 
					 | 
				
			||||||
 	.remove		= rpi_firmware_remove,
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
-module_platform_driver(rpi_firmware_driver);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int __init rpi_firmware_init(void)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return platform_driver_register(&rpi_firmware_driver);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+subsys_initcall(rpi_firmware_init);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void __init rpi_firmware_exit(void)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	platform_driver_unregister(&rpi_firmware_driver);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+module_exit(rpi_firmware_exit);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 MODULE_AUTHOR("Eric Anholt <eric@anholt.net>");
 | 
					 | 
				
			||||||
 MODULE_DESCRIPTION("Raspberry Pi firmware driver");
 | 
					 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,169 +0,0 @@
 | 
				
			|||||||
From 3bb38201fc6c1aef7709c3c744824db23f06c629 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Fri, 6 Feb 2015 13:50:57 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] BCM270x_DT: Add pwr_led, and the required "input"
 | 
					 | 
				
			||||||
 trigger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The "input" trigger makes the associated GPIO an input.  This is to support
 | 
					 | 
				
			||||||
the Raspberry Pi PWR LED, which is driven by external hardware in normal use.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
N.B. pwr_led is not available on Model A or B boards.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
leds-gpio: Implement the brightness_get method
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The power LED uses some clever logic that means it is driven
 | 
					 | 
				
			||||||
by a voltage measuring circuit when configured as input, otherwise
 | 
					 | 
				
			||||||
it is driven by the GPIO output value. This patch wires up the
 | 
					 | 
				
			||||||
brightness_get method for leds-gpio so that user-space can monitor
 | 
					 | 
				
			||||||
the LED value via /sys/class/gpio/led1/brightness. Using the input
 | 
					 | 
				
			||||||
trigger this returns an indication of the system power health,
 | 
					 | 
				
			||||||
otherwise it is just whatever value the trigger has written most
 | 
					 | 
				
			||||||
recently.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
See: https://github.com/raspberrypi/linux/issues/1064
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/leds/leds-gpio.c             | 17 ++++++++-
 | 
					 | 
				
			||||||
 drivers/leds/trigger/Kconfig         |  7 ++++
 | 
					 | 
				
			||||||
 drivers/leds/trigger/Makefile        |  1 +
 | 
					 | 
				
			||||||
 drivers/leds/trigger/ledtrig-input.c | 55 ++++++++++++++++++++++++++++
 | 
					 | 
				
			||||||
 include/linux/leds.h                 |  3 ++
 | 
					 | 
				
			||||||
 5 files changed, 82 insertions(+), 1 deletion(-)
 | 
					 | 
				
			||||||
 create mode 100644 drivers/leds/trigger/ledtrig-input.c
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/leds/leds-gpio.c
 | 
					 | 
				
			||||||
+++ b/drivers/leds/leds-gpio.c
 | 
					 | 
				
			||||||
@@ -46,8 +46,15 @@ static void gpio_led_set(struct led_clas
 | 
					 | 
				
			||||||
 		led_dat->platform_gpio_blink_set(led_dat->gpiod, level,
 | 
					 | 
				
			||||||
 						 NULL, NULL);
 | 
					 | 
				
			||||||
 		led_dat->blinking = 0;
 | 
					 | 
				
			||||||
+	} else if (led_dat->cdev.flags & SET_GPIO_INPUT) {
 | 
					 | 
				
			||||||
+		gpiod_direction_input(led_dat->gpiod);
 | 
					 | 
				
			||||||
+		led_dat->cdev.flags &= ~SET_GPIO_INPUT;
 | 
					 | 
				
			||||||
+	} else if (led_dat->cdev.flags & SET_GPIO_OUTPUT) {
 | 
					 | 
				
			||||||
+		gpiod_direction_output(led_dat->gpiod, level);
 | 
					 | 
				
			||||||
+		led_dat->cdev.flags &= ~SET_GPIO_OUTPUT;
 | 
					 | 
				
			||||||
 	} else {
 | 
					 | 
				
			||||||
-		if (led_dat->can_sleep)
 | 
					 | 
				
			||||||
+		if (led_dat->can_sleep ||
 | 
					 | 
				
			||||||
+			(led_dat->cdev.flags & (SET_GPIO_INPUT | SET_GPIO_OUTPUT) ))
 | 
					 | 
				
			||||||
 			gpiod_set_value_cansleep(led_dat->gpiod, level);
 | 
					 | 
				
			||||||
 		else
 | 
					 | 
				
			||||||
 			gpiod_set_value(led_dat->gpiod, level);
 | 
					 | 
				
			||||||
@@ -61,6 +68,13 @@ static int gpio_led_set_blocking(struct
 | 
					 | 
				
			||||||
 	return 0;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static enum led_brightness gpio_led_get(struct led_classdev *led_cdev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct gpio_led_data *led_dat =
 | 
					 | 
				
			||||||
+		container_of(led_cdev, struct gpio_led_data, cdev);
 | 
					 | 
				
			||||||
+	return gpiod_get_value_cansleep(led_dat->gpiod) ? LED_FULL : LED_OFF;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static int gpio_blink_set(struct led_classdev *led_cdev,
 | 
					 | 
				
			||||||
 	unsigned long *delay_on, unsigned long *delay_off)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
@@ -89,6 +103,7 @@ static int create_gpio_led(const struct
 | 
					 | 
				
			||||||
 		led_dat->platform_gpio_blink_set = blink_set;
 | 
					 | 
				
			||||||
 		led_dat->cdev.blink_set = gpio_blink_set;
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
+	led_dat->cdev.brightness_get = gpio_led_get;
 | 
					 | 
				
			||||||
 	if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP) {
 | 
					 | 
				
			||||||
 		state = gpiod_get_value_cansleep(led_dat->gpiod);
 | 
					 | 
				
			||||||
 		if (state < 0)
 | 
					 | 
				
			||||||
--- a/drivers/leds/trigger/Kconfig
 | 
					 | 
				
			||||||
+++ b/drivers/leds/trigger/Kconfig
 | 
					 | 
				
			||||||
@@ -114,6 +114,13 @@ config LEDS_TRIGGER_CAMERA
 | 
					 | 
				
			||||||
 	  This enables direct flash/torch on/off by the driver, kernel space.
 | 
					 | 
				
			||||||
 	  If unsure, say Y.
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+config LEDS_TRIGGER_INPUT
 | 
					 | 
				
			||||||
+	tristate "LED Input Trigger"
 | 
					 | 
				
			||||||
+	depends on LEDS_TRIGGERS
 | 
					 | 
				
			||||||
+	help
 | 
					 | 
				
			||||||
+	  This allows the GPIOs assigned to be LEDs to be initialised to inputs.
 | 
					 | 
				
			||||||
+	  If unsure, say Y.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 config LEDS_TRIGGER_PANIC
 | 
					 | 
				
			||||||
 	bool "LED Panic Trigger"
 | 
					 | 
				
			||||||
 	help
 | 
					 | 
				
			||||||
--- a/drivers/leds/trigger/Makefile
 | 
					 | 
				
			||||||
+++ b/drivers/leds/trigger/Makefile
 | 
					 | 
				
			||||||
@@ -11,6 +11,7 @@ obj-$(CONFIG_LEDS_TRIGGER_ACTIVITY)	+= l
 | 
					 | 
				
			||||||
 obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON)	+= ledtrig-default-on.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT)	+= ledtrig-transient.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_LEDS_TRIGGER_CAMERA)	+= ledtrig-camera.o
 | 
					 | 
				
			||||||
+obj-$(CONFIG_LEDS_TRIGGER_INPUT)	+= ledtrig-input.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_LEDS_TRIGGER_PANIC)	+= ledtrig-panic.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_LEDS_TRIGGER_NETDEV)	+= ledtrig-netdev.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_LEDS_TRIGGER_PATTERN)	+= ledtrig-pattern.o
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/drivers/leds/trigger/ledtrig-input.c
 | 
					 | 
				
			||||||
@@ -0,0 +1,55 @@
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ * Set LED GPIO to Input "Trigger"
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Copyright 2015 Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Based on Nick Forbes's ledtrig-default-on.c.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * 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/module.h>
 | 
					 | 
				
			||||||
+#include <linux/kernel.h>
 | 
					 | 
				
			||||||
+#include <linux/init.h>
 | 
					 | 
				
			||||||
+#include <linux/leds.h>
 | 
					 | 
				
			||||||
+#include <linux/gpio.h>
 | 
					 | 
				
			||||||
+#include "../leds.h"
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int input_trig_activate(struct led_classdev *led_cdev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	led_cdev->flags |= SET_GPIO_INPUT;
 | 
					 | 
				
			||||||
+	led_set_brightness(led_cdev, 0);
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void input_trig_deactivate(struct led_classdev *led_cdev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	led_cdev->flags |= SET_GPIO_OUTPUT;
 | 
					 | 
				
			||||||
+	led_set_brightness(led_cdev, 0);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct led_trigger input_led_trigger = {
 | 
					 | 
				
			||||||
+	.name     = "input",
 | 
					 | 
				
			||||||
+	.activate = input_trig_activate,
 | 
					 | 
				
			||||||
+	.deactivate = input_trig_deactivate,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int __init input_trig_init(void)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return led_trigger_register(&input_led_trigger);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void __exit input_trig_exit(void)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	led_trigger_unregister(&input_led_trigger);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+module_init(input_trig_init);
 | 
					 | 
				
			||||||
+module_exit(input_trig_exit);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+MODULE_AUTHOR("Phil Elwell <phil@raspberrypi.org>");
 | 
					 | 
				
			||||||
+MODULE_DESCRIPTION("Set LED GPIO to Input \"trigger\"");
 | 
					 | 
				
			||||||
+MODULE_LICENSE("GPL");
 | 
					 | 
				
			||||||
--- a/include/linux/leds.h
 | 
					 | 
				
			||||||
+++ b/include/linux/leds.h
 | 
					 | 
				
			||||||
@@ -79,6 +79,9 @@ struct led_classdev {
 | 
					 | 
				
			||||||
 #define LED_BRIGHT_HW_CHANGED	BIT(21)
 | 
					 | 
				
			||||||
 #define LED_RETAIN_AT_SHUTDOWN	BIT(22)
 | 
					 | 
				
			||||||
 #define LED_INIT_DEFAULT_TRIGGER BIT(23)
 | 
					 | 
				
			||||||
+	/* Additions for Raspberry Pi PWR LED */
 | 
					 | 
				
			||||||
+#define SET_GPIO_INPUT		BIT(30)
 | 
					 | 
				
			||||||
+#define SET_GPIO_OUTPUT		BIT(31)
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* set_brightness_work / blink_timer flags, atomic, private. */
 | 
					 | 
				
			||||||
 	unsigned long		work_flags;
 | 
					 | 
				
			||||||
@ -1,22 +0,0 @@
 | 
				
			|||||||
From 6bfe5935985ffc3b5365877db6e8b90ba4924540 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: popcornmix <popcornmix@gmail.com>
 | 
					 | 
				
			||||||
Date: Wed, 3 Jul 2013 00:54:08 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] Added Device IDs for August DVB-T 205
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 4 ++++
 | 
					 | 
				
			||||||
 1 file changed, 4 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
 | 
					 | 
				
			||||||
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
 | 
					 | 
				
			||||||
@@ -1953,6 +1953,10 @@ static const struct usb_device_id rtl28x
 | 
					 | 
				
			||||||
 		&rtl28xxu_props, "Compro VideoMate U650F", NULL) },
 | 
					 | 
				
			||||||
 	{ DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd394,
 | 
					 | 
				
			||||||
 		&rtl28xxu_props, "MaxMedia HU394-T", NULL) },
 | 
					 | 
				
			||||||
+	{ DVB_USB_DEVICE(USB_VID_GTEK, 0xb803 /*USB_PID_AUGUST_DVBT205*/,
 | 
					 | 
				
			||||||
+		&rtl28xxu_props, "August DVB-T 205", NULL) },
 | 
					 | 
				
			||||||
+	{ DVB_USB_DEVICE(USB_VID_GTEK, 0xa803 /*USB_PID_AUGUST_DVBT205*/,
 | 
					 | 
				
			||||||
+		&rtl28xxu_props, "August DVB-T 205", NULL) },
 | 
					 | 
				
			||||||
 	{ DVB_USB_DEVICE(USB_VID_LEADTEK, 0x6a03,
 | 
					 | 
				
			||||||
 		&rtl28xxu_props, "Leadtek WinFast DTV Dongle mini", NULL) },
 | 
					 | 
				
			||||||
 	{ DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_CPYTO_REDI_PC50A,
 | 
					 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,35 +0,0 @@
 | 
				
			|||||||
From 95a7bcf89e17aa8ec8cabc06d47479e1ecb76976 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Thu, 25 Jun 2015 12:16:11 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] gpio-poweroff: Allow it to work on Raspberry Pi
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The Raspberry Pi firmware manages the power-down and reboot
 | 
					 | 
				
			||||||
process. To do this it installs a pm_power_off handler, causing
 | 
					 | 
				
			||||||
the gpio-poweroff module to abort the probe function.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This patch introduces a "force" DT property that overrides that
 | 
					 | 
				
			||||||
behaviour, and also adds a DT overlay to enable and control it.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Note that running in an active-low configuration (DT parameter
 | 
					 | 
				
			||||||
"active_low") requires a custom dt-blob.bin and probably won't
 | 
					 | 
				
			||||||
allow a reboot without switching off, so an external inversion
 | 
					 | 
				
			||||||
of the trigger signal may be preferable.
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/power/reset/gpio-poweroff.c | 4 +++-
 | 
					 | 
				
			||||||
 1 file changed, 3 insertions(+), 1 deletion(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/power/reset/gpio-poweroff.c
 | 
					 | 
				
			||||||
+++ b/drivers/power/reset/gpio-poweroff.c
 | 
					 | 
				
			||||||
@@ -50,9 +50,11 @@ static int gpio_poweroff_probe(struct pl
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	bool input = false;
 | 
					 | 
				
			||||||
 	enum gpiod_flags flags;
 | 
					 | 
				
			||||||
+	bool force = false;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* If a pm_power_off function has already been added, leave it alone */
 | 
					 | 
				
			||||||
-	if (pm_power_off != NULL) {
 | 
					 | 
				
			||||||
+	force = of_property_read_bool(pdev->dev.of_node, "force");
 | 
					 | 
				
			||||||
+	if (!force && (pm_power_off != NULL)) {
 | 
					 | 
				
			||||||
 		dev_err(&pdev->dev,
 | 
					 | 
				
			||||||
 			"%s: pm_power_off function already registered\n",
 | 
					 | 
				
			||||||
 		       __func__);
 | 
					 | 
				
			||||||
@ -1,852 +0,0 @@
 | 
				
			|||||||
From cf000173e9ee4a80ac245e5feb042c18c5643d6c Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <pelwell@users.noreply.github.com>
 | 
					 | 
				
			||||||
Date: Tue, 14 Jul 2015 14:32:47 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] mfd: Add Raspberry Pi Sense HAT core driver
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
mfd: Add rpi_sense_core of compatible string
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/input/joystick/Kconfig           |   8 +
 | 
					 | 
				
			||||||
 drivers/input/joystick/Makefile          |   2 +-
 | 
					 | 
				
			||||||
 drivers/input/joystick/rpisense-js.c     | 153 ++++++++++++
 | 
					 | 
				
			||||||
 drivers/mfd/Kconfig                      |   8 +
 | 
					 | 
				
			||||||
 drivers/mfd/Makefile                     |   1 +
 | 
					 | 
				
			||||||
 drivers/mfd/rpisense-core.c              | 165 +++++++++++++
 | 
					 | 
				
			||||||
 drivers/video/fbdev/Kconfig              |  13 +
 | 
					 | 
				
			||||||
 drivers/video/fbdev/Makefile             |   1 +
 | 
					 | 
				
			||||||
 drivers/video/fbdev/rpisense-fb.c        | 293 +++++++++++++++++++++++
 | 
					 | 
				
			||||||
 include/linux/mfd/rpisense/core.h        |  47 ++++
 | 
					 | 
				
			||||||
 include/linux/mfd/rpisense/framebuffer.h |  32 +++
 | 
					 | 
				
			||||||
 include/linux/mfd/rpisense/joystick.h    |  35 +++
 | 
					 | 
				
			||||||
 12 files changed, 757 insertions(+), 1 deletion(-)
 | 
					 | 
				
			||||||
 create mode 100644 drivers/input/joystick/rpisense-js.c
 | 
					 | 
				
			||||||
 create mode 100644 drivers/mfd/rpisense-core.c
 | 
					 | 
				
			||||||
 create mode 100644 drivers/video/fbdev/rpisense-fb.c
 | 
					 | 
				
			||||||
 create mode 100644 include/linux/mfd/rpisense/core.h
 | 
					 | 
				
			||||||
 create mode 100644 include/linux/mfd/rpisense/framebuffer.h
 | 
					 | 
				
			||||||
 create mode 100644 include/linux/mfd/rpisense/joystick.h
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/input/joystick/Kconfig
 | 
					 | 
				
			||||||
+++ b/drivers/input/joystick/Kconfig
 | 
					 | 
				
			||||||
@@ -382,4 +382,12 @@ config JOYSTICK_FSIA6B
 | 
					 | 
				
			||||||
 	  To compile this driver as a module, choose M here: the
 | 
					 | 
				
			||||||
 	  module will be called fsia6b.
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+config JOYSTICK_RPISENSE
 | 
					 | 
				
			||||||
+	tristate "Raspberry Pi Sense HAT joystick"
 | 
					 | 
				
			||||||
+	depends on GPIOLIB && INPUT
 | 
					 | 
				
			||||||
+	select MFD_RPISENSE_CORE
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	help
 | 
					 | 
				
			||||||
+	  This is the joystick driver for the Raspberry Pi Sense HAT
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 endif
 | 
					 | 
				
			||||||
--- a/drivers/input/joystick/Makefile
 | 
					 | 
				
			||||||
+++ b/drivers/input/joystick/Makefile
 | 
					 | 
				
			||||||
@@ -37,4 +37,4 @@ obj-$(CONFIG_JOYSTICK_WARRIOR)		+= warri
 | 
					 | 
				
			||||||
 obj-$(CONFIG_JOYSTICK_WALKERA0701)	+= walkera0701.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_JOYSTICK_XPAD)		+= xpad.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_JOYSTICK_ZHENHUA)		+= zhenhua.o
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
+obj-$(CONFIG_JOYSTICK_RPISENSE)		+= rpisense-js.o
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/drivers/input/joystick/rpisense-js.c
 | 
					 | 
				
			||||||
@@ -0,0 +1,153 @@
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ * Raspberry Pi Sense HAT joystick driver
 | 
					 | 
				
			||||||
+ * http://raspberrypi.org
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Copyright (C) 2015 Raspberry Pi
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Author: Serge Schneider
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ *  This program is free software; you can redistribute  it and/or modify it
 | 
					 | 
				
			||||||
+ *  under  the terms of  the GNU General  Public License as published by the
 | 
					 | 
				
			||||||
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
 | 
					 | 
				
			||||||
+ *  option) any later version.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#include <linux/module.h>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#include <linux/mfd/rpisense/joystick.h>
 | 
					 | 
				
			||||||
+#include <linux/mfd/rpisense/core.h>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct rpisense *rpisense;
 | 
					 | 
				
			||||||
+static unsigned char keymap[5] = {KEY_DOWN, KEY_RIGHT, KEY_UP, KEY_ENTER, KEY_LEFT,};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void keys_work_fn(struct work_struct *work)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int i;
 | 
					 | 
				
			||||||
+	static s32 prev_keys;
 | 
					 | 
				
			||||||
+	struct rpisense_js *rpisense_js = &rpisense->joystick;
 | 
					 | 
				
			||||||
+	s32 keys = rpisense_reg_read(rpisense, RPISENSE_KEYS);
 | 
					 | 
				
			||||||
+	s32 changes = keys ^ prev_keys;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	prev_keys = keys;
 | 
					 | 
				
			||||||
+	for (i = 0; i < 5; i++) {
 | 
					 | 
				
			||||||
+		if (changes & 1) {
 | 
					 | 
				
			||||||
+			input_report_key(rpisense_js->keys_dev,
 | 
					 | 
				
			||||||
+					 keymap[i], keys & 1);
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+		changes >>= 1;
 | 
					 | 
				
			||||||
+		keys >>= 1;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	input_sync(rpisense_js->keys_dev);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static irqreturn_t keys_irq_handler(int irq, void *pdev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct rpisense_js *rpisense_js = &rpisense->joystick;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	schedule_work(&rpisense_js->keys_work_s);
 | 
					 | 
				
			||||||
+	return IRQ_HANDLED;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int rpisense_js_probe(struct platform_device *pdev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int ret;
 | 
					 | 
				
			||||||
+	int i;
 | 
					 | 
				
			||||||
+	struct rpisense_js *rpisense_js;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	rpisense = rpisense_get_dev();
 | 
					 | 
				
			||||||
+	rpisense_js = &rpisense->joystick;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	INIT_WORK(&rpisense_js->keys_work_s, keys_work_fn);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	rpisense_js->keys_dev = input_allocate_device();
 | 
					 | 
				
			||||||
+	if (!rpisense_js->keys_dev) {
 | 
					 | 
				
			||||||
+		dev_err(&pdev->dev, "Could not allocate input device.\n");
 | 
					 | 
				
			||||||
+		return -ENOMEM;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	rpisense_js->keys_dev->evbit[0] = BIT_MASK(EV_KEY);
 | 
					 | 
				
			||||||
+	for (i = 0; i < ARRAY_SIZE(keymap); i++) {
 | 
					 | 
				
			||||||
+		set_bit(keymap[i],
 | 
					 | 
				
			||||||
+			rpisense_js->keys_dev->keybit);
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	rpisense_js->keys_dev->name = "Raspberry Pi Sense HAT Joystick";
 | 
					 | 
				
			||||||
+	rpisense_js->keys_dev->phys = "rpi-sense-joy/input0";
 | 
					 | 
				
			||||||
+	rpisense_js->keys_dev->id.bustype = BUS_I2C;
 | 
					 | 
				
			||||||
+	rpisense_js->keys_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
 | 
					 | 
				
			||||||
+	rpisense_js->keys_dev->keycode = keymap;
 | 
					 | 
				
			||||||
+	rpisense_js->keys_dev->keycodesize = sizeof(unsigned char);
 | 
					 | 
				
			||||||
+	rpisense_js->keys_dev->keycodemax = ARRAY_SIZE(keymap);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ret = input_register_device(rpisense_js->keys_dev);
 | 
					 | 
				
			||||||
+	if (ret) {
 | 
					 | 
				
			||||||
+		dev_err(&pdev->dev, "Could not register input device.\n");
 | 
					 | 
				
			||||||
+		goto err_keys_alloc;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ret = gpiod_direction_input(rpisense_js->keys_desc);
 | 
					 | 
				
			||||||
+	if (ret) {
 | 
					 | 
				
			||||||
+		dev_err(&pdev->dev, "Could not set keys-int direction.\n");
 | 
					 | 
				
			||||||
+		goto err_keys_reg;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	rpisense_js->keys_irq = gpiod_to_irq(rpisense_js->keys_desc);
 | 
					 | 
				
			||||||
+	if (rpisense_js->keys_irq < 0) {
 | 
					 | 
				
			||||||
+		dev_err(&pdev->dev, "Could not determine keys-int IRQ.\n");
 | 
					 | 
				
			||||||
+		ret = rpisense_js->keys_irq;
 | 
					 | 
				
			||||||
+		goto err_keys_reg;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ret = devm_request_irq(&pdev->dev, rpisense_js->keys_irq,
 | 
					 | 
				
			||||||
+			       keys_irq_handler, IRQF_TRIGGER_RISING,
 | 
					 | 
				
			||||||
+			       "keys", &pdev->dev);
 | 
					 | 
				
			||||||
+	if (ret) {
 | 
					 | 
				
			||||||
+		dev_err(&pdev->dev, "IRQ request failed.\n");
 | 
					 | 
				
			||||||
+		goto err_keys_reg;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+err_keys_reg:
 | 
					 | 
				
			||||||
+	input_unregister_device(rpisense_js->keys_dev);
 | 
					 | 
				
			||||||
+err_keys_alloc:
 | 
					 | 
				
			||||||
+	input_free_device(rpisense_js->keys_dev);
 | 
					 | 
				
			||||||
+	return ret;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int rpisense_js_remove(struct platform_device *pdev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct rpisense_js *rpisense_js = &rpisense->joystick;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	input_unregister_device(rpisense_js->keys_dev);
 | 
					 | 
				
			||||||
+	input_free_device(rpisense_js->keys_dev);
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#ifdef CONFIG_OF
 | 
					 | 
				
			||||||
+static const struct of_device_id rpisense_js_id[] = {
 | 
					 | 
				
			||||||
+	{ .compatible = "rpi,rpi-sense-js" },
 | 
					 | 
				
			||||||
+	{ },
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+MODULE_DEVICE_TABLE(of, rpisense_js_id);
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct platform_device_id rpisense_js_device_id[] = {
 | 
					 | 
				
			||||||
+	{ .name = "rpi-sense-js" },
 | 
					 | 
				
			||||||
+	{ },
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+MODULE_DEVICE_TABLE(platform, rpisense_js_device_id);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct platform_driver rpisense_js_driver = {
 | 
					 | 
				
			||||||
+	.probe = rpisense_js_probe,
 | 
					 | 
				
			||||||
+	.remove = rpisense_js_remove,
 | 
					 | 
				
			||||||
+	.driver = {
 | 
					 | 
				
			||||||
+		.name = "rpi-sense-js",
 | 
					 | 
				
			||||||
+		.owner = THIS_MODULE,
 | 
					 | 
				
			||||||
+	},
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+module_platform_driver(rpisense_js_driver);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+MODULE_DESCRIPTION("Raspberry Pi Sense HAT joystick driver");
 | 
					 | 
				
			||||||
+MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
 | 
					 | 
				
			||||||
+MODULE_LICENSE("GPL");
 | 
					 | 
				
			||||||
--- a/drivers/mfd/Kconfig
 | 
					 | 
				
			||||||
+++ b/drivers/mfd/Kconfig
 | 
					 | 
				
			||||||
@@ -11,6 +11,14 @@ config MFD_CORE
 | 
					 | 
				
			||||||
 	select IRQ_DOMAIN
 | 
					 | 
				
			||||||
 	default n
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+config MFD_RPISENSE_CORE
 | 
					 | 
				
			||||||
+	tristate "Raspberry Pi Sense HAT core functions"
 | 
					 | 
				
			||||||
+	depends on I2C
 | 
					 | 
				
			||||||
+	select MFD_CORE
 | 
					 | 
				
			||||||
+	help
 | 
					 | 
				
			||||||
+	  This is the core driver for the Raspberry Pi Sense HAT. This provides
 | 
					 | 
				
			||||||
+	  the necessary functions to communicate with the hardware.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 config MFD_CS5535
 | 
					 | 
				
			||||||
 	tristate "AMD CS5535 and CS5536 southbridge core functions"
 | 
					 | 
				
			||||||
 	select MFD_CORE
 | 
					 | 
				
			||||||
--- a/drivers/mfd/Makefile
 | 
					 | 
				
			||||||
+++ b/drivers/mfd/Makefile
 | 
					 | 
				
			||||||
@@ -263,6 +263,7 @@ obj-$(CONFIG_MFD_ROHM_BD71828)	+= rohm-b
 | 
					 | 
				
			||||||
 obj-$(CONFIG_MFD_ROHM_BD718XX)	+= rohm-bd718x7.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_MFD_STMFX) 	+= stmfx.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_MFD_KHADAS_MCU) 	+= khadas-mcu.o
 | 
					 | 
				
			||||||
+obj-$(CONFIG_MFD_RPISENSE_CORE)	+= rpisense-core.o
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 obj-$(CONFIG_SGI_MFD_IOC3)	+= ioc3.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_MFD_SIMPLE_MFD_I2C)	+= simple-mfd-i2c.o
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/drivers/mfd/rpisense-core.c
 | 
					 | 
				
			||||||
@@ -0,0 +1,165 @@
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ * Raspberry Pi Sense HAT core driver
 | 
					 | 
				
			||||||
+ * http://raspberrypi.org
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Copyright (C) 2015 Raspberry Pi
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Author: Serge Schneider
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ *  This program is free software; you can redistribute  it and/or modify it
 | 
					 | 
				
			||||||
+ *  under  the terms of  the GNU General  Public License as published by the
 | 
					 | 
				
			||||||
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
 | 
					 | 
				
			||||||
+ *  option) any later version.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ *  This driver is based on wm8350 implementation.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#include <linux/module.h>
 | 
					 | 
				
			||||||
+#include <linux/moduleparam.h>
 | 
					 | 
				
			||||||
+#include <linux/err.h>
 | 
					 | 
				
			||||||
+#include <linux/init.h>
 | 
					 | 
				
			||||||
+#include <linux/i2c.h>
 | 
					 | 
				
			||||||
+#include <linux/platform_device.h>
 | 
					 | 
				
			||||||
+#include <linux/mfd/rpisense/core.h>
 | 
					 | 
				
			||||||
+#include <linux/slab.h>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct rpisense *rpisense;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void rpisense_client_dev_register(struct rpisense *rpisense,
 | 
					 | 
				
			||||||
+					 const char *name,
 | 
					 | 
				
			||||||
+					 struct platform_device **pdev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	*pdev = platform_device_alloc(name, -1);
 | 
					 | 
				
			||||||
+	if (*pdev == NULL) {
 | 
					 | 
				
			||||||
+		dev_err(rpisense->dev, "Failed to allocate %s\n", name);
 | 
					 | 
				
			||||||
+		return;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	(*pdev)->dev.parent = rpisense->dev;
 | 
					 | 
				
			||||||
+	platform_set_drvdata(*pdev, rpisense);
 | 
					 | 
				
			||||||
+	ret = platform_device_add(*pdev);
 | 
					 | 
				
			||||||
+	if (ret != 0) {
 | 
					 | 
				
			||||||
+		dev_err(rpisense->dev, "Failed to register %s: %d\n",
 | 
					 | 
				
			||||||
+			name, ret);
 | 
					 | 
				
			||||||
+		platform_device_put(*pdev);
 | 
					 | 
				
			||||||
+		*pdev = NULL;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int rpisense_probe(struct i2c_client *i2c,
 | 
					 | 
				
			||||||
+			       const struct i2c_device_id *id)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int ret;
 | 
					 | 
				
			||||||
+	struct rpisense_js *rpisense_js;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	rpisense = devm_kzalloc(&i2c->dev, sizeof(struct rpisense), GFP_KERNEL);
 | 
					 | 
				
			||||||
+	if (rpisense == NULL)
 | 
					 | 
				
			||||||
+		return -ENOMEM;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	i2c_set_clientdata(i2c, rpisense);
 | 
					 | 
				
			||||||
+	rpisense->dev = &i2c->dev;
 | 
					 | 
				
			||||||
+	rpisense->i2c_client = i2c;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ret = rpisense_reg_read(rpisense, RPISENSE_WAI);
 | 
					 | 
				
			||||||
+	if (ret > 0) {
 | 
					 | 
				
			||||||
+		if (ret != 's')
 | 
					 | 
				
			||||||
+			return -EINVAL;
 | 
					 | 
				
			||||||
+	} else {
 | 
					 | 
				
			||||||
+		return ret;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	ret = rpisense_reg_read(rpisense, RPISENSE_VER);
 | 
					 | 
				
			||||||
+	if (ret < 0)
 | 
					 | 
				
			||||||
+		return ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	dev_info(rpisense->dev,
 | 
					 | 
				
			||||||
+		 "Raspberry Pi Sense HAT firmware version %i\n", ret);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	rpisense_js = &rpisense->joystick;
 | 
					 | 
				
			||||||
+	rpisense_js->keys_desc = devm_gpiod_get(&i2c->dev,
 | 
					 | 
				
			||||||
+						"keys-int", GPIOD_IN);
 | 
					 | 
				
			||||||
+	if (IS_ERR(rpisense_js->keys_desc)) {
 | 
					 | 
				
			||||||
+		dev_warn(&i2c->dev, "Failed to get keys-int descriptor.\n");
 | 
					 | 
				
			||||||
+		rpisense_js->keys_desc = gpio_to_desc(23);
 | 
					 | 
				
			||||||
+		if (rpisense_js->keys_desc == NULL) {
 | 
					 | 
				
			||||||
+			dev_err(&i2c->dev, "GPIO23 fallback failed.\n");
 | 
					 | 
				
			||||||
+			return PTR_ERR(rpisense_js->keys_desc);
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	rpisense_client_dev_register(rpisense, "rpi-sense-js",
 | 
					 | 
				
			||||||
+				     &(rpisense->joystick.pdev));
 | 
					 | 
				
			||||||
+	rpisense_client_dev_register(rpisense, "rpi-sense-fb",
 | 
					 | 
				
			||||||
+				     &(rpisense->framebuffer.pdev));
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int rpisense_remove(struct i2c_client *i2c)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct rpisense *rpisense = i2c_get_clientdata(i2c);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	platform_device_unregister(rpisense->joystick.pdev);
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+struct rpisense *rpisense_get_dev(void)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return rpisense;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL_GPL(rpisense_get_dev);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+s32 rpisense_reg_read(struct rpisense *rpisense, int reg)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int ret = i2c_smbus_read_byte_data(rpisense->i2c_client, reg);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (ret < 0)
 | 
					 | 
				
			||||||
+		dev_err(rpisense->dev, "Read from reg %d failed\n", reg);
 | 
					 | 
				
			||||||
+	/* Due to the BCM270x I2C clock stretching bug, some values
 | 
					 | 
				
			||||||
+	 * may have MSB set. Clear it to avoid incorrect values.
 | 
					 | 
				
			||||||
+	 * */
 | 
					 | 
				
			||||||
+	return ret & 0x7F;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL_GPL(rpisense_reg_read);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+int rpisense_block_write(struct rpisense *rpisense, const char *buf, int count)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int ret = i2c_master_send(rpisense->i2c_client, buf, count);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (ret < 0)
 | 
					 | 
				
			||||||
+		dev_err(rpisense->dev, "Block write failed\n");
 | 
					 | 
				
			||||||
+	return ret;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL_GPL(rpisense_block_write);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static const struct i2c_device_id rpisense_i2c_id[] = {
 | 
					 | 
				
			||||||
+	{ "rpi-sense", 0 },
 | 
					 | 
				
			||||||
+	{ }
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+MODULE_DEVICE_TABLE(i2c, rpisense_i2c_id);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#ifdef CONFIG_OF
 | 
					 | 
				
			||||||
+static const struct of_device_id rpisense_core_id[] = {
 | 
					 | 
				
			||||||
+	{ .compatible = "rpi,rpi-sense" },
 | 
					 | 
				
			||||||
+	{ },
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+MODULE_DEVICE_TABLE(of, rpisense_core_id);
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct i2c_driver rpisense_driver = {
 | 
					 | 
				
			||||||
+	.driver = {
 | 
					 | 
				
			||||||
+		   .name = "rpi-sense",
 | 
					 | 
				
			||||||
+		   .owner = THIS_MODULE,
 | 
					 | 
				
			||||||
+	},
 | 
					 | 
				
			||||||
+	.probe = rpisense_probe,
 | 
					 | 
				
			||||||
+	.remove = rpisense_remove,
 | 
					 | 
				
			||||||
+	.id_table = rpisense_i2c_id,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+module_i2c_driver(rpisense_driver);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+MODULE_DESCRIPTION("Raspberry Pi Sense HAT core driver");
 | 
					 | 
				
			||||||
+MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
 | 
					 | 
				
			||||||
+MODULE_LICENSE("GPL");
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
--- a/drivers/video/fbdev/Kconfig
 | 
					 | 
				
			||||||
+++ b/drivers/video/fbdev/Kconfig
 | 
					 | 
				
			||||||
@@ -2250,6 +2250,19 @@ config FB_SM712
 | 
					 | 
				
			||||||
 	  called sm712fb. If you want to compile it as a module, say M
 | 
					 | 
				
			||||||
 	  here and read <file:Documentation/kbuild/modules.rst>.
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+config FB_RPISENSE
 | 
					 | 
				
			||||||
+	tristate "Raspberry Pi Sense HAT framebuffer"
 | 
					 | 
				
			||||||
+	depends on FB
 | 
					 | 
				
			||||||
+	select MFD_RPISENSE_CORE
 | 
					 | 
				
			||||||
+	select FB_SYS_FOPS
 | 
					 | 
				
			||||||
+	select FB_SYS_FILLRECT
 | 
					 | 
				
			||||||
+	select FB_SYS_COPYAREA
 | 
					 | 
				
			||||||
+	select FB_SYS_IMAGEBLIT
 | 
					 | 
				
			||||||
+	select FB_DEFERRED_IO
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	help
 | 
					 | 
				
			||||||
+	  This is the framebuffer driver for the Raspberry Pi Sense HAT
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 source "drivers/video/fbdev/omap/Kconfig"
 | 
					 | 
				
			||||||
 source "drivers/video/fbdev/omap2/Kconfig"
 | 
					 | 
				
			||||||
 source "drivers/video/fbdev/mmp/Kconfig"
 | 
					 | 
				
			||||||
--- a/drivers/video/fbdev/Makefile
 | 
					 | 
				
			||||||
+++ b/drivers/video/fbdev/Makefile
 | 
					 | 
				
			||||||
@@ -130,6 +130,7 @@ obj-$(CONFIG_FB_MX3)		  += mx3fb.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_FB_DA8XX)		  += da8xx-fb.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_FB_SSD1307)	  += ssd1307fb.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_FB_SIMPLE)           += simplefb.o
 | 
					 | 
				
			||||||
+obj-$(CONFIG_FB_RPISENSE)	  += rpisense-fb.o
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 # the test framebuffer is last
 | 
					 | 
				
			||||||
 obj-$(CONFIG_FB_VIRTUAL)          += vfb.o
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/drivers/video/fbdev/rpisense-fb.c
 | 
					 | 
				
			||||||
@@ -0,0 +1,293 @@
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ * Raspberry Pi Sense HAT framebuffer driver
 | 
					 | 
				
			||||||
+ * http://raspberrypi.org
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Copyright (C) 2015 Raspberry Pi
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Author: Serge Schneider
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ *  This program is free software; you can redistribute  it and/or modify it
 | 
					 | 
				
			||||||
+ *  under  the terms of  the GNU General  Public License as published by the
 | 
					 | 
				
			||||||
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
 | 
					 | 
				
			||||||
+ *  option) any later version.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#include <linux/module.h>
 | 
					 | 
				
			||||||
+#include <linux/kernel.h>
 | 
					 | 
				
			||||||
+#include <linux/errno.h>
 | 
					 | 
				
			||||||
+#include <linux/string.h>
 | 
					 | 
				
			||||||
+#include <linux/mm.h>
 | 
					 | 
				
			||||||
+#include <linux/slab.h>
 | 
					 | 
				
			||||||
+#include <linux/uaccess.h>
 | 
					 | 
				
			||||||
+#include <linux/delay.h>
 | 
					 | 
				
			||||||
+#include <linux/fb.h>
 | 
					 | 
				
			||||||
+#include <linux/init.h>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#include <linux/mfd/rpisense/framebuffer.h>
 | 
					 | 
				
			||||||
+#include <linux/mfd/rpisense/core.h>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static bool lowlight;
 | 
					 | 
				
			||||||
+module_param(lowlight, bool, 0);
 | 
					 | 
				
			||||||
+MODULE_PARM_DESC(lowlight, "Reduce LED matrix brightness to one third");
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct rpisense *rpisense;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+struct rpisense_fb_param {
 | 
					 | 
				
			||||||
+	char __iomem *vmem;
 | 
					 | 
				
			||||||
+	u8 *vmem_work;
 | 
					 | 
				
			||||||
+	u32 vmemsize;
 | 
					 | 
				
			||||||
+	u8 *gamma;
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static u8 gamma_default[32] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
 | 
					 | 
				
			||||||
+			       0x02, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07,
 | 
					 | 
				
			||||||
+			       0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0E, 0x0F, 0x11,
 | 
					 | 
				
			||||||
+			       0x12, 0x14, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F,};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static u8 gamma_low[32] = {0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
 | 
					 | 
				
			||||||
+			   0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02,
 | 
					 | 
				
			||||||
+			   0x03, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06,
 | 
					 | 
				
			||||||
+			   0x06, 0x07, 0x07, 0x08, 0x08, 0x09, 0x0A, 0x0A,};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static u8 gamma_user[32];
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct rpisense_fb_param rpisense_fb_param = {
 | 
					 | 
				
			||||||
+	.vmem = NULL,
 | 
					 | 
				
			||||||
+	.vmemsize = 128,
 | 
					 | 
				
			||||||
+	.gamma = gamma_default,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct fb_deferred_io rpisense_fb_defio;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct fb_fix_screeninfo rpisense_fb_fix = {
 | 
					 | 
				
			||||||
+	.id =		"RPi-Sense FB",
 | 
					 | 
				
			||||||
+	.type =		FB_TYPE_PACKED_PIXELS,
 | 
					 | 
				
			||||||
+	.visual =	FB_VISUAL_TRUECOLOR,
 | 
					 | 
				
			||||||
+	.xpanstep =	0,
 | 
					 | 
				
			||||||
+	.ypanstep =	0,
 | 
					 | 
				
			||||||
+	.ywrapstep =	0,
 | 
					 | 
				
			||||||
+	.accel =	FB_ACCEL_NONE,
 | 
					 | 
				
			||||||
+	.line_length =	16,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct fb_var_screeninfo rpisense_fb_var = {
 | 
					 | 
				
			||||||
+	.xres		= 8,
 | 
					 | 
				
			||||||
+	.yres		= 8,
 | 
					 | 
				
			||||||
+	.xres_virtual	= 8,
 | 
					 | 
				
			||||||
+	.yres_virtual	= 8,
 | 
					 | 
				
			||||||
+	.bits_per_pixel = 16,
 | 
					 | 
				
			||||||
+	.red		= {11, 5, 0},
 | 
					 | 
				
			||||||
+	.green		= {5, 6, 0},
 | 
					 | 
				
			||||||
+	.blue		= {0, 5, 0},
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static ssize_t rpisense_fb_write(struct fb_info *info,
 | 
					 | 
				
			||||||
+				 const char __user *buf, size_t count,
 | 
					 | 
				
			||||||
+				 loff_t *ppos)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	ssize_t res = fb_sys_write(info, buf, count, ppos);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
 | 
					 | 
				
			||||||
+	return res;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void rpisense_fb_fillrect(struct fb_info *info,
 | 
					 | 
				
			||||||
+				 const struct fb_fillrect *rect)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	sys_fillrect(info, rect);
 | 
					 | 
				
			||||||
+	schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void rpisense_fb_copyarea(struct fb_info *info,
 | 
					 | 
				
			||||||
+				 const struct fb_copyarea *area)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	sys_copyarea(info, area);
 | 
					 | 
				
			||||||
+	schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void rpisense_fb_imageblit(struct fb_info *info,
 | 
					 | 
				
			||||||
+				  const struct fb_image *image)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	sys_imageblit(info, image);
 | 
					 | 
				
			||||||
+	schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void rpisense_fb_deferred_io(struct fb_info *info,
 | 
					 | 
				
			||||||
+				struct list_head *pagelist)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int i;
 | 
					 | 
				
			||||||
+	int j;
 | 
					 | 
				
			||||||
+	u8 *vmem_work = rpisense_fb_param.vmem_work;
 | 
					 | 
				
			||||||
+	u16 *mem = (u16 *)rpisense_fb_param.vmem;
 | 
					 | 
				
			||||||
+	u8 *gamma = rpisense_fb_param.gamma;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	vmem_work[0] = 0;
 | 
					 | 
				
			||||||
+	for (j = 0; j < 8; j++) {
 | 
					 | 
				
			||||||
+		for (i = 0; i < 8; i++) {
 | 
					 | 
				
			||||||
+			vmem_work[(j * 24) + i + 1] =
 | 
					 | 
				
			||||||
+				gamma[(mem[(j * 8) + i] >> 11) & 0x1F];
 | 
					 | 
				
			||||||
+			vmem_work[(j * 24) + (i + 8) + 1] =
 | 
					 | 
				
			||||||
+				gamma[(mem[(j * 8) + i] >> 6) & 0x1F];
 | 
					 | 
				
			||||||
+			vmem_work[(j * 24) + (i + 16) + 1] =
 | 
					 | 
				
			||||||
+				gamma[(mem[(j * 8) + i]) & 0x1F];
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	rpisense_block_write(rpisense, vmem_work, 193);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct fb_deferred_io rpisense_fb_defio = {
 | 
					 | 
				
			||||||
+	.delay		= HZ/100,
 | 
					 | 
				
			||||||
+	.deferred_io	= rpisense_fb_deferred_io,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int rpisense_fb_ioctl(struct fb_info *info, unsigned int cmd,
 | 
					 | 
				
			||||||
+			     unsigned long arg)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	switch (cmd) {
 | 
					 | 
				
			||||||
+	case SENSEFB_FBIOGET_GAMMA:
 | 
					 | 
				
			||||||
+		if (copy_to_user((void __user *) arg, rpisense_fb_param.gamma,
 | 
					 | 
				
			||||||
+				 sizeof(u8[32])))
 | 
					 | 
				
			||||||
+			return -EFAULT;
 | 
					 | 
				
			||||||
+		return 0;
 | 
					 | 
				
			||||||
+	case SENSEFB_FBIOSET_GAMMA:
 | 
					 | 
				
			||||||
+		if (copy_from_user(gamma_user, (void __user *)arg,
 | 
					 | 
				
			||||||
+				   sizeof(u8[32])))
 | 
					 | 
				
			||||||
+			return -EFAULT;
 | 
					 | 
				
			||||||
+		rpisense_fb_param.gamma = gamma_user;
 | 
					 | 
				
			||||||
+		schedule_delayed_work(&info->deferred_work,
 | 
					 | 
				
			||||||
+				      rpisense_fb_defio.delay);
 | 
					 | 
				
			||||||
+		return 0;
 | 
					 | 
				
			||||||
+	case SENSEFB_FBIORESET_GAMMA:
 | 
					 | 
				
			||||||
+		switch (arg) {
 | 
					 | 
				
			||||||
+		case 0:
 | 
					 | 
				
			||||||
+			rpisense_fb_param.gamma = gamma_default;
 | 
					 | 
				
			||||||
+			break;
 | 
					 | 
				
			||||||
+		case 1:
 | 
					 | 
				
			||||||
+			rpisense_fb_param.gamma = gamma_low;
 | 
					 | 
				
			||||||
+			break;
 | 
					 | 
				
			||||||
+		case 2:
 | 
					 | 
				
			||||||
+			rpisense_fb_param.gamma = gamma_user;
 | 
					 | 
				
			||||||
+			break;
 | 
					 | 
				
			||||||
+		default:
 | 
					 | 
				
			||||||
+			return -EINVAL;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+		schedule_delayed_work(&info->deferred_work,
 | 
					 | 
				
			||||||
+				      rpisense_fb_defio.delay);
 | 
					 | 
				
			||||||
+		break;
 | 
					 | 
				
			||||||
+	default:
 | 
					 | 
				
			||||||
+		return -EINVAL;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct fb_ops rpisense_fb_ops = {
 | 
					 | 
				
			||||||
+	.owner		= THIS_MODULE,
 | 
					 | 
				
			||||||
+	.fb_read	= fb_sys_read,
 | 
					 | 
				
			||||||
+	.fb_write	= rpisense_fb_write,
 | 
					 | 
				
			||||||
+	.fb_fillrect	= rpisense_fb_fillrect,
 | 
					 | 
				
			||||||
+	.fb_copyarea	= rpisense_fb_copyarea,
 | 
					 | 
				
			||||||
+	.fb_imageblit	= rpisense_fb_imageblit,
 | 
					 | 
				
			||||||
+	.fb_ioctl	= rpisense_fb_ioctl,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int rpisense_fb_probe(struct platform_device *pdev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct fb_info *info;
 | 
					 | 
				
			||||||
+	int ret = -ENOMEM;
 | 
					 | 
				
			||||||
+	struct rpisense_fb *rpisense_fb;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	rpisense = rpisense_get_dev();
 | 
					 | 
				
			||||||
+	rpisense_fb = &rpisense->framebuffer;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	rpisense_fb_param.vmem = vzalloc(rpisense_fb_param.vmemsize);
 | 
					 | 
				
			||||||
+	if (!rpisense_fb_param.vmem)
 | 
					 | 
				
			||||||
+		return ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	rpisense_fb_param.vmem_work = devm_kmalloc(&pdev->dev, 193, GFP_KERNEL);
 | 
					 | 
				
			||||||
+	if (!rpisense_fb_param.vmem_work)
 | 
					 | 
				
			||||||
+		goto err_malloc;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	info = framebuffer_alloc(0, &pdev->dev);
 | 
					 | 
				
			||||||
+	if (!info) {
 | 
					 | 
				
			||||||
+		dev_err(&pdev->dev, "Could not allocate framebuffer.\n");
 | 
					 | 
				
			||||||
+		goto err_malloc;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	rpisense_fb->info = info;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	rpisense_fb_fix.smem_start = (unsigned long)rpisense_fb_param.vmem;
 | 
					 | 
				
			||||||
+	rpisense_fb_fix.smem_len = rpisense_fb_param.vmemsize;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	info->fbops = &rpisense_fb_ops;
 | 
					 | 
				
			||||||
+	info->fix = rpisense_fb_fix;
 | 
					 | 
				
			||||||
+	info->var = rpisense_fb_var;
 | 
					 | 
				
			||||||
+	info->fbdefio = &rpisense_fb_defio;
 | 
					 | 
				
			||||||
+	info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
 | 
					 | 
				
			||||||
+	info->screen_base = rpisense_fb_param.vmem;
 | 
					 | 
				
			||||||
+	info->screen_size = rpisense_fb_param.vmemsize;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (lowlight)
 | 
					 | 
				
			||||||
+		rpisense_fb_param.gamma = gamma_low;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	fb_deferred_io_init(info);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ret = register_framebuffer(info);
 | 
					 | 
				
			||||||
+	if (ret < 0) {
 | 
					 | 
				
			||||||
+		dev_err(&pdev->dev, "Could not register framebuffer.\n");
 | 
					 | 
				
			||||||
+		goto err_fballoc;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	fb_info(info, "%s frame buffer device\n", info->fix.id);
 | 
					 | 
				
			||||||
+	schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+err_fballoc:
 | 
					 | 
				
			||||||
+	framebuffer_release(info);
 | 
					 | 
				
			||||||
+err_malloc:
 | 
					 | 
				
			||||||
+	vfree(rpisense_fb_param.vmem);
 | 
					 | 
				
			||||||
+	return ret;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int rpisense_fb_remove(struct platform_device *pdev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct rpisense_fb *rpisense_fb = &rpisense->framebuffer;
 | 
					 | 
				
			||||||
+	struct fb_info *info = rpisense_fb->info;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (info) {
 | 
					 | 
				
			||||||
+		unregister_framebuffer(info);
 | 
					 | 
				
			||||||
+		fb_deferred_io_cleanup(info);
 | 
					 | 
				
			||||||
+		framebuffer_release(info);
 | 
					 | 
				
			||||||
+		vfree(rpisense_fb_param.vmem);
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#ifdef CONFIG_OF
 | 
					 | 
				
			||||||
+static const struct of_device_id rpisense_fb_id[] = {
 | 
					 | 
				
			||||||
+	{ .compatible = "rpi,rpi-sense-fb" },
 | 
					 | 
				
			||||||
+	{ },
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+MODULE_DEVICE_TABLE(of, rpisense_fb_id);
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct platform_device_id rpisense_fb_device_id[] = {
 | 
					 | 
				
			||||||
+	{ .name = "rpi-sense-fb" },
 | 
					 | 
				
			||||||
+	{ },
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+MODULE_DEVICE_TABLE(platform, rpisense_fb_device_id);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct platform_driver rpisense_fb_driver = {
 | 
					 | 
				
			||||||
+	.probe = rpisense_fb_probe,
 | 
					 | 
				
			||||||
+	.remove = rpisense_fb_remove,
 | 
					 | 
				
			||||||
+	.driver = {
 | 
					 | 
				
			||||||
+		.name = "rpi-sense-fb",
 | 
					 | 
				
			||||||
+		.owner = THIS_MODULE,
 | 
					 | 
				
			||||||
+	},
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+module_platform_driver(rpisense_fb_driver);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+MODULE_DESCRIPTION("Raspberry Pi Sense HAT framebuffer driver");
 | 
					 | 
				
			||||||
+MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
 | 
					 | 
				
			||||||
+MODULE_LICENSE("GPL");
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/include/linux/mfd/rpisense/core.h
 | 
					 | 
				
			||||||
@@ -0,0 +1,47 @@
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ * Raspberry Pi Sense HAT core driver
 | 
					 | 
				
			||||||
+ * http://raspberrypi.org
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Copyright (C) 2015 Raspberry Pi
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Author: Serge Schneider
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ *  This program is free software; you can redistribute  it and/or modify it
 | 
					 | 
				
			||||||
+ *  under  the terms of  the GNU General  Public License as published by the
 | 
					 | 
				
			||||||
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
 | 
					 | 
				
			||||||
+ *  option) any later version.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#ifndef __LINUX_MFD_RPISENSE_CORE_H_
 | 
					 | 
				
			||||||
+#define __LINUX_MFD_RPISENSE_CORE_H_
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#include <linux/mfd/rpisense/joystick.h>
 | 
					 | 
				
			||||||
+#include <linux/mfd/rpisense/framebuffer.h>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ * Register values.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+#define RPISENSE_FB			0x00
 | 
					 | 
				
			||||||
+#define RPISENSE_WAI			0xF0
 | 
					 | 
				
			||||||
+#define RPISENSE_VER			0xF1
 | 
					 | 
				
			||||||
+#define RPISENSE_KEYS			0xF2
 | 
					 | 
				
			||||||
+#define RPISENSE_EE_WP			0xF3
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define RPISENSE_ID			's'
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+struct rpisense {
 | 
					 | 
				
			||||||
+	struct device *dev;
 | 
					 | 
				
			||||||
+	struct i2c_client *i2c_client;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* Client devices */
 | 
					 | 
				
			||||||
+	struct rpisense_js joystick;
 | 
					 | 
				
			||||||
+	struct rpisense_fb framebuffer;
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+struct rpisense *rpisense_get_dev(void);
 | 
					 | 
				
			||||||
+s32 rpisense_reg_read(struct rpisense *rpisense, int reg);
 | 
					 | 
				
			||||||
+int rpisense_reg_write(struct rpisense *rpisense, int reg, u16 val);
 | 
					 | 
				
			||||||
+int rpisense_block_write(struct rpisense *rpisense, const char *buf, int count);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/include/linux/mfd/rpisense/framebuffer.h
 | 
					 | 
				
			||||||
@@ -0,0 +1,32 @@
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ * Raspberry Pi Sense HAT framebuffer driver
 | 
					 | 
				
			||||||
+ * http://raspberrypi.org
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Copyright (C) 2015 Raspberry Pi
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Author: Serge Schneider
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ *  This program is free software; you can redistribute  it and/or modify it
 | 
					 | 
				
			||||||
+ *  under  the terms of  the GNU General  Public License as published by the
 | 
					 | 
				
			||||||
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
 | 
					 | 
				
			||||||
+ *  option) any later version.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#ifndef __LINUX_RPISENSE_FB_H_
 | 
					 | 
				
			||||||
+#define __LINUX_RPISENSE_FB_H_
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define SENSEFB_FBIO_IOC_MAGIC 0xF1
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define SENSEFB_FBIOGET_GAMMA _IO(SENSEFB_FBIO_IOC_MAGIC, 0)
 | 
					 | 
				
			||||||
+#define SENSEFB_FBIOSET_GAMMA _IO(SENSEFB_FBIO_IOC_MAGIC, 1)
 | 
					 | 
				
			||||||
+#define SENSEFB_FBIORESET_GAMMA _IO(SENSEFB_FBIO_IOC_MAGIC, 2)
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+struct rpisense;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+struct rpisense_fb {
 | 
					 | 
				
			||||||
+	struct platform_device *pdev;
 | 
					 | 
				
			||||||
+	struct fb_info *info;
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/include/linux/mfd/rpisense/joystick.h
 | 
					 | 
				
			||||||
@@ -0,0 +1,35 @@
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ * Raspberry Pi Sense HAT joystick driver
 | 
					 | 
				
			||||||
+ * http://raspberrypi.org
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Copyright (C) 2015 Raspberry Pi
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Author: Serge Schneider
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ *  This program is free software; you can redistribute  it and/or modify it
 | 
					 | 
				
			||||||
+ *  under  the terms of  the GNU General  Public License as published by the
 | 
					 | 
				
			||||||
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
 | 
					 | 
				
			||||||
+ *  option) any later version.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#ifndef __LINUX_RPISENSE_JOYSTICK_H_
 | 
					 | 
				
			||||||
+#define __LINUX_RPISENSE_JOYSTICK_H_
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#include <linux/input.h>
 | 
					 | 
				
			||||||
+#include <linux/interrupt.h>
 | 
					 | 
				
			||||||
+#include <linux/gpio/consumer.h>
 | 
					 | 
				
			||||||
+#include <linux/platform_device.h>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+struct rpisense;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+struct rpisense_js {
 | 
					 | 
				
			||||||
+	struct platform_device *pdev;
 | 
					 | 
				
			||||||
+	struct input_dev *keys_dev;
 | 
					 | 
				
			||||||
+	struct gpio_desc *keys_desc;
 | 
					 | 
				
			||||||
+	struct work_struct keys_work_s;
 | 
					 | 
				
			||||||
+	int keys_irq;
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,46 +0,0 @@
 | 
				
			|||||||
From 4be98b29d123fc7615ea8404fbda450ec8acb084 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Joerg Schambacher <joerg@i2audio.com>
 | 
					 | 
				
			||||||
Date: Fri, 16 Oct 2020 15:17:07 +0200
 | 
					 | 
				
			||||||
Subject: [PATCH] Fixes a problem when module probes before i2c module
 | 
					 | 
				
			||||||
 is available
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The driver crashed while a NULL pointer returned by i2c_get_adapter()
 | 
					 | 
				
			||||||
has been used to access the i2c bus functions.
 | 
					 | 
				
			||||||
The headphone probing function hb_hp_probe() now returns -EPROBE_DEFER
 | 
					 | 
				
			||||||
in case the i2c module has not been loaded yet.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Joerg Schambacher <joerg@i2audio.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 sound/soc/bcm/hifiberry_dacplus.c | 9 +++++++--
 | 
					 | 
				
			||||||
 1 file changed, 7 insertions(+), 2 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/sound/soc/bcm/hifiberry_dacplus.c
 | 
					 | 
				
			||||||
+++ b/sound/soc/bcm/hifiberry_dacplus.c
 | 
					 | 
				
			||||||
@@ -315,12 +315,14 @@ static int hb_hp_detect(void)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct i2c_adapter *adap = i2c_get_adapter(1);
 | 
					 | 
				
			||||||
 	int ret;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 	struct i2c_client tpa_i2c_client = {
 | 
					 | 
				
			||||||
 		.addr = 0x60,
 | 
					 | 
				
			||||||
 		.adapter = adap,
 | 
					 | 
				
			||||||
 	};
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	if (!adap)
 | 
					 | 
				
			||||||
+		return -EPROBE_DEFER;	/* I2C module not yet available */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	ret = i2c_smbus_read_byte(&tpa_i2c_client) >= 0;
 | 
					 | 
				
			||||||
 	i2c_put_adapter(adap);
 | 
					 | 
				
			||||||
 	return ret;
 | 
					 | 
				
			||||||
@@ -342,7 +344,10 @@ static int snd_rpi_hifiberry_dacplus_pro
 | 
					 | 
				
			||||||
 	struct of_changeset ocs;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* probe for head phone amp */
 | 
					 | 
				
			||||||
-	if (hb_hp_detect()) {
 | 
					 | 
				
			||||||
+	ret = hb_hp_detect();
 | 
					 | 
				
			||||||
+	if (ret < 0)
 | 
					 | 
				
			||||||
+		return ret;
 | 
					 | 
				
			||||||
+	if (ret) {
 | 
					 | 
				
			||||||
 		card->aux_dev = hifiberry_dacplus_aux_devs;
 | 
					 | 
				
			||||||
 		card->num_aux_devs =
 | 
					 | 
				
			||||||
 				ARRAY_SIZE(hifiberry_dacplus_aux_devs);
 | 
					 | 
				
			||||||
@ -1,172 +0,0 @@
 | 
				
			|||||||
From 3bbdfc9493655ea9ff7e3c8eeee85f28b0f84369 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: P33M <P33M@github.com>
 | 
					 | 
				
			||||||
Date: Wed, 21 Oct 2015 14:55:21 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] rpi_display: add backlight driver and overlay
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add a mailbox-driven backlight controller for the Raspberry Pi DSI
 | 
					 | 
				
			||||||
touchscreen display. Requires updated GPU firmware to recognise the
 | 
					 | 
				
			||||||
mailbox request.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Gordon Hollingworth <gordon@raspberrypi.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add Raspberry Pi firmware driver to the dependencies of backlight driver
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Otherwise the backlight driver fails to build if the firmware
 | 
					 | 
				
			||||||
loading driver is not in the kernel
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Alex Riesen <alexander.riesen@cetitec.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/video/backlight/Kconfig         |   7 ++
 | 
					 | 
				
			||||||
 drivers/video/backlight/Makefile        |   1 +
 | 
					 | 
				
			||||||
 drivers/video/backlight/rpi_backlight.c | 119 ++++++++++++++++++++++++
 | 
					 | 
				
			||||||
 3 files changed, 127 insertions(+)
 | 
					 | 
				
			||||||
 create mode 100644 drivers/video/backlight/rpi_backlight.c
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/video/backlight/Kconfig
 | 
					 | 
				
			||||||
+++ b/drivers/video/backlight/Kconfig
 | 
					 | 
				
			||||||
@@ -248,6 +248,13 @@ config BACKLIGHT_PWM
 | 
					 | 
				
			||||||
 	  If you have a LCD backlight adjustable by PWM, say Y to enable
 | 
					 | 
				
			||||||
 	  this driver.
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+config BACKLIGHT_RPI
 | 
					 | 
				
			||||||
+	tristate "Raspberry Pi display firmware driven backlight"
 | 
					 | 
				
			||||||
+	depends on RASPBERRYPI_FIRMWARE
 | 
					 | 
				
			||||||
+	help
 | 
					 | 
				
			||||||
+	  If you have the Raspberry Pi DSI touchscreen display, say Y to
 | 
					 | 
				
			||||||
+	  enable the mailbox-controlled backlight driver.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 config BACKLIGHT_DA903X
 | 
					 | 
				
			||||||
 	tristate "Backlight Driver for DA9030/DA9034 using WLED"
 | 
					 | 
				
			||||||
 	depends on PMIC_DA903X
 | 
					 | 
				
			||||||
--- a/drivers/video/backlight/Makefile
 | 
					 | 
				
			||||||
+++ b/drivers/video/backlight/Makefile
 | 
					 | 
				
			||||||
@@ -49,6 +49,7 @@ obj-$(CONFIG_BACKLIGHT_PANDORA)		+= pand
 | 
					 | 
				
			||||||
 obj-$(CONFIG_BACKLIGHT_PCF50633)	+= pcf50633-backlight.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_BACKLIGHT_PWM)		+= pwm_bl.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_BACKLIGHT_QCOM_WLED)	+= qcom-wled.o
 | 
					 | 
				
			||||||
+obj-$(CONFIG_BACKLIGHT_RPI)			+= rpi_backlight.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_BACKLIGHT_SAHARA)		+= kb3886_bl.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_BACKLIGHT_SKY81452)	+= sky81452-backlight.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_BACKLIGHT_TOSA)		+= tosa_bl.o
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/drivers/video/backlight/rpi_backlight.c
 | 
					 | 
				
			||||||
@@ -0,0 +1,119 @@
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ * rpi_bl.c - Backlight controller through VPU
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * 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/backlight.h>
 | 
					 | 
				
			||||||
+#include <linux/err.h>
 | 
					 | 
				
			||||||
+#include <linux/fb.h>
 | 
					 | 
				
			||||||
+#include <linux/gpio.h>
 | 
					 | 
				
			||||||
+#include <linux/init.h>
 | 
					 | 
				
			||||||
+#include <linux/kernel.h>
 | 
					 | 
				
			||||||
+#include <linux/module.h>
 | 
					 | 
				
			||||||
+#include <linux/of.h>
 | 
					 | 
				
			||||||
+#include <linux/of_gpio.h>
 | 
					 | 
				
			||||||
+#include <linux/platform_device.h>
 | 
					 | 
				
			||||||
+#include <linux/slab.h>
 | 
					 | 
				
			||||||
+#include <soc/bcm2835/raspberrypi-firmware.h>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+struct rpi_backlight {
 | 
					 | 
				
			||||||
+	struct device *dev;
 | 
					 | 
				
			||||||
+	struct device *fbdev;
 | 
					 | 
				
			||||||
+	struct rpi_firmware *fw;
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int rpi_backlight_update_status(struct backlight_device *bl)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct rpi_backlight *gbl = bl_get_data(bl);
 | 
					 | 
				
			||||||
+	int brightness = bl->props.brightness;
 | 
					 | 
				
			||||||
+	int ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (bl->props.power != FB_BLANK_UNBLANK ||
 | 
					 | 
				
			||||||
+	    bl->props.fb_blank != FB_BLANK_UNBLANK ||
 | 
					 | 
				
			||||||
+	    bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
 | 
					 | 
				
			||||||
+		brightness = 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ret = rpi_firmware_property(gbl->fw,
 | 
					 | 
				
			||||||
+			RPI_FIRMWARE_FRAMEBUFFER_SET_BACKLIGHT,
 | 
					 | 
				
			||||||
+			&brightness, sizeof(brightness));
 | 
					 | 
				
			||||||
+	if (ret) {
 | 
					 | 
				
			||||||
+		dev_err(gbl->dev, "Failed to set brightness\n");
 | 
					 | 
				
			||||||
+		return ret;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (brightness < 0) {
 | 
					 | 
				
			||||||
+		dev_err(gbl->dev, "Backlight change failed\n");
 | 
					 | 
				
			||||||
+		return -EAGAIN;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static const struct backlight_ops rpi_backlight_ops = {
 | 
					 | 
				
			||||||
+	.options	= BL_CORE_SUSPENDRESUME,
 | 
					 | 
				
			||||||
+	.update_status	= rpi_backlight_update_status,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int rpi_backlight_probe(struct platform_device *pdev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct backlight_properties props;
 | 
					 | 
				
			||||||
+	struct backlight_device *bl;
 | 
					 | 
				
			||||||
+	struct rpi_backlight *gbl;
 | 
					 | 
				
			||||||
+	struct device_node *fw_node;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	gbl = devm_kzalloc(&pdev->dev, sizeof(*gbl), GFP_KERNEL);
 | 
					 | 
				
			||||||
+	if (gbl == NULL)
 | 
					 | 
				
			||||||
+		return -ENOMEM;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	gbl->dev = &pdev->dev;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	fw_node = of_parse_phandle(pdev->dev.of_node, "firmware", 0);
 | 
					 | 
				
			||||||
+	if (!fw_node) {
 | 
					 | 
				
			||||||
+		dev_err(&pdev->dev, "Missing firmware node\n");
 | 
					 | 
				
			||||||
+		return -ENOENT;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	gbl->fw = rpi_firmware_get(fw_node);
 | 
					 | 
				
			||||||
+	if (!gbl->fw)
 | 
					 | 
				
			||||||
+		return -EPROBE_DEFER;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	memset(&props, 0, sizeof(props));
 | 
					 | 
				
			||||||
+	props.type = BACKLIGHT_RAW;
 | 
					 | 
				
			||||||
+	props.max_brightness = 255;
 | 
					 | 
				
			||||||
+	bl = devm_backlight_device_register(&pdev->dev, dev_name(&pdev->dev),
 | 
					 | 
				
			||||||
+					&pdev->dev, gbl, &rpi_backlight_ops,
 | 
					 | 
				
			||||||
+					&props);
 | 
					 | 
				
			||||||
+	if (IS_ERR(bl)) {
 | 
					 | 
				
			||||||
+		dev_err(&pdev->dev, "failed to register backlight\n");
 | 
					 | 
				
			||||||
+		return PTR_ERR(bl);
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	bl->props.brightness = 255;
 | 
					 | 
				
			||||||
+	backlight_update_status(bl);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	platform_set_drvdata(pdev, bl);
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static const struct of_device_id rpi_backlight_of_match[] = {
 | 
					 | 
				
			||||||
+	{ .compatible = "raspberrypi,rpi-backlight" },
 | 
					 | 
				
			||||||
+	{ /* sentinel */ }
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+MODULE_DEVICE_TABLE(of, rpi_backlight_of_match);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct platform_driver rpi_backlight_driver = {
 | 
					 | 
				
			||||||
+	.driver		= {
 | 
					 | 
				
			||||||
+		.name		= "rpi-backlight",
 | 
					 | 
				
			||||||
+		.of_match_table = of_match_ptr(rpi_backlight_of_match),
 | 
					 | 
				
			||||||
+	},
 | 
					 | 
				
			||||||
+	.probe		= rpi_backlight_probe,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+module_platform_driver(rpi_backlight_driver);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+MODULE_AUTHOR("Gordon Hollingworth <gordon@raspberrypi.org>");
 | 
					 | 
				
			||||||
+MODULE_DESCRIPTION("Raspberry Pi mailbox based Backlight Driver");
 | 
					 | 
				
			||||||
+MODULE_LICENSE("GPL");
 | 
					 | 
				
			||||||
@ -1,256 +0,0 @@
 | 
				
			|||||||
From f82deda879262289568dec22dd43ecc10c265fb6 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: popcornmix <popcornmix@gmail.com>
 | 
					 | 
				
			||||||
Date: Tue, 23 Feb 2016 19:56:04 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] bcm2835-virtgpio: Virtual GPIO driver
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add a virtual GPIO driver that uses the firmware mailbox interface to
 | 
					 | 
				
			||||||
request that the VPU toggles LEDs.
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/gpio/Kconfig         |   6 +
 | 
					 | 
				
			||||||
 drivers/gpio/Makefile        |   1 +
 | 
					 | 
				
			||||||
 drivers/gpio/gpio-bcm-virt.c | 214 +++++++++++++++++++++++++++++++++++
 | 
					 | 
				
			||||||
 3 files changed, 221 insertions(+)
 | 
					 | 
				
			||||||
 create mode 100644 drivers/gpio/gpio-bcm-virt.c
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/gpio/Kconfig
 | 
					 | 
				
			||||||
+++ b/drivers/gpio/Kconfig
 | 
					 | 
				
			||||||
@@ -193,6 +193,12 @@ config GPIO_BCM_XGS_IPROC
 | 
					 | 
				
			||||||
 	help
 | 
					 | 
				
			||||||
 	  Say yes here to enable GPIO support for Broadcom XGS iProc SoCs.
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+config GPIO_BCM_VIRT
 | 
					 | 
				
			||||||
+	bool "Broadcom Virt GPIO"
 | 
					 | 
				
			||||||
+	depends on OF_GPIO && RASPBERRYPI_FIRMWARE && (ARCH_BCM2835 || COMPILE_TEST)
 | 
					 | 
				
			||||||
+	help
 | 
					 | 
				
			||||||
+	  Turn on virtual GPIO support for Broadcom BCM283X chips.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 config GPIO_BRCMSTB
 | 
					 | 
				
			||||||
 	tristate "BRCMSTB GPIO support"
 | 
					 | 
				
			||||||
 	default y if (ARCH_BRCMSTB || BMIPS_GENERIC)
 | 
					 | 
				
			||||||
--- a/drivers/gpio/Makefile
 | 
					 | 
				
			||||||
+++ b/drivers/gpio/Makefile
 | 
					 | 
				
			||||||
@@ -38,6 +38,7 @@ obj-$(CONFIG_GPIO_ASPEED_SGPIO)		+= gpio
 | 
					 | 
				
			||||||
 obj-$(CONFIG_GPIO_ATH79)		+= gpio-ath79.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_GPIO_BCM_KONA)		+= gpio-bcm-kona.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_GPIO_BCM_XGS_IPROC)	+= gpio-xgs-iproc.o
 | 
					 | 
				
			||||||
+obj-$(CONFIG_GPIO_BCM_VIRT)		+= gpio-bcm-virt.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_GPIO_BD70528)		+= gpio-bd70528.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_GPIO_BD71828)		+= gpio-bd71828.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_GPIO_BD9571MWV)		+= gpio-bd9571mwv.o
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/drivers/gpio/gpio-bcm-virt.c
 | 
					 | 
				
			||||||
@@ -0,0 +1,214 @@
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ *  brcmvirt GPIO driver
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ *  Copyright (C) 2012,2013 Dom Cobley <popcornmix@gmail.com>
 | 
					 | 
				
			||||||
+ *  Based on gpio-clps711x.c by Alexander Shiyan <shc_work@mail.ru>
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * This program is free software; you can redistribute it and/or modify
 | 
					 | 
				
			||||||
+ * it under the terms of the GNU General Public License as published by
 | 
					 | 
				
			||||||
+ * the Free Software Foundation; either version 2 of the License, or
 | 
					 | 
				
			||||||
+ * (at your option) any later version.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#include <linux/err.h>
 | 
					 | 
				
			||||||
+#include <linux/gpio.h>
 | 
					 | 
				
			||||||
+#include <linux/module.h>
 | 
					 | 
				
			||||||
+#include <linux/platform_device.h>
 | 
					 | 
				
			||||||
+#include <linux/dma-mapping.h>
 | 
					 | 
				
			||||||
+#include <soc/bcm2835/raspberrypi-firmware.h>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define MODULE_NAME "brcmvirt-gpio"
 | 
					 | 
				
			||||||
+#define NUM_GPIO 2
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+struct brcmvirt_gpio {
 | 
					 | 
				
			||||||
+	struct gpio_chip	gc;
 | 
					 | 
				
			||||||
+	u32 __iomem		*ts_base;
 | 
					 | 
				
			||||||
+	/* two packed 16-bit counts of enabled and disables
 | 
					 | 
				
			||||||
+           Allows host to detect a brief enable that was missed */
 | 
					 | 
				
			||||||
+	u32			enables_disables[NUM_GPIO];
 | 
					 | 
				
			||||||
+	dma_addr_t		bus_addr;
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int brcmvirt_gpio_dir_in(struct gpio_chip *gc, unsigned off)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct brcmvirt_gpio *gpio;
 | 
					 | 
				
			||||||
+	gpio = container_of(gc, struct brcmvirt_gpio, gc);
 | 
					 | 
				
			||||||
+	return -EINVAL;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int brcmvirt_gpio_dir_out(struct gpio_chip *gc, unsigned off, int val)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct brcmvirt_gpio *gpio;
 | 
					 | 
				
			||||||
+	gpio = container_of(gc, struct brcmvirt_gpio, gc);
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int brcmvirt_gpio_get(struct gpio_chip *gc, unsigned off)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct brcmvirt_gpio *gpio;
 | 
					 | 
				
			||||||
+	unsigned v;
 | 
					 | 
				
			||||||
+	gpio = container_of(gc, struct brcmvirt_gpio, gc);
 | 
					 | 
				
			||||||
+	v = readl(gpio->ts_base + off);
 | 
					 | 
				
			||||||
+	return (v >> off) & 1;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void brcmvirt_gpio_set(struct gpio_chip *gc, unsigned off, int val)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct brcmvirt_gpio *gpio;
 | 
					 | 
				
			||||||
+	u16 enables, disables;
 | 
					 | 
				
			||||||
+	s16 diff;
 | 
					 | 
				
			||||||
+	bool lit;
 | 
					 | 
				
			||||||
+	gpio = container_of(gc, struct brcmvirt_gpio, gc);
 | 
					 | 
				
			||||||
+	enables  = gpio->enables_disables[off] >> 16;
 | 
					 | 
				
			||||||
+	disables = gpio->enables_disables[off] >>  0;
 | 
					 | 
				
			||||||
+	diff = (s16)(enables - disables);
 | 
					 | 
				
			||||||
+	lit = diff > 0;
 | 
					 | 
				
			||||||
+	if ((val && lit) || (!val && !lit))
 | 
					 | 
				
			||||||
+		return;
 | 
					 | 
				
			||||||
+	if (val)
 | 
					 | 
				
			||||||
+		enables++;
 | 
					 | 
				
			||||||
+	else
 | 
					 | 
				
			||||||
+		disables++;
 | 
					 | 
				
			||||||
+	diff = (s16)(enables - disables);
 | 
					 | 
				
			||||||
+	BUG_ON(diff != 0 && diff != 1);
 | 
					 | 
				
			||||||
+	gpio->enables_disables[off] = (enables << 16) | (disables << 0);
 | 
					 | 
				
			||||||
+	writel(gpio->enables_disables[off], gpio->ts_base + off);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int brcmvirt_gpio_probe(struct platform_device *pdev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int err = 0;
 | 
					 | 
				
			||||||
+	struct device *dev = &pdev->dev;
 | 
					 | 
				
			||||||
+	struct device_node *np = dev->of_node;
 | 
					 | 
				
			||||||
+	struct device_node *fw_node;
 | 
					 | 
				
			||||||
+	struct rpi_firmware *fw;
 | 
					 | 
				
			||||||
+	struct brcmvirt_gpio *ucb;
 | 
					 | 
				
			||||||
+	u32 gpiovirtbuf;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	fw_node = of_parse_phandle(np, "firmware", 0);
 | 
					 | 
				
			||||||
+	if (!fw_node) {
 | 
					 | 
				
			||||||
+		dev_err(dev, "Missing firmware node\n");
 | 
					 | 
				
			||||||
+		return -ENOENT;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	fw = rpi_firmware_get(fw_node);
 | 
					 | 
				
			||||||
+	if (!fw)
 | 
					 | 
				
			||||||
+		return -EPROBE_DEFER;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ucb = devm_kzalloc(dev, sizeof *ucb, GFP_KERNEL);
 | 
					 | 
				
			||||||
+	if (!ucb) {
 | 
					 | 
				
			||||||
+		err = -EINVAL;
 | 
					 | 
				
			||||||
+		goto out;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ucb->ts_base = dma_alloc_coherent(dev, PAGE_SIZE, &ucb->bus_addr, GFP_KERNEL);
 | 
					 | 
				
			||||||
+	if (!ucb->ts_base) {
 | 
					 | 
				
			||||||
+		pr_err("[%s]: failed to dma_alloc_coherent(%ld)\n",
 | 
					 | 
				
			||||||
+				__func__, PAGE_SIZE);
 | 
					 | 
				
			||||||
+		err = -ENOMEM;
 | 
					 | 
				
			||||||
+		goto out;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	gpiovirtbuf = (u32)ucb->bus_addr;
 | 
					 | 
				
			||||||
+	err = rpi_firmware_property(fw, RPI_FIRMWARE_FRAMEBUFFER_SET_GPIOVIRTBUF,
 | 
					 | 
				
			||||||
+				    &gpiovirtbuf, sizeof(gpiovirtbuf));
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (err || gpiovirtbuf != 0) {
 | 
					 | 
				
			||||||
+		dev_warn(dev, "Failed to set gpiovirtbuf, trying to get err:%x\n", err);
 | 
					 | 
				
			||||||
+		dma_free_coherent(dev, PAGE_SIZE, ucb->ts_base, ucb->bus_addr);
 | 
					 | 
				
			||||||
+		ucb->ts_base = 0;
 | 
					 | 
				
			||||||
+		ucb->bus_addr = 0;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (!ucb->ts_base) {
 | 
					 | 
				
			||||||
+		err = rpi_firmware_property(fw, RPI_FIRMWARE_FRAMEBUFFER_GET_GPIOVIRTBUF,
 | 
					 | 
				
			||||||
+					    &gpiovirtbuf, sizeof(gpiovirtbuf));
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		if (err) {
 | 
					 | 
				
			||||||
+			dev_err(dev, "Failed to get gpiovirtbuf\n");
 | 
					 | 
				
			||||||
+			goto out;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		if (!gpiovirtbuf) {
 | 
					 | 
				
			||||||
+			dev_err(dev, "No virtgpio buffer\n");
 | 
					 | 
				
			||||||
+			err = -ENOENT;
 | 
					 | 
				
			||||||
+			goto out;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		// mmap the physical memory
 | 
					 | 
				
			||||||
+		gpiovirtbuf &= ~0xc0000000;
 | 
					 | 
				
			||||||
+		ucb->ts_base = ioremap(gpiovirtbuf, 4096);
 | 
					 | 
				
			||||||
+		if (ucb->ts_base == NULL) {
 | 
					 | 
				
			||||||
+			dev_err(dev, "Failed to map physical address\n");
 | 
					 | 
				
			||||||
+			err = -ENOENT;
 | 
					 | 
				
			||||||
+			goto out;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+		ucb->bus_addr = 0;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	ucb->gc.label = MODULE_NAME;
 | 
					 | 
				
			||||||
+	ucb->gc.owner = THIS_MODULE;
 | 
					 | 
				
			||||||
+	//ucb->gc.dev = dev;
 | 
					 | 
				
			||||||
+	ucb->gc.of_node = np;
 | 
					 | 
				
			||||||
+	ucb->gc.base = 100;
 | 
					 | 
				
			||||||
+	ucb->gc.ngpio = NUM_GPIO;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ucb->gc.direction_input = brcmvirt_gpio_dir_in;
 | 
					 | 
				
			||||||
+	ucb->gc.direction_output = brcmvirt_gpio_dir_out;
 | 
					 | 
				
			||||||
+	ucb->gc.get = brcmvirt_gpio_get;
 | 
					 | 
				
			||||||
+	ucb->gc.set = brcmvirt_gpio_set;
 | 
					 | 
				
			||||||
+	ucb->gc.can_sleep = true;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	err = gpiochip_add(&ucb->gc);
 | 
					 | 
				
			||||||
+	if (err)
 | 
					 | 
				
			||||||
+		goto out;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	platform_set_drvdata(pdev, ucb);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+out:
 | 
					 | 
				
			||||||
+	if (ucb->bus_addr) {
 | 
					 | 
				
			||||||
+		dma_free_coherent(dev, PAGE_SIZE, ucb->ts_base, ucb->bus_addr);
 | 
					 | 
				
			||||||
+		ucb->bus_addr = 0;
 | 
					 | 
				
			||||||
+		ucb->ts_base = NULL;
 | 
					 | 
				
			||||||
+	} else if (ucb->ts_base) {
 | 
					 | 
				
			||||||
+		iounmap(ucb->ts_base);
 | 
					 | 
				
			||||||
+		ucb->ts_base = NULL;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	return err;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int brcmvirt_gpio_remove(struct platform_device *pdev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct device *dev = &pdev->dev;
 | 
					 | 
				
			||||||
+	int err = 0;
 | 
					 | 
				
			||||||
+	struct brcmvirt_gpio *ucb = platform_get_drvdata(pdev);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	gpiochip_remove(&ucb->gc);
 | 
					 | 
				
			||||||
+	if (ucb->bus_addr)
 | 
					 | 
				
			||||||
+		dma_free_coherent(dev, PAGE_SIZE, ucb->ts_base, ucb->bus_addr);
 | 
					 | 
				
			||||||
+	else if (ucb->ts_base)
 | 
					 | 
				
			||||||
+		iounmap(ucb->ts_base);
 | 
					 | 
				
			||||||
+	return err;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static const struct of_device_id __maybe_unused brcmvirt_gpio_ids[] = {
 | 
					 | 
				
			||||||
+	{ .compatible = "brcm,bcm2835-virtgpio" },
 | 
					 | 
				
			||||||
+	{ }
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+MODULE_DEVICE_TABLE(of, brcmvirt_gpio_ids);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct platform_driver brcmvirt_gpio_driver = {
 | 
					 | 
				
			||||||
+	.driver	= {
 | 
					 | 
				
			||||||
+		.name		= MODULE_NAME,
 | 
					 | 
				
			||||||
+		.owner		= THIS_MODULE,
 | 
					 | 
				
			||||||
+		.of_match_table	= of_match_ptr(brcmvirt_gpio_ids),
 | 
					 | 
				
			||||||
+	},
 | 
					 | 
				
			||||||
+	.probe	= brcmvirt_gpio_probe,
 | 
					 | 
				
			||||||
+	.remove	= brcmvirt_gpio_remove,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+module_platform_driver(brcmvirt_gpio_driver);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+MODULE_LICENSE("GPL");
 | 
					 | 
				
			||||||
+MODULE_AUTHOR("Dom Cobley <popcornmix@gmail.com>");
 | 
					 | 
				
			||||||
+MODULE_DESCRIPTION("brcmvirt GPIO driver");
 | 
					 | 
				
			||||||
+MODULE_ALIAS("platform:brcmvirt-gpio");
 | 
					 | 
				
			||||||
@ -1,427 +0,0 @@
 | 
				
			|||||||
From 3027a12f910d164be1cb7c028dd178409ace7f0a Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
 | 
					 | 
				
			||||||
Date: Wed, 3 Dec 2014 13:23:28 +0200
 | 
					 | 
				
			||||||
Subject: [PATCH] OF: DT-Overlay configfs interface
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This is a port of Pantelis Antoniou's v3 port that makes use of the
 | 
					 | 
				
			||||||
new upstreamed configfs support for binary attributes.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Original commit message:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add a runtime interface to using configfs for generic device tree overlay
 | 
					 | 
				
			||||||
usage. With it its possible to use device tree overlays without having
 | 
					 | 
				
			||||||
to use a per-platform overlay manager.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Please see Documentation/devicetree/configfs-overlays.txt for more info.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Changes since v2:
 | 
					 | 
				
			||||||
- Removed ifdef CONFIG_OF_OVERLAY (since for now it's required)
 | 
					 | 
				
			||||||
- Created a documentation entry
 | 
					 | 
				
			||||||
- Slight rewording in Kconfig
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Changes since v1:
 | 
					 | 
				
			||||||
- of_resolve() -> of_resolve_phandles().
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Originally-signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DT configfs: Fix build errors on other platforms
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DT configfs: fix build error
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
There is an error when compiling rpi-4.6.y branch:
 | 
					 | 
				
			||||||
  CC      drivers/of/configfs.o
 | 
					 | 
				
			||||||
drivers/of/configfs.c:291:21: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
 | 
					 | 
				
			||||||
   .default_groups = of_cfs_def_groups,
 | 
					 | 
				
			||||||
                     ^
 | 
					 | 
				
			||||||
drivers/of/configfs.c:291:21: note: (near initialization for 'of_cfs_subsys.su_group.default_groups.next')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The .default_groups is linked list since commit
 | 
					 | 
				
			||||||
1ae1602de028acaa42a0f6ff18d19756f8e825c6.
 | 
					 | 
				
			||||||
This commit uses configfs_add_default_group to fix this problem.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Slawomir Stepien <sst@poczta.fm>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
configfs: New of_overlay API
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 .../devicetree/configfs-overlays.txt          |  31 ++
 | 
					 | 
				
			||||||
 drivers/of/Kconfig                            |   7 +
 | 
					 | 
				
			||||||
 drivers/of/Makefile                           |   1 +
 | 
					 | 
				
			||||||
 drivers/of/configfs.c                         | 310 ++++++++++++++++++
 | 
					 | 
				
			||||||
 4 files changed, 349 insertions(+)
 | 
					 | 
				
			||||||
 create mode 100644 Documentation/devicetree/configfs-overlays.txt
 | 
					 | 
				
			||||||
 create mode 100644 drivers/of/configfs.c
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/Documentation/devicetree/configfs-overlays.txt
 | 
					 | 
				
			||||||
@@ -0,0 +1,31 @@
 | 
					 | 
				
			||||||
+Howto use the configfs overlay interface.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+A device-tree configfs entry is created in /config/device-tree/overlays
 | 
					 | 
				
			||||||
+and and it is manipulated using standard file system I/O.
 | 
					 | 
				
			||||||
+Note that this is a debug level interface, for use by developers and
 | 
					 | 
				
			||||||
+not necessarily something accessed by normal users due to the
 | 
					 | 
				
			||||||
+security implications of having direct access to the kernel's device tree.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+* To create an overlay you mkdir the directory:
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	# mkdir /config/device-tree/overlays/foo
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+* Either you echo the overlay firmware file to the path property file.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	# echo foo.dtbo >/config/device-tree/overlays/foo/path
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+* Or you cat the contents of the overlay to the dtbo file
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	# cat foo.dtbo >/config/device-tree/overlays/foo/dtbo
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+The overlay file will be applied, and devices will be created/destroyed
 | 
					 | 
				
			||||||
+as required.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+To remove it simply rmdir the directory.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	# rmdir /config/device-tree/overlays/foo
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+The rationalle of the dual interface (firmware & direct copy) is that each is
 | 
					 | 
				
			||||||
+better suited to different use patterns. The firmware interface is what's
 | 
					 | 
				
			||||||
+intended to be used by hardware managers in the kernel, while the copy interface
 | 
					 | 
				
			||||||
+make sense for developers (since it avoids problems with namespaces).
 | 
					 | 
				
			||||||
--- a/drivers/of/Kconfig
 | 
					 | 
				
			||||||
+++ b/drivers/of/Kconfig
 | 
					 | 
				
			||||||
@@ -100,4 +100,11 @@ config OF_DMA_DEFAULT_COHERENT
 | 
					 | 
				
			||||||
 	# arches should select this if DMA is coherent by default for OF devices
 | 
					 | 
				
			||||||
 	bool
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+config OF_CONFIGFS
 | 
					 | 
				
			||||||
+	bool "Device Tree Overlay ConfigFS interface"
 | 
					 | 
				
			||||||
+	select CONFIGFS_FS
 | 
					 | 
				
			||||||
+	select OF_OVERLAY
 | 
					 | 
				
			||||||
+	help
 | 
					 | 
				
			||||||
+	  Enable a simple user-space driven DT overlay interface.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 endif # OF
 | 
					 | 
				
			||||||
--- a/drivers/of/Makefile
 | 
					 | 
				
			||||||
+++ b/drivers/of/Makefile
 | 
					 | 
				
			||||||
@@ -1,6 +1,7 @@
 | 
					 | 
				
			||||||
 # SPDX-License-Identifier: GPL-2.0
 | 
					 | 
				
			||||||
 obj-y = base.o device.o platform.o property.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_OF_KOBJ) += kobj.o
 | 
					 | 
				
			||||||
+obj-$(CONFIG_OF_CONFIGFS) += configfs.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_OF_DYNAMIC) += dynamic.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_OF_FLATTREE) += fdt.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/drivers/of/configfs.c
 | 
					 | 
				
			||||||
@@ -0,0 +1,310 @@
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ * Configfs entries for device-tree
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Copyright (C) 2013 - Pantelis Antoniou <panto@antoniou-consulting.com>
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * This program is free software; you can redistribute it and/or
 | 
					 | 
				
			||||||
+ * modify it under the terms of the GNU General Public License
 | 
					 | 
				
			||||||
+ * as published by the Free Software Foundation; either version
 | 
					 | 
				
			||||||
+ * 2 of the License, or (at your option) any later version.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+#include <linux/ctype.h>
 | 
					 | 
				
			||||||
+#include <linux/cpu.h>
 | 
					 | 
				
			||||||
+#include <linux/module.h>
 | 
					 | 
				
			||||||
+#include <linux/of.h>
 | 
					 | 
				
			||||||
+#include <linux/of_fdt.h>
 | 
					 | 
				
			||||||
+#include <linux/spinlock.h>
 | 
					 | 
				
			||||||
+#include <linux/slab.h>
 | 
					 | 
				
			||||||
+#include <linux/proc_fs.h>
 | 
					 | 
				
			||||||
+#include <linux/configfs.h>
 | 
					 | 
				
			||||||
+#include <linux/types.h>
 | 
					 | 
				
			||||||
+#include <linux/stat.h>
 | 
					 | 
				
			||||||
+#include <linux/limits.h>
 | 
					 | 
				
			||||||
+#include <linux/file.h>
 | 
					 | 
				
			||||||
+#include <linux/vmalloc.h>
 | 
					 | 
				
			||||||
+#include <linux/firmware.h>
 | 
					 | 
				
			||||||
+#include <linux/sizes.h>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#include "of_private.h"
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+struct cfs_overlay_item {
 | 
					 | 
				
			||||||
+	struct config_item	item;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	char			path[PATH_MAX];
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	const struct firmware	*fw;
 | 
					 | 
				
			||||||
+	struct device_node	*overlay;
 | 
					 | 
				
			||||||
+	int			ov_id;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	void			*dtbo;
 | 
					 | 
				
			||||||
+	int			dtbo_size;
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int create_overlay(struct cfs_overlay_item *overlay, void *blob)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int err;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* unflatten the tree */
 | 
					 | 
				
			||||||
+	of_fdt_unflatten_tree(blob, NULL, &overlay->overlay);
 | 
					 | 
				
			||||||
+	if (overlay->overlay == NULL) {
 | 
					 | 
				
			||||||
+		pr_err("%s: failed to unflatten tree\n", __func__);
 | 
					 | 
				
			||||||
+		err = -EINVAL;
 | 
					 | 
				
			||||||
+		goto out_err;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	pr_debug("%s: unflattened OK\n", __func__);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* mark it as detached */
 | 
					 | 
				
			||||||
+	of_node_set_flag(overlay->overlay, OF_DETACHED);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* perform resolution */
 | 
					 | 
				
			||||||
+	err = of_resolve_phandles(overlay->overlay);
 | 
					 | 
				
			||||||
+	if (err != 0) {
 | 
					 | 
				
			||||||
+		pr_err("%s: Failed to resolve tree\n", __func__);
 | 
					 | 
				
			||||||
+		goto out_err;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	pr_debug("%s: resolved OK\n", __func__);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	err = of_overlay_apply(overlay->overlay, &overlay->ov_id);
 | 
					 | 
				
			||||||
+	if (err < 0) {
 | 
					 | 
				
			||||||
+		pr_err("%s: Failed to create overlay (err=%d)\n",
 | 
					 | 
				
			||||||
+				__func__, err);
 | 
					 | 
				
			||||||
+		goto out_err;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+out_err:
 | 
					 | 
				
			||||||
+	return err;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static inline struct cfs_overlay_item *to_cfs_overlay_item(
 | 
					 | 
				
			||||||
+		struct config_item *item)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return item ? container_of(item, struct cfs_overlay_item, item) : NULL;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static ssize_t cfs_overlay_item_path_show(struct config_item *item,
 | 
					 | 
				
			||||||
+		char *page)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
 | 
					 | 
				
			||||||
+	return sprintf(page, "%s\n", overlay->path);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static ssize_t cfs_overlay_item_path_store(struct config_item *item,
 | 
					 | 
				
			||||||
+		const char *page, size_t count)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
 | 
					 | 
				
			||||||
+	const char *p = page;
 | 
					 | 
				
			||||||
+	char *s;
 | 
					 | 
				
			||||||
+	int err;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* if it's set do not allow changes */
 | 
					 | 
				
			||||||
+	if (overlay->path[0] != '\0' || overlay->dtbo_size > 0)
 | 
					 | 
				
			||||||
+		return -EPERM;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* copy to path buffer (and make sure it's always zero terminated */
 | 
					 | 
				
			||||||
+	count = snprintf(overlay->path, sizeof(overlay->path) - 1, "%s", p);
 | 
					 | 
				
			||||||
+	overlay->path[sizeof(overlay->path) - 1] = '\0';
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* strip trailing newlines */
 | 
					 | 
				
			||||||
+	s = overlay->path + strlen(overlay->path);
 | 
					 | 
				
			||||||
+	while (s > overlay->path && *--s == '\n')
 | 
					 | 
				
			||||||
+		*s = '\0';
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	pr_debug("%s: path is '%s'\n", __func__, overlay->path);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	err = request_firmware(&overlay->fw, overlay->path, NULL);
 | 
					 | 
				
			||||||
+	if (err != 0)
 | 
					 | 
				
			||||||
+		goto out_err;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	err = create_overlay(overlay, (void *)overlay->fw->data);
 | 
					 | 
				
			||||||
+	if (err != 0)
 | 
					 | 
				
			||||||
+		goto out_err;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return count;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+out_err:
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	release_firmware(overlay->fw);
 | 
					 | 
				
			||||||
+	overlay->fw = NULL;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	overlay->path[0] = '\0';
 | 
					 | 
				
			||||||
+	return err;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static ssize_t cfs_overlay_item_status_show(struct config_item *item,
 | 
					 | 
				
			||||||
+		char *page)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return sprintf(page, "%s\n",
 | 
					 | 
				
			||||||
+			overlay->ov_id >= 0 ? "applied" : "unapplied");
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+CONFIGFS_ATTR(cfs_overlay_item_, path);
 | 
					 | 
				
			||||||
+CONFIGFS_ATTR_RO(cfs_overlay_item_, status);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct configfs_attribute *cfs_overlay_attrs[] = {
 | 
					 | 
				
			||||||
+	&cfs_overlay_item_attr_path,
 | 
					 | 
				
			||||||
+	&cfs_overlay_item_attr_status,
 | 
					 | 
				
			||||||
+	NULL,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+ssize_t cfs_overlay_item_dtbo_read(struct config_item *item,
 | 
					 | 
				
			||||||
+		void *buf, size_t max_count)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	pr_debug("%s: buf=%p max_count=%zu\n", __func__,
 | 
					 | 
				
			||||||
+			buf, max_count);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (overlay->dtbo == NULL)
 | 
					 | 
				
			||||||
+		return 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* copy if buffer provided */
 | 
					 | 
				
			||||||
+	if (buf != NULL) {
 | 
					 | 
				
			||||||
+		/* the buffer must be large enough */
 | 
					 | 
				
			||||||
+		if (overlay->dtbo_size > max_count)
 | 
					 | 
				
			||||||
+			return -ENOSPC;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		memcpy(buf, overlay->dtbo, overlay->dtbo_size);
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return overlay->dtbo_size;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+ssize_t cfs_overlay_item_dtbo_write(struct config_item *item,
 | 
					 | 
				
			||||||
+		const void *buf, size_t count)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
 | 
					 | 
				
			||||||
+	int err;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* if it's set do not allow changes */
 | 
					 | 
				
			||||||
+	if (overlay->path[0] != '\0' || overlay->dtbo_size > 0)
 | 
					 | 
				
			||||||
+		return -EPERM;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* copy the contents */
 | 
					 | 
				
			||||||
+	overlay->dtbo = kmemdup(buf, count, GFP_KERNEL);
 | 
					 | 
				
			||||||
+	if (overlay->dtbo == NULL)
 | 
					 | 
				
			||||||
+		return -ENOMEM;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	overlay->dtbo_size = count;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	err = create_overlay(overlay, overlay->dtbo);
 | 
					 | 
				
			||||||
+	if (err != 0)
 | 
					 | 
				
			||||||
+		goto out_err;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return count;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+out_err:
 | 
					 | 
				
			||||||
+	kfree(overlay->dtbo);
 | 
					 | 
				
			||||||
+	overlay->dtbo = NULL;
 | 
					 | 
				
			||||||
+	overlay->dtbo_size = 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return err;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+CONFIGFS_BIN_ATTR(cfs_overlay_item_, dtbo, NULL, SZ_1M);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct configfs_bin_attribute *cfs_overlay_bin_attrs[] = {
 | 
					 | 
				
			||||||
+	&cfs_overlay_item_attr_dtbo,
 | 
					 | 
				
			||||||
+	NULL,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void cfs_overlay_release(struct config_item *item)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (overlay->ov_id >= 0)
 | 
					 | 
				
			||||||
+		of_overlay_remove(&overlay->ov_id);
 | 
					 | 
				
			||||||
+	if (overlay->fw)
 | 
					 | 
				
			||||||
+		release_firmware(overlay->fw);
 | 
					 | 
				
			||||||
+	/* kfree with NULL is safe */
 | 
					 | 
				
			||||||
+	kfree(overlay->dtbo);
 | 
					 | 
				
			||||||
+	kfree(overlay);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct configfs_item_operations cfs_overlay_item_ops = {
 | 
					 | 
				
			||||||
+	.release	= cfs_overlay_release,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct config_item_type cfs_overlay_type = {
 | 
					 | 
				
			||||||
+	.ct_item_ops	= &cfs_overlay_item_ops,
 | 
					 | 
				
			||||||
+	.ct_attrs	= cfs_overlay_attrs,
 | 
					 | 
				
			||||||
+	.ct_bin_attrs	= cfs_overlay_bin_attrs,
 | 
					 | 
				
			||||||
+	.ct_owner	= THIS_MODULE,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct config_item *cfs_overlay_group_make_item(
 | 
					 | 
				
			||||||
+		struct config_group *group, const char *name)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct cfs_overlay_item *overlay;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	overlay = kzalloc(sizeof(*overlay), GFP_KERNEL);
 | 
					 | 
				
			||||||
+	if (!overlay)
 | 
					 | 
				
			||||||
+		return ERR_PTR(-ENOMEM);
 | 
					 | 
				
			||||||
+	overlay->ov_id = -1;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	config_item_init_type_name(&overlay->item, name, &cfs_overlay_type);
 | 
					 | 
				
			||||||
+	return &overlay->item;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void cfs_overlay_group_drop_item(struct config_group *group,
 | 
					 | 
				
			||||||
+		struct config_item *item)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	config_item_put(&overlay->item);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct configfs_group_operations overlays_ops = {
 | 
					 | 
				
			||||||
+	.make_item	= cfs_overlay_group_make_item,
 | 
					 | 
				
			||||||
+	.drop_item	= cfs_overlay_group_drop_item,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct config_item_type overlays_type = {
 | 
					 | 
				
			||||||
+	.ct_group_ops   = &overlays_ops,
 | 
					 | 
				
			||||||
+	.ct_owner       = THIS_MODULE,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct configfs_group_operations of_cfs_ops = {
 | 
					 | 
				
			||||||
+	/* empty - we don't allow anything to be created */
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct config_item_type of_cfs_type = {
 | 
					 | 
				
			||||||
+	.ct_group_ops   = &of_cfs_ops,
 | 
					 | 
				
			||||||
+	.ct_owner       = THIS_MODULE,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+struct config_group of_cfs_overlay_group;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct configfs_subsystem of_cfs_subsys = {
 | 
					 | 
				
			||||||
+	.su_group = {
 | 
					 | 
				
			||||||
+		.cg_item = {
 | 
					 | 
				
			||||||
+			.ci_namebuf = "device-tree",
 | 
					 | 
				
			||||||
+			.ci_type = &of_cfs_type,
 | 
					 | 
				
			||||||
+		},
 | 
					 | 
				
			||||||
+	},
 | 
					 | 
				
			||||||
+	.su_mutex = __MUTEX_INITIALIZER(of_cfs_subsys.su_mutex),
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int __init of_cfs_init(void)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	pr_info("%s\n", __func__);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	config_group_init(&of_cfs_subsys.su_group);
 | 
					 | 
				
			||||||
+	config_group_init_type_name(&of_cfs_overlay_group, "overlays",
 | 
					 | 
				
			||||||
+			&overlays_type);
 | 
					 | 
				
			||||||
+	configfs_add_default_group(&of_cfs_overlay_group,
 | 
					 | 
				
			||||||
+			&of_cfs_subsys.su_group);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ret = configfs_register_subsystem(&of_cfs_subsys);
 | 
					 | 
				
			||||||
+	if (ret != 0) {
 | 
					 | 
				
			||||||
+		pr_err("%s: failed to register subsys\n", __func__);
 | 
					 | 
				
			||||||
+		goto out;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	pr_info("%s: OK\n", __func__);
 | 
					 | 
				
			||||||
+out:
 | 
					 | 
				
			||||||
+	return ret;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+late_initcall(of_cfs_init);
 | 
					 | 
				
			||||||
@ -1,23 +0,0 @@
 | 
				
			|||||||
From 32fe7cc286f4a4d7f5d623ddb9fba10907c2abea Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Thu, 17 Dec 2015 13:37:07 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] hci_h5: Don't send conf_req when ACTIVE
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Without this patch, a modem and kernel can continuously bombard each
 | 
					 | 
				
			||||||
other with conf_req and conf_rsp messages, in a demented game of tag.
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/bluetooth/hci_h5.c | 3 ++-
 | 
					 | 
				
			||||||
 1 file changed, 2 insertions(+), 1 deletion(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/bluetooth/hci_h5.c
 | 
					 | 
				
			||||||
+++ b/drivers/bluetooth/hci_h5.c
 | 
					 | 
				
			||||||
@@ -343,7 +343,8 @@ static void h5_handle_internal_rx(struct
 | 
					 | 
				
			||||||
 		h5_link_control(hu, conf_req, 3);
 | 
					 | 
				
			||||||
 	} else if (memcmp(data, conf_req, 2) == 0) {
 | 
					 | 
				
			||||||
 		h5_link_control(hu, conf_rsp, 2);
 | 
					 | 
				
			||||||
-		h5_link_control(hu, conf_req, 3);
 | 
					 | 
				
			||||||
+		if (h5->state != H5_ACTIVE)
 | 
					 | 
				
			||||||
+		    h5_link_control(hu, conf_req, 3);
 | 
					 | 
				
			||||||
 	} else if (memcmp(data, conf_rsp, 2) == 0) {
 | 
					 | 
				
			||||||
 		if (H5_HDR_LEN(hdr) > 2)
 | 
					 | 
				
			||||||
 			h5->tx_win = (data[2] & 0x07);
 | 
					 | 
				
			||||||
@ -1,72 +0,0 @@
 | 
				
			|||||||
From 443eb00795c4da76e3877ae7448697ba14584596 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Michael Zoran <mzoran@crowfest.net>
 | 
					 | 
				
			||||||
Date: Sat, 14 Jan 2017 21:43:57 -0800
 | 
					 | 
				
			||||||
Subject: [PATCH] ARM64: Round-Robin dispatch IRQs between CPUs.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
IRQ-CPU mapping is round robined on ARM64 to increase
 | 
					 | 
				
			||||||
concurrency and allow multiple interrupts to be serviced
 | 
					 | 
				
			||||||
at a time.  This reduces the need for FIQ.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Michael Zoran <mzoran@crowfest.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/irqchip/irq-bcm2835.c | 15 ++++++++++++++-
 | 
					 | 
				
			||||||
 drivers/irqchip/irq-bcm2836.c | 21 +++++++++++++++++++++
 | 
					 | 
				
			||||||
 2 files changed, 35 insertions(+), 1 deletion(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/irqchip/irq-bcm2835.c
 | 
					 | 
				
			||||||
+++ b/drivers/irqchip/irq-bcm2835.c
 | 
					 | 
				
			||||||
@@ -154,10 +154,23 @@ static void armctrl_unmask_irq(struct ir
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+#ifdef CONFIG_ARM64
 | 
					 | 
				
			||||||
+void bcm2836_arm_irqchip_spin_gpu_irq(void);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void armctrl_ack_irq(struct irq_data *d)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	bcm2836_arm_irqchip_spin_gpu_irq();
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static struct irq_chip armctrl_chip = {
 | 
					 | 
				
			||||||
 	.name = "ARMCTRL-level",
 | 
					 | 
				
			||||||
 	.irq_mask = armctrl_mask_irq,
 | 
					 | 
				
			||||||
-	.irq_unmask = armctrl_unmask_irq
 | 
					 | 
				
			||||||
+	.irq_unmask = armctrl_unmask_irq,
 | 
					 | 
				
			||||||
+#ifdef CONFIG_ARM64
 | 
					 | 
				
			||||||
+	.irq_ack    = armctrl_ack_irq
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static int armctrl_xlate(struct irq_domain *d, struct device_node *ctrlr,
 | 
					 | 
				
			||||||
--- a/drivers/irqchip/irq-bcm2836.c
 | 
					 | 
				
			||||||
+++ b/drivers/irqchip/irq-bcm2836.c
 | 
					 | 
				
			||||||
@@ -87,6 +87,27 @@ static void bcm2836_arm_irqchip_unmask_g
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+#ifdef CONFIG_ARM64
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+void bcm2836_arm_irqchip_spin_gpu_irq(void)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	u32 i;
 | 
					 | 
				
			||||||
+	void __iomem *gpurouting = (intc.base + LOCAL_GPU_ROUTING);
 | 
					 | 
				
			||||||
+	u32 routing_val = readl(gpurouting);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	for (i = 1; i <= 3; i++) {
 | 
					 | 
				
			||||||
+		u32 new_routing_val = (routing_val + i) & 3;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		if (cpu_active(new_routing_val)) {
 | 
					 | 
				
			||||||
+			writel(new_routing_val, gpurouting);
 | 
					 | 
				
			||||||
+			return;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL(bcm2836_arm_irqchip_spin_gpu_irq);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static struct irq_chip bcm2836_arm_irqchip_gpu = {
 | 
					 | 
				
			||||||
 	.name		= "bcm2836-gpu",
 | 
					 | 
				
			||||||
 	.irq_mask	= bcm2836_arm_irqchip_mask_gpu_irq,
 | 
					 | 
				
			||||||
@ -1,28 +0,0 @@
 | 
				
			|||||||
From b4e6e3b95c4f41b415d9d7fed7f121a1850af042 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Michael Zoran <mzoran@crowfest.net>
 | 
					 | 
				
			||||||
Date: Sat, 11 Feb 2017 01:18:31 -0800
 | 
					 | 
				
			||||||
Subject: [PATCH] ARM64: Force hardware emulation of deprecated
 | 
					 | 
				
			||||||
 instructions.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 arch/arm64/kernel/armv8_deprecated.c | 5 +++++
 | 
					 | 
				
			||||||
 1 file changed, 5 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/arch/arm64/kernel/armv8_deprecated.c
 | 
					 | 
				
			||||||
+++ b/arch/arm64/kernel/armv8_deprecated.c
 | 
					 | 
				
			||||||
@@ -182,10 +182,15 @@ static void __init register_insn_emulati
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	switch (ops->status) {
 | 
					 | 
				
			||||||
 	case INSN_DEPRECATED:
 | 
					 | 
				
			||||||
+#if 0
 | 
					 | 
				
			||||||
 		insn->current_mode = INSN_EMULATE;
 | 
					 | 
				
			||||||
 		/* Disable the HW mode if it was turned on at early boot time */
 | 
					 | 
				
			||||||
 		run_all_cpu_set_hw_mode(insn, false);
 | 
					 | 
				
			||||||
+#else
 | 
					 | 
				
			||||||
+		insn->current_mode = INSN_HW;
 | 
					 | 
				
			||||||
+		run_all_cpu_set_hw_mode(insn, true);
 | 
					 | 
				
			||||||
 		insn->max = INSN_HW;
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
 		break;
 | 
					 | 
				
			||||||
 	case INSN_OBSOLETE:
 | 
					 | 
				
			||||||
 		insn->current_mode = INSN_UNDEF;
 | 
					 | 
				
			||||||
@ -1,53 +0,0 @@
 | 
				
			|||||||
From c9cb22efe558ee7fb1f30deb19dfe7b7bfe1f369 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: popcornmix <popcornmix@gmail.com>
 | 
					 | 
				
			||||||
Date: Fri, 25 Aug 2017 19:18:13 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] cache: export clean and invalidate
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
hack: cache: Fix linker error
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 arch/arm/mm/cache-v6.S | 4 ++--
 | 
					 | 
				
			||||||
 arch/arm/mm/cache-v7.S | 6 ++++--
 | 
					 | 
				
			||||||
 2 files changed, 6 insertions(+), 4 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/arch/arm/mm/cache-v6.S
 | 
					 | 
				
			||||||
+++ b/arch/arm/mm/cache-v6.S
 | 
					 | 
				
			||||||
@@ -198,7 +198,7 @@ ENTRY(v6_flush_kern_dcache_area)
 | 
					 | 
				
			||||||
  *	- start   - virtual start address of region
 | 
					 | 
				
			||||||
  *	- end     - virtual end address of region
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
-v6_dma_inv_range:
 | 
					 | 
				
			||||||
+ENTRY(v6_dma_inv_range)
 | 
					 | 
				
			||||||
 #ifdef CONFIG_DMA_CACHE_RWFO
 | 
					 | 
				
			||||||
 	ldrb	r2, [r0]			@ read for ownership
 | 
					 | 
				
			||||||
 	strb	r2, [r0]			@ write for ownership
 | 
					 | 
				
			||||||
@@ -243,7 +243,7 @@ v6_dma_inv_range:
 | 
					 | 
				
			||||||
  *	- start   - virtual start address of region
 | 
					 | 
				
			||||||
  *	- end     - virtual end address of region
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
-v6_dma_clean_range:
 | 
					 | 
				
			||||||
+ENTRY(v6_dma_clean_range)
 | 
					 | 
				
			||||||
 	bic	r0, r0, #D_CACHE_LINE_SIZE - 1
 | 
					 | 
				
			||||||
 1:
 | 
					 | 
				
			||||||
 #ifdef CONFIG_DMA_CACHE_RWFO
 | 
					 | 
				
			||||||
--- a/arch/arm/mm/cache-v7.S
 | 
					 | 
				
			||||||
+++ b/arch/arm/mm/cache-v7.S
 | 
					 | 
				
			||||||
@@ -363,7 +363,8 @@ ENDPROC(v7_flush_kern_dcache_area)
 | 
					 | 
				
			||||||
  *	- start   - virtual start address of region
 | 
					 | 
				
			||||||
  *	- end     - virtual end address of region
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
-v7_dma_inv_range:
 | 
					 | 
				
			||||||
+ENTRY(b15_dma_inv_range)
 | 
					 | 
				
			||||||
+ENTRY(v7_dma_inv_range)
 | 
					 | 
				
			||||||
 	dcache_line_size r2, r3
 | 
					 | 
				
			||||||
 	sub	r3, r2, #1
 | 
					 | 
				
			||||||
 	tst	r0, r3
 | 
					 | 
				
			||||||
@@ -393,7 +394,8 @@ ENDPROC(v7_dma_inv_range)
 | 
					 | 
				
			||||||
  *	- start   - virtual start address of region
 | 
					 | 
				
			||||||
  *	- end     - virtual end address of region
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
-v7_dma_clean_range:
 | 
					 | 
				
			||||||
+ENTRY(b15_dma_clean_range)
 | 
					 | 
				
			||||||
+ENTRY(v7_dma_clean_range)
 | 
					 | 
				
			||||||
 	dcache_line_size r2, r3
 | 
					 | 
				
			||||||
 	sub	r3, r2, #1
 | 
					 | 
				
			||||||
 	bic	r0, r0, r3
 | 
					 | 
				
			||||||
@ -1,691 +0,0 @@
 | 
				
			|||||||
From d9151380fdab4a0f2abcc08d2c344b0eabe48ab9 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: James Hughes <JamesH65@users.noreply.github.com>
 | 
					 | 
				
			||||||
Date: Tue, 14 Nov 2017 15:13:15 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] AXI performance monitor driver (#2222)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Uses the debugfs I/F to provide access to the AXI
 | 
					 | 
				
			||||||
bus performance monitors.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Requires the new mailbox peripheral access for access
 | 
					 | 
				
			||||||
to the VPU performance registers, system bus access
 | 
					 | 
				
			||||||
is done using direct register reads.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: James Hughes <james.hughes@raspberrypi.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
raspberrypi_axi_monitor: suppress warning
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Suppress the following warning by casting the pointer to and uintptr_t
 | 
					 | 
				
			||||||
before to u32:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Matteo Croce <mcroce@redhat.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/perf/Kconfig                   |   8 +
 | 
					 | 
				
			||||||
 drivers/perf/Makefile                  |   1 +
 | 
					 | 
				
			||||||
 drivers/perf/raspberrypi_axi_monitor.c | 637 +++++++++++++++++++++++++
 | 
					 | 
				
			||||||
 3 files changed, 646 insertions(+)
 | 
					 | 
				
			||||||
 create mode 100644 drivers/perf/raspberrypi_axi_monitor.c
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/perf/Kconfig
 | 
					 | 
				
			||||||
+++ b/drivers/perf/Kconfig
 | 
					 | 
				
			||||||
@@ -130,6 +130,14 @@ config ARM_SPE_PMU
 | 
					 | 
				
			||||||
 	  Extension, which provides periodic sampling of operations in
 | 
					 | 
				
			||||||
 	  the CPU pipeline and reports this via the perf AUX interface.
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+config RPI_AXIPERF
 | 
					 | 
				
			||||||
+        depends on ARCH_BCM2835
 | 
					 | 
				
			||||||
+        tristate "RaspberryPi AXI Performance monitors"
 | 
					 | 
				
			||||||
+        default n
 | 
					 | 
				
			||||||
+        help
 | 
					 | 
				
			||||||
+          Say y if you want to use Raspberry Pi AXI performance monitors, m if
 | 
					 | 
				
			||||||
+          you want to build it as a module.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 source "drivers/perf/hisilicon/Kconfig"
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 endmenu
 | 
					 | 
				
			||||||
--- a/drivers/perf/Makefile
 | 
					 | 
				
			||||||
+++ b/drivers/perf/Makefile
 | 
					 | 
				
			||||||
@@ -13,3 +13,4 @@ obj-$(CONFIG_QCOM_L3_PMU) += qcom_l3_pmu
 | 
					 | 
				
			||||||
 obj-$(CONFIG_THUNDERX2_PMU) += thunderx2_pmu.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_XGENE_PMU) += xgene_pmu.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_ARM_SPE_PMU) += arm_spe_pmu.o
 | 
					 | 
				
			||||||
+obj-$(CONFIG_RPI_AXIPERF) += raspberrypi_axi_monitor.o
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/drivers/perf/raspberrypi_axi_monitor.c
 | 
					 | 
				
			||||||
@@ -0,0 +1,637 @@
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ * raspberrypi_axi_monitor.c
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Author: james.hughes@raspberrypi.org
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Raspberry Pi AXI performance counters.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Copyright (C) 2017 Raspberry Pi Trading Ltd.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * 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/debugfs.h>
 | 
					 | 
				
			||||||
+#include <linux/devcoredump.h>
 | 
					 | 
				
			||||||
+#include <linux/device.h>
 | 
					 | 
				
			||||||
+#include <linux/kthread.h>
 | 
					 | 
				
			||||||
+#include <linux/module.h>
 | 
					 | 
				
			||||||
+#include <linux/netdevice.h>
 | 
					 | 
				
			||||||
+#include <linux/mutex.h>
 | 
					 | 
				
			||||||
+#include <linux/of.h>
 | 
					 | 
				
			||||||
+#include <linux/platform_device.h>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#include <soc/bcm2835/raspberrypi-firmware.h>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define NUM_MONITORS 2
 | 
					 | 
				
			||||||
+#define NUM_BUS_WATCHERS_PER_MONITOR 3
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define SYSTEM_MONITOR 0
 | 
					 | 
				
			||||||
+#define VPU_MONITOR 1
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define MAX_BUSES 16
 | 
					 | 
				
			||||||
+#define DEFAULT_SAMPLE_TIME 100
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define NUM_BUS_WATCHER_RESULTS 9
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+struct bus_watcher_data {
 | 
					 | 
				
			||||||
+	union	{
 | 
					 | 
				
			||||||
+		u32 results[NUM_BUS_WATCHER_RESULTS];
 | 
					 | 
				
			||||||
+		struct {
 | 
					 | 
				
			||||||
+			u32 atrans;
 | 
					 | 
				
			||||||
+			u32 atwait;
 | 
					 | 
				
			||||||
+			u32 amax;
 | 
					 | 
				
			||||||
+			u32 wtrans;
 | 
					 | 
				
			||||||
+			u32 wtwait;
 | 
					 | 
				
			||||||
+			u32 wmax;
 | 
					 | 
				
			||||||
+			u32 rtrans;
 | 
					 | 
				
			||||||
+			u32 rtwait;
 | 
					 | 
				
			||||||
+			u32 rmax;
 | 
					 | 
				
			||||||
+		};
 | 
					 | 
				
			||||||
+	};
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+struct rpi_axiperf {
 | 
					 | 
				
			||||||
+	struct platform_device *dev;
 | 
					 | 
				
			||||||
+	struct dentry *root_folder;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	struct task_struct *monitor_thread;
 | 
					 | 
				
			||||||
+	struct mutex lock;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	struct rpi_firmware *firmware;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* Sample time spent on for each bus */
 | 
					 | 
				
			||||||
+	int sample_time;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* Now storage for the per monitor settings and the resulting
 | 
					 | 
				
			||||||
+	 * performance figures
 | 
					 | 
				
			||||||
+	 */
 | 
					 | 
				
			||||||
+	struct {
 | 
					 | 
				
			||||||
+		/* Bit field of buses we want to monitor */
 | 
					 | 
				
			||||||
+		int bus_enabled;
 | 
					 | 
				
			||||||
+		/* Bit field of buses to filter by */
 | 
					 | 
				
			||||||
+		int bus_filter;
 | 
					 | 
				
			||||||
+		/* The current buses being monitored on this monitor */
 | 
					 | 
				
			||||||
+		int current_bus[NUM_BUS_WATCHERS_PER_MONITOR];
 | 
					 | 
				
			||||||
+		/* The last bus monitored on this monitor */
 | 
					 | 
				
			||||||
+		int last_monitored;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		/* Set true if this mailbox must use the mailbox interface
 | 
					 | 
				
			||||||
+		 * rather than access registers directly.
 | 
					 | 
				
			||||||
+		 */
 | 
					 | 
				
			||||||
+		int use_mailbox_interface;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		/* Current result values */
 | 
					 | 
				
			||||||
+		struct bus_watcher_data results[MAX_BUSES];
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		struct dentry *debugfs_entry;
 | 
					 | 
				
			||||||
+		void __iomem *base_address;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	}  monitor[NUM_MONITORS];
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct rpi_axiperf *state;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* Two monitors, System and VPU, each with the following register sets.
 | 
					 | 
				
			||||||
+ * Each monitor can only monitor one bus at a time, so we time share them,
 | 
					 | 
				
			||||||
+ * giving each bus 100ms (default, settable via debugfs) of time on its
 | 
					 | 
				
			||||||
+ * associated monitor
 | 
					 | 
				
			||||||
+ * Record results from the three Bus watchers per monitor and push to the sysfs
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* general registers */
 | 
					 | 
				
			||||||
+const int GEN_CTRL;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+const int GEN_CTL_ENABLE_BIT	= BIT(0);
 | 
					 | 
				
			||||||
+const int GEN_CTL_RESET_BIT	= BIT(1);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* Bus watcher registers */
 | 
					 | 
				
			||||||
+const int BW_PITCH		= 0x40;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+const int BW0_CTRL		= 0x40;
 | 
					 | 
				
			||||||
+const int BW1_CTRL		= 0x80;
 | 
					 | 
				
			||||||
+const int BW2_CTRL		= 0xc0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+const int BW_ATRANS_OFFSET	= 0x04;
 | 
					 | 
				
			||||||
+const int BW_ATWAIT_OFFSET	= 0x08;
 | 
					 | 
				
			||||||
+const int BW_AMAX_OFFSET	= 0x0c;
 | 
					 | 
				
			||||||
+const int BW_WTRANS_OFFSET	= 0x10;
 | 
					 | 
				
			||||||
+const int BW_WTWAIT_OFFSET	= 0x14;
 | 
					 | 
				
			||||||
+const int BW_WMAX_OFFSET	= 0x18;
 | 
					 | 
				
			||||||
+const int BW_RTRANS_OFFSET	= 0x1c;
 | 
					 | 
				
			||||||
+const int BW_RTWAIT_OFFSET	= 0x20;
 | 
					 | 
				
			||||||
+const int BW_RMAX_OFFSET	= 0x24;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+const int BW_CTRL_RESET_BIT	= BIT(31);
 | 
					 | 
				
			||||||
+const int BW_CTRL_ENABLE_BIT	= BIT(30);
 | 
					 | 
				
			||||||
+const int BW_CTRL_ENABLE_ID_FILTER_BIT	= BIT(29);
 | 
					 | 
				
			||||||
+const int BW_CTRL_LIMIT_HALT_BIT	= BIT(28);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+const int BW_CTRL_SOURCE_SHIFT	= 8;
 | 
					 | 
				
			||||||
+const int BW_CTRL_SOURCE_MASK	= GENMASK(12, 8); // 5 bits
 | 
					 | 
				
			||||||
+const int BW_CTRL_BUS_WATCH_SHIFT;
 | 
					 | 
				
			||||||
+const int BW_CTRL_BUS_WATCH_MASK = GENMASK(5, 0); // 6 bits
 | 
					 | 
				
			||||||
+const int BW_CTRL_BUS_FILTER_SHIFT = 8;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+const static char *bus_filter_strings[] = {
 | 
					 | 
				
			||||||
+	"",
 | 
					 | 
				
			||||||
+	"CORE0_V",
 | 
					 | 
				
			||||||
+	"ICACHE0",
 | 
					 | 
				
			||||||
+	"DCACHE0",
 | 
					 | 
				
			||||||
+	"CORE1_V",
 | 
					 | 
				
			||||||
+	"ICACHE1",
 | 
					 | 
				
			||||||
+	"DCACHE1",
 | 
					 | 
				
			||||||
+	"L2_MAIN",
 | 
					 | 
				
			||||||
+	"HOST_PORT",
 | 
					 | 
				
			||||||
+	"HOST_PORT2",
 | 
					 | 
				
			||||||
+	"HVS",
 | 
					 | 
				
			||||||
+	"ISP",
 | 
					 | 
				
			||||||
+	"VIDEO_DCT",
 | 
					 | 
				
			||||||
+	"VIDEO_SD2AXI",
 | 
					 | 
				
			||||||
+	"CAM0",
 | 
					 | 
				
			||||||
+	"CAM1",
 | 
					 | 
				
			||||||
+	"DMA0",
 | 
					 | 
				
			||||||
+	"DMA1",
 | 
					 | 
				
			||||||
+	"DMA2_VPU",
 | 
					 | 
				
			||||||
+	"JPEG",
 | 
					 | 
				
			||||||
+	"VIDEO_CME",
 | 
					 | 
				
			||||||
+	"TRANSPOSER",
 | 
					 | 
				
			||||||
+	"VIDEO_FME",
 | 
					 | 
				
			||||||
+	"CCP2TX",
 | 
					 | 
				
			||||||
+	"USB",
 | 
					 | 
				
			||||||
+	"V3D0",
 | 
					 | 
				
			||||||
+	"V3D1",
 | 
					 | 
				
			||||||
+	"V3D2",
 | 
					 | 
				
			||||||
+	"AVE",
 | 
					 | 
				
			||||||
+	"DEBUG",
 | 
					 | 
				
			||||||
+	"CPU",
 | 
					 | 
				
			||||||
+	"M30"
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+const int num_bus_filters = ARRAY_SIZE(bus_filter_strings);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+const static char *system_bus_string[] = {
 | 
					 | 
				
			||||||
+	"DMA_L2",
 | 
					 | 
				
			||||||
+	"TRANS",
 | 
					 | 
				
			||||||
+	"JPEG",
 | 
					 | 
				
			||||||
+	"SYSTEM_UC",
 | 
					 | 
				
			||||||
+	"DMA_UC",
 | 
					 | 
				
			||||||
+	"SYSTEM_L2",
 | 
					 | 
				
			||||||
+	"CCP2TX",
 | 
					 | 
				
			||||||
+	"MPHI_RX",
 | 
					 | 
				
			||||||
+	"MPHI_TX",
 | 
					 | 
				
			||||||
+	"HVS",
 | 
					 | 
				
			||||||
+	"H264",
 | 
					 | 
				
			||||||
+	"ISP",
 | 
					 | 
				
			||||||
+	"V3D",
 | 
					 | 
				
			||||||
+	"PERIPHERAL",
 | 
					 | 
				
			||||||
+	"CPU_UC",
 | 
					 | 
				
			||||||
+	"CPU_L2"
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+const int num_system_buses = ARRAY_SIZE(system_bus_string);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+const static char *vpu_bus_string[] = {
 | 
					 | 
				
			||||||
+	"VPU1_D_L2",
 | 
					 | 
				
			||||||
+	"VPU0_D_L2",
 | 
					 | 
				
			||||||
+	"VPU1_I_L2",
 | 
					 | 
				
			||||||
+	"VPU0_I_L2",
 | 
					 | 
				
			||||||
+	"SYSTEM_L2",
 | 
					 | 
				
			||||||
+	"L2_FLUSH",
 | 
					 | 
				
			||||||
+	"DMA_L2",
 | 
					 | 
				
			||||||
+	"VPU1_D_UC",
 | 
					 | 
				
			||||||
+	"VPU0_D_UC",
 | 
					 | 
				
			||||||
+	"VPU1_I_UC",
 | 
					 | 
				
			||||||
+	"VPU0_I_UC",
 | 
					 | 
				
			||||||
+	"SYSTEM_UC",
 | 
					 | 
				
			||||||
+	"L2_OUT",
 | 
					 | 
				
			||||||
+	"DMA_UC",
 | 
					 | 
				
			||||||
+	"SDRAM",
 | 
					 | 
				
			||||||
+	"L2_IN"
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+const int num_vpu_buses = ARRAY_SIZE(vpu_bus_string);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+const static char *monitor_name[] = {
 | 
					 | 
				
			||||||
+	"System",
 | 
					 | 
				
			||||||
+	"VPU"
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static inline void write_reg(int monitor, int reg, u32 value)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	writel(value, state->monitor[monitor].base_address + reg);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static inline u32 read_reg(int monitor, u32 reg)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	return readl(state->monitor[monitor].base_address + reg);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void read_bus_watcher(int monitor, int watcher, u32 *results)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	if (state->monitor[monitor].use_mailbox_interface) {
 | 
					 | 
				
			||||||
+		/* We have 9 results, plus the overheads of start address and
 | 
					 | 
				
			||||||
+		 * length So 11 u32 to define
 | 
					 | 
				
			||||||
+		 */
 | 
					 | 
				
			||||||
+		u32 tmp[11];
 | 
					 | 
				
			||||||
+		int err;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		tmp[0] = (u32)(uintptr_t)(state->monitor[monitor].base_address + watcher
 | 
					 | 
				
			||||||
+				+ BW_ATRANS_OFFSET);
 | 
					 | 
				
			||||||
+		tmp[1] = NUM_BUS_WATCHER_RESULTS;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		err = rpi_firmware_property(state->firmware,
 | 
					 | 
				
			||||||
+					    RPI_FIRMWARE_GET_PERIPH_REG,
 | 
					 | 
				
			||||||
+					    tmp, sizeof(tmp));
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		if (err < 0 || tmp[1] != NUM_BUS_WATCHER_RESULTS)
 | 
					 | 
				
			||||||
+			dev_err_once(&state->dev->dev,
 | 
					 | 
				
			||||||
+				     "Failed to read bus watcher");
 | 
					 | 
				
			||||||
+		else
 | 
					 | 
				
			||||||
+			memcpy(results, &tmp[2],
 | 
					 | 
				
			||||||
+			       NUM_BUS_WATCHER_RESULTS * sizeof(u32));
 | 
					 | 
				
			||||||
+	} else {
 | 
					 | 
				
			||||||
+		int i;
 | 
					 | 
				
			||||||
+		void __iomem *addr = state->monitor[monitor].base_address
 | 
					 | 
				
			||||||
+				+ watcher + BW_ATRANS_OFFSET;
 | 
					 | 
				
			||||||
+		for (i = 0; i < NUM_BUS_WATCHER_RESULTS; i++, addr += 4)
 | 
					 | 
				
			||||||
+			*results++ = readl(addr);
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void set_monitor_control(int monitor, u32 set)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	if (state->monitor[monitor].use_mailbox_interface) {
 | 
					 | 
				
			||||||
+		u32 tmp[3] = {(u32)(uintptr_t)(state->monitor[monitor].base_address +
 | 
					 | 
				
			||||||
+				GEN_CTRL), 1, set};
 | 
					 | 
				
			||||||
+		int err = rpi_firmware_property(state->firmware,
 | 
					 | 
				
			||||||
+						RPI_FIRMWARE_SET_PERIPH_REG,
 | 
					 | 
				
			||||||
+						tmp, sizeof(tmp));
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		if (err < 0 || tmp[1] != 1)
 | 
					 | 
				
			||||||
+			dev_err_once(&state->dev->dev,
 | 
					 | 
				
			||||||
+				"Failed to set monitor control");
 | 
					 | 
				
			||||||
+	} else
 | 
					 | 
				
			||||||
+		write_reg(monitor, GEN_CTRL, set);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void set_bus_watcher_control(int monitor, int watcher, u32 set)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	if (state->monitor[monitor].use_mailbox_interface) {
 | 
					 | 
				
			||||||
+		u32 tmp[3] = {(u32)(uintptr_t)(state->monitor[monitor].base_address +
 | 
					 | 
				
			||||||
+				    watcher), 1, set};
 | 
					 | 
				
			||||||
+		int err = rpi_firmware_property(state->firmware,
 | 
					 | 
				
			||||||
+						RPI_FIRMWARE_SET_PERIPH_REG,
 | 
					 | 
				
			||||||
+						tmp, sizeof(tmp));
 | 
					 | 
				
			||||||
+		if (err < 0 || tmp[1] != 1)
 | 
					 | 
				
			||||||
+			dev_err_once(&state->dev->dev,
 | 
					 | 
				
			||||||
+				"Failed to set bus watcher control");
 | 
					 | 
				
			||||||
+	} else
 | 
					 | 
				
			||||||
+		write_reg(monitor, watcher, set);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void monitor(struct rpi_axiperf *state)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int monitor, num_buses[NUM_MONITORS];
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	mutex_lock(&state->lock);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	for (monitor = 0; monitor < NUM_MONITORS; monitor++) {
 | 
					 | 
				
			||||||
+		typeof(state->monitor[0]) *mon = &(state->monitor[monitor]);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		/* Anything enabled? */
 | 
					 | 
				
			||||||
+		if (mon->bus_enabled == 0) {
 | 
					 | 
				
			||||||
+			/* No, disable all monitoring for this monitor */
 | 
					 | 
				
			||||||
+			set_monitor_control(monitor, GEN_CTL_RESET_BIT);
 | 
					 | 
				
			||||||
+		} else {
 | 
					 | 
				
			||||||
+			int i;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+			/* Find out how many busses we want to monitor, and
 | 
					 | 
				
			||||||
+			 * spread our 3 actual monitors over them
 | 
					 | 
				
			||||||
+			 */
 | 
					 | 
				
			||||||
+			num_buses[monitor] = hweight32(mon->bus_enabled);
 | 
					 | 
				
			||||||
+			num_buses[monitor] = min(num_buses[monitor],
 | 
					 | 
				
			||||||
+						 NUM_BUS_WATCHERS_PER_MONITOR);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+			for (i = 0; i < num_buses[monitor]; i++) {
 | 
					 | 
				
			||||||
+				int bus_control;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+				do {
 | 
					 | 
				
			||||||
+					mon->last_monitored++;
 | 
					 | 
				
			||||||
+					mon->last_monitored &= 0xf;
 | 
					 | 
				
			||||||
+				} while ((mon->bus_enabled &
 | 
					 | 
				
			||||||
+					 (1 << mon->last_monitored)) == 0);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+				mon->current_bus[i] = mon->last_monitored;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+				/* Reset the counters */
 | 
					 | 
				
			||||||
+				set_bus_watcher_control(monitor,
 | 
					 | 
				
			||||||
+							BW0_CTRL +
 | 
					 | 
				
			||||||
+							i*BW_PITCH,
 | 
					 | 
				
			||||||
+							BW_CTRL_RESET_BIT);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+				bus_control = BW_CTRL_ENABLE_BIT |
 | 
					 | 
				
			||||||
+						mon->current_bus[i];
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+				if (mon->bus_filter) {
 | 
					 | 
				
			||||||
+					bus_control |=
 | 
					 | 
				
			||||||
+						BW_CTRL_ENABLE_ID_FILTER_BIT;
 | 
					 | 
				
			||||||
+					bus_control |=
 | 
					 | 
				
			||||||
+						((mon->bus_filter & 0x1f)
 | 
					 | 
				
			||||||
+						<< BW_CTRL_BUS_FILTER_SHIFT);
 | 
					 | 
				
			||||||
+				}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+				// Start capture
 | 
					 | 
				
			||||||
+				set_bus_watcher_control(monitor,
 | 
					 | 
				
			||||||
+							BW0_CTRL + i*BW_PITCH,
 | 
					 | 
				
			||||||
+							bus_control);
 | 
					 | 
				
			||||||
+			}
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		/* start monitoring */
 | 
					 | 
				
			||||||
+		set_monitor_control(monitor, GEN_CTL_ENABLE_BIT);
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	mutex_unlock(&state->lock);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	msleep(state->sample_time);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* Now read the results */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	mutex_lock(&state->lock);
 | 
					 | 
				
			||||||
+	for (monitor = 0; monitor < NUM_MONITORS; monitor++) {
 | 
					 | 
				
			||||||
+		typeof(state->monitor[0]) *mon = &(state->monitor[monitor]);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		/* Anything enabled? */
 | 
					 | 
				
			||||||
+		if (mon->bus_enabled == 0) {
 | 
					 | 
				
			||||||
+			/* No, disable all monitoring for this monitor */
 | 
					 | 
				
			||||||
+			set_monitor_control(monitor, 0);
 | 
					 | 
				
			||||||
+		} else {
 | 
					 | 
				
			||||||
+			int i;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+			for (i = 0; i < num_buses[monitor]; i++) {
 | 
					 | 
				
			||||||
+				int bus = mon->current_bus[i];
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+				read_bus_watcher(monitor,
 | 
					 | 
				
			||||||
+					BW0_CTRL + i*BW_PITCH,
 | 
					 | 
				
			||||||
+					(u32 *)&mon->results[bus].results);
 | 
					 | 
				
			||||||
+			}
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	mutex_unlock(&state->lock);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int monitor_thread(void *data)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct rpi_axiperf *state  = data;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	while (1) {
 | 
					 | 
				
			||||||
+		monitor(state);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		if (kthread_should_stop())
 | 
					 | 
				
			||||||
+			return 0;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static ssize_t myreader(struct file *fp, char __user *user_buffer,
 | 
					 | 
				
			||||||
+			size_t count, loff_t *position)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+#define INIT_BUFF_SIZE 2048
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	int i;
 | 
					 | 
				
			||||||
+	int idx = (int)(uintptr_t)(fp->private_data);
 | 
					 | 
				
			||||||
+	int num_buses, cnt;
 | 
					 | 
				
			||||||
+	char *string_buffer;
 | 
					 | 
				
			||||||
+	int buff_size = INIT_BUFF_SIZE;
 | 
					 | 
				
			||||||
+	char *p;
 | 
					 | 
				
			||||||
+	typeof(state->monitor[0]) *mon = &(state->monitor[idx]);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (idx < 0 || idx > NUM_MONITORS)
 | 
					 | 
				
			||||||
+		idx = 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	num_buses = idx == SYSTEM_MONITOR ? num_system_buses : num_vpu_buses;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	string_buffer = kmalloc(buff_size, GFP_KERNEL);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (!string_buffer) {
 | 
					 | 
				
			||||||
+		dev_err(&state->dev->dev,
 | 
					 | 
				
			||||||
+				"Failed temporary string allocation\n");
 | 
					 | 
				
			||||||
+		return 0;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	p = string_buffer;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	mutex_lock(&state->lock);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (mon->bus_filter) {
 | 
					 | 
				
			||||||
+		int filt = min(mon->bus_filter & 0x1f, num_bus_filters);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		cnt = snprintf(p, buff_size,
 | 
					 | 
				
			||||||
+			       "\nMonitoring transactions from %s only\n",
 | 
					 | 
				
			||||||
+			       bus_filter_strings[filt]);
 | 
					 | 
				
			||||||
+		p += cnt;
 | 
					 | 
				
			||||||
+		buff_size -= cnt;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	cnt = snprintf(p, buff_size, "     Bus   |    Atrans    Atwait      AMax    Wtrans    Wtwait      WMax    Rtrans    Rtwait      RMax\n"
 | 
					 | 
				
			||||||
+				     "======================================================================================================\n");
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (cnt >= buff_size)
 | 
					 | 
				
			||||||
+		goto done;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	p += cnt;
 | 
					 | 
				
			||||||
+	buff_size -= cnt;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	for (i = 0; i < num_buses; i++) {
 | 
					 | 
				
			||||||
+		if (mon->bus_enabled & (1 << i)) {
 | 
					 | 
				
			||||||
+#define DIVIDER (1024)
 | 
					 | 
				
			||||||
+			typeof(mon->results[0]) *res = &(mon->results[i]);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+			cnt = snprintf(p, buff_size,
 | 
					 | 
				
			||||||
+					"%10s | %8uK %8uK %8uK %8uK %8uK %8uK %8uK %8uK %8uK\n",
 | 
					 | 
				
			||||||
+					idx == SYSTEM_MONITOR ?
 | 
					 | 
				
			||||||
+						system_bus_string[i] :
 | 
					 | 
				
			||||||
+						vpu_bus_string[i],
 | 
					 | 
				
			||||||
+					res->atrans/DIVIDER,
 | 
					 | 
				
			||||||
+					res->atwait/DIVIDER,
 | 
					 | 
				
			||||||
+					res->amax/DIVIDER,
 | 
					 | 
				
			||||||
+					res->wtrans/DIVIDER,
 | 
					 | 
				
			||||||
+					res->wtwait/DIVIDER,
 | 
					 | 
				
			||||||
+					res->wmax/DIVIDER,
 | 
					 | 
				
			||||||
+					res->rtrans/DIVIDER,
 | 
					 | 
				
			||||||
+					res->rtwait/DIVIDER,
 | 
					 | 
				
			||||||
+					res->rmax/DIVIDER
 | 
					 | 
				
			||||||
+					);
 | 
					 | 
				
			||||||
+			if (cnt >= buff_size)
 | 
					 | 
				
			||||||
+				goto done;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+			p += cnt;
 | 
					 | 
				
			||||||
+			buff_size -= cnt;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	mutex_unlock(&state->lock);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+done:
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* did the last string entry exceeed our buffer size? ie out of string
 | 
					 | 
				
			||||||
+	 * buffer space. Null terminate, use what we have.
 | 
					 | 
				
			||||||
+	 */
 | 
					 | 
				
			||||||
+	if (cnt >= buff_size) {
 | 
					 | 
				
			||||||
+		buff_size = 0;
 | 
					 | 
				
			||||||
+		string_buffer[INIT_BUFF_SIZE] = 0;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	cnt = simple_read_from_buffer(user_buffer, count, position,
 | 
					 | 
				
			||||||
+				      string_buffer,
 | 
					 | 
				
			||||||
+				      INIT_BUFF_SIZE - buff_size);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	kfree(string_buffer);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return cnt;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static ssize_t mywriter(struct file *fp, const char __user *user_buffer,
 | 
					 | 
				
			||||||
+			size_t count, loff_t *position)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int idx = (int)(uintptr_t)(fp->private_data);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (idx < 0 || idx > NUM_MONITORS)
 | 
					 | 
				
			||||||
+		idx = 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* At the moment, this does nothing, but in the future it could be
 | 
					 | 
				
			||||||
+	 * used to reset counters etc
 | 
					 | 
				
			||||||
+	 */
 | 
					 | 
				
			||||||
+	return count;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static const struct file_operations fops_debug = {
 | 
					 | 
				
			||||||
+	.read = myreader,
 | 
					 | 
				
			||||||
+	.write = mywriter,
 | 
					 | 
				
			||||||
+	.open = simple_open
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int rpi_axiperf_probe(struct platform_device *pdev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int ret = 0, i;
 | 
					 | 
				
			||||||
+	struct device *dev = &pdev->dev;
 | 
					 | 
				
			||||||
+	struct device_node *np = dev->of_node;
 | 
					 | 
				
			||||||
+	struct device_node *fw_node;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	state = kzalloc(sizeof(struct rpi_axiperf), GFP_KERNEL);
 | 
					 | 
				
			||||||
+	if (!state)
 | 
					 | 
				
			||||||
+		return -ENOMEM;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* Get the firmware handle for future rpi-firmware-xxx calls */
 | 
					 | 
				
			||||||
+	fw_node = of_parse_phandle(np, "firmware", 0);
 | 
					 | 
				
			||||||
+	if (!fw_node) {
 | 
					 | 
				
			||||||
+		dev_err(dev, "Missing firmware node\n");
 | 
					 | 
				
			||||||
+		return -ENOENT;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	state->firmware = rpi_firmware_get(fw_node);
 | 
					 | 
				
			||||||
+	if (!state->firmware)
 | 
					 | 
				
			||||||
+		return -EPROBE_DEFER;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* Special case for the VPU monitor, we must use the mailbox interface
 | 
					 | 
				
			||||||
+	 * as it is not accessible from the ARM address space.
 | 
					 | 
				
			||||||
+	 */
 | 
					 | 
				
			||||||
+	state->monitor[VPU_MONITOR].use_mailbox_interface = 1;
 | 
					 | 
				
			||||||
+	state->monitor[SYSTEM_MONITOR].use_mailbox_interface = 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	for (i = 0; i < NUM_MONITORS; i++) {
 | 
					 | 
				
			||||||
+		if (state->monitor[i].use_mailbox_interface) {
 | 
					 | 
				
			||||||
+			 of_property_read_u32_index(np, "reg", i*2,
 | 
					 | 
				
			||||||
+				(u32 *)(&state->monitor[i].base_address));
 | 
					 | 
				
			||||||
+		} else {
 | 
					 | 
				
			||||||
+			struct resource *resource =
 | 
					 | 
				
			||||||
+				platform_get_resource(pdev, IORESOURCE_MEM, i);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+			state->monitor[i].base_address =
 | 
					 | 
				
			||||||
+				devm_ioremap_resource(&pdev->dev, resource);
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		if (IS_ERR(state->monitor[i].base_address))
 | 
					 | 
				
			||||||
+			return PTR_ERR(state->monitor[i].base_address);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		/* Enable all buses by default */
 | 
					 | 
				
			||||||
+		state->monitor[i].bus_enabled = 0xffff;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	state->dev = pdev;
 | 
					 | 
				
			||||||
+	platform_set_drvdata(pdev, state);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	state->sample_time = DEFAULT_SAMPLE_TIME;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* Set up all the debugfs stuff */
 | 
					 | 
				
			||||||
+	state->root_folder = debugfs_create_dir(KBUILD_MODNAME, NULL);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	for (i = 0; i < NUM_MONITORS; i++) {
 | 
					 | 
				
			||||||
+		state->monitor[i].debugfs_entry =
 | 
					 | 
				
			||||||
+			debugfs_create_dir(monitor_name[i], state->root_folder);
 | 
					 | 
				
			||||||
+		if (IS_ERR(state->monitor[i].debugfs_entry))
 | 
					 | 
				
			||||||
+			state->monitor[i].debugfs_entry = NULL;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		debugfs_create_file("data", 0444,
 | 
					 | 
				
			||||||
+				    state->monitor[i].debugfs_entry,
 | 
					 | 
				
			||||||
+				    (void *)(uintptr_t)i, &fops_debug);
 | 
					 | 
				
			||||||
+		debugfs_create_u32("enable", 0644,
 | 
					 | 
				
			||||||
+				   state->monitor[i].debugfs_entry,
 | 
					 | 
				
			||||||
+				   &state->monitor[i].bus_enabled);
 | 
					 | 
				
			||||||
+		debugfs_create_u32("filter", 0644,
 | 
					 | 
				
			||||||
+				   state->monitor[i].debugfs_entry,
 | 
					 | 
				
			||||||
+				   &state->monitor[i].bus_filter);
 | 
					 | 
				
			||||||
+		debugfs_create_u32("sample_time", 0644,
 | 
					 | 
				
			||||||
+				   state->monitor[i].debugfs_entry,
 | 
					 | 
				
			||||||
+				   &state->sample_time);
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	mutex_init(&state->lock);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	state->monitor_thread = kthread_run(monitor_thread, state,
 | 
					 | 
				
			||||||
+					    "rpi-axiperfmon");
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int rpi_axiperf_remove(struct platform_device *dev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int ret = 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	kthread_stop(state->monitor_thread);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	debugfs_remove_recursive(state->root_folder);
 | 
					 | 
				
			||||||
+	state->root_folder = NULL;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return ret;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static const struct of_device_id rpi_axiperf_match[] = {
 | 
					 | 
				
			||||||
+	{
 | 
					 | 
				
			||||||
+		.compatible = "brcm,bcm2835-axiperf",
 | 
					 | 
				
			||||||
+	},
 | 
					 | 
				
			||||||
+	{},
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+MODULE_DEVICE_TABLE(of, rpi_axiperf_match);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct platform_driver rpi_axiperf_driver  = {
 | 
					 | 
				
			||||||
+	.probe =	rpi_axiperf_probe,
 | 
					 | 
				
			||||||
+	.remove =	rpi_axiperf_remove,
 | 
					 | 
				
			||||||
+	.driver = {
 | 
					 | 
				
			||||||
+		.name   = "rpi-bcm2835-axiperf",
 | 
					 | 
				
			||||||
+		.of_match_table = of_match_ptr(rpi_axiperf_match),
 | 
					 | 
				
			||||||
+	},
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+module_platform_driver(rpi_axiperf_driver);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* Module information */
 | 
					 | 
				
			||||||
+MODULE_AUTHOR("James Hughes <james.hughes@raspberrypi.org>");
 | 
					 | 
				
			||||||
+MODULE_DESCRIPTION("RPI AXI Performance monitor driver");
 | 
					 | 
				
			||||||
+MODULE_LICENSE("GPL");
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
@ -1,63 +0,0 @@
 | 
				
			|||||||
From 65565784e5faad0af487d54f3f92d9d4c9c38e4a Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= <noralf@tronnes.org>
 | 
					 | 
				
			||||||
Date: Wed, 3 Jun 2015 12:26:13 +0200
 | 
					 | 
				
			||||||
Subject: [PATCH] ARM: bcm2835: Set Serial number and Revision
 | 
					 | 
				
			||||||
MIME-Version: 1.0
 | 
					 | 
				
			||||||
Content-Type: text/plain; charset=UTF-8
 | 
					 | 
				
			||||||
Content-Transfer-Encoding: 8bit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The VideoCore bootloader passes in Serial number and
 | 
					 | 
				
			||||||
Revision number through Device Tree. Make these available to
 | 
					 | 
				
			||||||
userspace through /proc/cpuinfo.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Mainline status:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
There is a commit in linux-next that standardize passing the serial
 | 
					 | 
				
			||||||
number through Device Tree (string: /serial-number):
 | 
					 | 
				
			||||||
ARM: 8355/1: arch: Show the serial number from devicetree in cpuinfo
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
There was an attempt to do the same with the revision number, but it
 | 
					 | 
				
			||||||
didn't get in:
 | 
					 | 
				
			||||||
[PATCH v2 1/2] arm: devtree: Set system_rev from DT revision
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 arch/arm/mach-bcm/board_bcm2835.c | 14 ++++++++++++++
 | 
					 | 
				
			||||||
 1 file changed, 14 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/arch/arm/mach-bcm/board_bcm2835.c
 | 
					 | 
				
			||||||
+++ b/arch/arm/mach-bcm/board_bcm2835.c
 | 
					 | 
				
			||||||
@@ -6,12 +6,25 @@
 | 
					 | 
				
			||||||
 #include <linux/init.h>
 | 
					 | 
				
			||||||
 #include <linux/irqchip.h>
 | 
					 | 
				
			||||||
 #include <linux/of_address.h>
 | 
					 | 
				
			||||||
+#include <asm/system_info.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #include <asm/mach/arch.h>
 | 
					 | 
				
			||||||
 #include <asm/mach/map.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #include "platsmp.h"
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static void __init bcm2835_init(void)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct device_node *np = of_find_node_by_path("/system");
 | 
					 | 
				
			||||||
+	u32 val;
 | 
					 | 
				
			||||||
+	u64 val64;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (!of_property_read_u32(np, "linux,revision", &val))
 | 
					 | 
				
			||||||
+		system_rev = val;
 | 
					 | 
				
			||||||
+	if (!of_property_read_u64(np, "linux,serial", &val64))
 | 
					 | 
				
			||||||
+		system_serial_low = val64;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static const char * const bcm2835_compat[] = {
 | 
					 | 
				
			||||||
 #ifdef CONFIG_ARCH_MULTI_V6
 | 
					 | 
				
			||||||
 	"brcm,bcm2835",
 | 
					 | 
				
			||||||
@@ -24,6 +37,7 @@ static const char * const bcm2835_compat
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 DT_MACHINE_START(BCM2835, "BCM2835")
 | 
					 | 
				
			||||||
+	.init_machine = bcm2835_init,
 | 
					 | 
				
			||||||
 	.dt_compat = bcm2835_compat,
 | 
					 | 
				
			||||||
 	.smp = smp_ops(bcm2836_smp_ops),
 | 
					 | 
				
			||||||
 MACHINE_END
 | 
					 | 
				
			||||||
@ -1,116 +0,0 @@
 | 
				
			|||||||
From 347167bd9ac2a68870064edd921f2525bd569c6d Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Mon, 16 Jul 2018 14:40:13 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] dwc-otg: FIQ: Fix "bad mode in data abort handler"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Create a semi-static mapping for the USB registers early in the boot
 | 
					 | 
				
			||||||
process, before additional kernel threads are started, so all threads
 | 
					 | 
				
			||||||
will have the mappings from the start. This avoids the need for
 | 
					 | 
				
			||||||
data aborts to lazily update them.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
See: https://github.com/raspberrypi/linux/issues/2450
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Floris Bos <bos@je-eigen-domein.nl>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 arch/arm/mach-bcm/board_bcm2835.c | 69 +++++++++++++++++++++++++++++++
 | 
					 | 
				
			||||||
 1 file changed, 69 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/arch/arm/mach-bcm/board_bcm2835.c
 | 
					 | 
				
			||||||
+++ b/arch/arm/mach-bcm/board_bcm2835.c
 | 
					 | 
				
			||||||
@@ -6,6 +6,7 @@
 | 
					 | 
				
			||||||
 #include <linux/init.h>
 | 
					 | 
				
			||||||
 #include <linux/irqchip.h>
 | 
					 | 
				
			||||||
 #include <linux/of_address.h>
 | 
					 | 
				
			||||||
+#include <linux/of_fdt.h>
 | 
					 | 
				
			||||||
 #include <asm/system_info.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #include <asm/mach/arch.h>
 | 
					 | 
				
			||||||
@@ -13,6 +14,9 @@
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #include "platsmp.h"
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+#define BCM2835_USB_VIRT_BASE   0xf0980000
 | 
					 | 
				
			||||||
+#define BCM2835_USB_VIRT_MPHI   0xf0006000
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static void __init bcm2835_init(void)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct device_node *np = of_find_node_by_path("/system");
 | 
					 | 
				
			||||||
@@ -25,6 +29,70 @@ static void __init bcm2835_init(void)
 | 
					 | 
				
			||||||
 		system_serial_low = val64;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ * We need to map registers that are going to be accessed by the FIQ
 | 
					 | 
				
			||||||
+ * very early, before any kernel threads are spawned. Because if done
 | 
					 | 
				
			||||||
+ * later, the mapping tables are not updated instantly but lazily upon
 | 
					 | 
				
			||||||
+ * first access through a data abort handler. While that is fine
 | 
					 | 
				
			||||||
+ * when executing regular kernel code, if the first access in a specific
 | 
					 | 
				
			||||||
+ * thread happens while running FIQ code this will result in a panic.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * For more background see the following old mailing list thread:
 | 
					 | 
				
			||||||
+ * https://www.spinics.net/lists/arm-kernel/msg325250.html
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+static int __init bcm2835_map_usb(unsigned long node, const char *uname,
 | 
					 | 
				
			||||||
+					int depth, void *data)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct map_desc map[2];
 | 
					 | 
				
			||||||
+	const __be32 *reg;
 | 
					 | 
				
			||||||
+	int len;
 | 
					 | 
				
			||||||
+	unsigned long p2b_offset = *((unsigned long *) data);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (!of_flat_dt_is_compatible(node, "brcm,bcm2708-usb"))
 | 
					 | 
				
			||||||
+		return 0;
 | 
					 | 
				
			||||||
+	reg = of_get_flat_dt_prop(node, "reg", &len);
 | 
					 | 
				
			||||||
+	if (!reg || len != (sizeof(unsigned long) * 4))
 | 
					 | 
				
			||||||
+		return 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* Use information about the physical addresses of the
 | 
					 | 
				
			||||||
+	 * registers from the device tree, but use legacy
 | 
					 | 
				
			||||||
+	 * iotable_init() static mapping function to map them,
 | 
					 | 
				
			||||||
+	 * as ioremap() is not functional at this stage in boot.
 | 
					 | 
				
			||||||
+	 */
 | 
					 | 
				
			||||||
+	map[0].virtual = (unsigned long) BCM2835_USB_VIRT_BASE;
 | 
					 | 
				
			||||||
+	map[0].pfn = __phys_to_pfn(be32_to_cpu(reg[0]) - p2b_offset);
 | 
					 | 
				
			||||||
+	map[0].length = be32_to_cpu(reg[1]);
 | 
					 | 
				
			||||||
+	map[0].type = MT_DEVICE;
 | 
					 | 
				
			||||||
+	map[1].virtual = (unsigned long) BCM2835_USB_VIRT_MPHI;
 | 
					 | 
				
			||||||
+	map[1].pfn = __phys_to_pfn(be32_to_cpu(reg[2]) - p2b_offset);
 | 
					 | 
				
			||||||
+	map[1].length = be32_to_cpu(reg[3]);
 | 
					 | 
				
			||||||
+	map[1].type = MT_DEVICE;
 | 
					 | 
				
			||||||
+		iotable_init(map, 2);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 1;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void __init bcm2835_map_io(void)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	const __be32 *ranges;
 | 
					 | 
				
			||||||
+	int soc, len;
 | 
					 | 
				
			||||||
+	unsigned long p2b_offset;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	debug_ll_io_init();
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* Find out how to map bus to physical address first from soc/ranges */
 | 
					 | 
				
			||||||
+	soc = of_get_flat_dt_subnode_by_name(of_get_flat_dt_root(), "soc");
 | 
					 | 
				
			||||||
+	if (soc < 0)
 | 
					 | 
				
			||||||
+		return;
 | 
					 | 
				
			||||||
+	ranges = of_get_flat_dt_prop(soc, "ranges", &len);
 | 
					 | 
				
			||||||
+	if (!ranges || len < (sizeof(unsigned long) * 3))
 | 
					 | 
				
			||||||
+		return;
 | 
					 | 
				
			||||||
+	p2b_offset = be32_to_cpu(ranges[0]) - be32_to_cpu(ranges[1]);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	/* Now search for bcm2708-usb node in device tree */
 | 
					 | 
				
			||||||
+	of_scan_flat_dt(bcm2835_map_usb, &p2b_offset);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static const char * const bcm2835_compat[] = {
 | 
					 | 
				
			||||||
 #ifdef CONFIG_ARCH_MULTI_V6
 | 
					 | 
				
			||||||
 	"brcm,bcm2835",
 | 
					 | 
				
			||||||
@@ -37,6 +105,7 @@ static const char * const bcm2835_compat
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 DT_MACHINE_START(BCM2835, "BCM2835")
 | 
					 | 
				
			||||||
+	.map_io = bcm2835_map_io,
 | 
					 | 
				
			||||||
 	.init_machine = bcm2835_init,
 | 
					 | 
				
			||||||
 	.dt_compat = bcm2835_compat,
 | 
					 | 
				
			||||||
 	.smp = smp_ops(bcm2836_smp_ops),
 | 
					 | 
				
			||||||
@ -1,36 +0,0 @@
 | 
				
			|||||||
From 594abebdffdcff606bc445f2661d9a504f180136 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Mon, 11 Dec 2017 09:18:32 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] ARM: Activate FIQs to avoid __irq_startup warnings
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
There is a new test in __irq_startup that the IRQ is activated, which
 | 
					 | 
				
			||||||
hasn't been the case for FIQs since they bypass some of the usual setup.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Augment enable_fiq to include a call to irq_activate to avoid the
 | 
					 | 
				
			||||||
warning.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 arch/arm/kernel/fiq.c | 4 ++++
 | 
					 | 
				
			||||||
 1 file changed, 4 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/arch/arm/kernel/fiq.c
 | 
					 | 
				
			||||||
+++ b/arch/arm/kernel/fiq.c
 | 
					 | 
				
			||||||
@@ -56,6 +56,8 @@
 | 
					 | 
				
			||||||
 static unsigned long dfl_fiq_insn;
 | 
					 | 
				
			||||||
 static struct pt_regs dfl_fiq_regs;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+extern int irq_activate(struct irq_desc *desc);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 /* Default reacquire function
 | 
					 | 
				
			||||||
  * - we always relinquish FIQ control
 | 
					 | 
				
			||||||
  * - we always reacquire FIQ control
 | 
					 | 
				
			||||||
@@ -140,6 +142,8 @@ static int fiq_start;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 void enable_fiq(int fiq)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
+	struct irq_desc *desc = irq_to_desc(fiq + fiq_start);
 | 
					 | 
				
			||||||
+	irq_activate(desc);
 | 
					 | 
				
			||||||
 	enable_irq(fiq + fiq_start);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@ -1,32 +0,0 @@
 | 
				
			|||||||
From 9cf689eda644ccba0c80982cbd821c9163f5de39 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Eric Anholt <eric@anholt.net>
 | 
					 | 
				
			||||||
Date: Wed, 14 Sep 2016 09:16:19 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] raspberrypi-firmware: Export the general transaction
 | 
					 | 
				
			||||||
 function.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The vc4-firmware-kms module is going to be doing the MBOX FB call.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Eric Anholt <eric@anholt.net>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/firmware/raspberrypi.c | 3 ++-
 | 
					 | 
				
			||||||
 1 file changed, 2 insertions(+), 1 deletion(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/firmware/raspberrypi.c
 | 
					 | 
				
			||||||
+++ b/drivers/firmware/raspberrypi.c
 | 
					 | 
				
			||||||
@@ -46,7 +46,7 @@ static void response_callback(struct mbo
 | 
					 | 
				
			||||||
  * Sends a request to the firmware through the BCM2835 mailbox driver,
 | 
					 | 
				
			||||||
  * and synchronously waits for the reply.
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
-static int
 | 
					 | 
				
			||||||
+int
 | 
					 | 
				
			||||||
 rpi_firmware_transaction(struct rpi_firmware *fw, u32 chan, u32 data)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	u32 message = MBOX_MSG(chan, data);
 | 
					 | 
				
			||||||
@@ -71,6 +71,7 @@ rpi_firmware_transaction(struct rpi_firm
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	return ret;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
+EXPORT_SYMBOL_GPL(rpi_firmware_transaction);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 /**
 | 
					 | 
				
			||||||
  * rpi_firmware_property_list - Submit firmware property list
 | 
					 | 
				
			||||||
@ -1,35 +0,0 @@
 | 
				
			|||||||
From 798407c217fee8f4869bf76d9cd2cda16f93e24f Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Tue, 20 Feb 2018 10:07:27 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] i2c-gpio: Also set bus numbers from reg property
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
I2C busses can be assigned specific bus numbers using aliases in
 | 
					 | 
				
			||||||
Device Tree - string properties where the name is the alias and the
 | 
					 | 
				
			||||||
value is the path to the node. The current DT parameter mechanism
 | 
					 | 
				
			||||||
does not allow property names to be derived from a parameter value
 | 
					 | 
				
			||||||
in any way, so it isn't possible to generate unique or matching
 | 
					 | 
				
			||||||
aliases for nodes from an overlay that can generate multiple
 | 
					 | 
				
			||||||
instances, e.g. i2c-gpio.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Work around this limitation (at least temporarily) by allowing
 | 
					 | 
				
			||||||
the i2c adapter number to be initialised from the "reg" property
 | 
					 | 
				
			||||||
if present.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/i2c/busses/i2c-gpio.c | 4 +++-
 | 
					 | 
				
			||||||
 1 file changed, 3 insertions(+), 1 deletion(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/i2c/busses/i2c-gpio.c
 | 
					 | 
				
			||||||
+++ b/drivers/i2c/busses/i2c-gpio.c
 | 
					 | 
				
			||||||
@@ -445,7 +445,9 @@ static int i2c_gpio_probe(struct platfor
 | 
					 | 
				
			||||||
 	adap->dev.parent = dev;
 | 
					 | 
				
			||||||
 	adap->dev.of_node = np;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	adap->nr = pdev->id;
 | 
					 | 
				
			||||||
+	if (pdev->id != PLATFORM_DEVID_NONE || !pdev->dev.of_node ||
 | 
					 | 
				
			||||||
+	    of_property_read_u32(pdev->dev.of_node, "reg", &adap->nr))
 | 
					 | 
				
			||||||
+		adap->nr = pdev->id;
 | 
					 | 
				
			||||||
 	ret = i2c_bit_add_numbered_bus(adap);
 | 
					 | 
				
			||||||
 	if (ret)
 | 
					 | 
				
			||||||
 		return ret;
 | 
					 | 
				
			||||||
@ -1,21 +0,0 @@
 | 
				
			|||||||
From 781bfd769e192aa39ef93fa37c812af444a9cf93 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: hdoverobinson <hdoverobinson@gmail.com>
 | 
					 | 
				
			||||||
Date: Tue, 13 Mar 2018 06:58:39 -0400
 | 
					 | 
				
			||||||
Subject: [PATCH] added capture_clear option to pps-gpio via dtoverlay
 | 
					 | 
				
			||||||
 (#2433)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/pps/clients/pps-gpio.c | 2 ++
 | 
					 | 
				
			||||||
 1 file changed, 2 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/pps/clients/pps-gpio.c
 | 
					 | 
				
			||||||
+++ b/drivers/pps/clients/pps-gpio.c
 | 
					 | 
				
			||||||
@@ -145,6 +145,8 @@ static int pps_gpio_setup(struct platfor
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	if (of_property_read_bool(np, "assert-falling-edge"))
 | 
					 | 
				
			||||||
 		data->assert_falling_edge = true;
 | 
					 | 
				
			||||||
+        if (of_property_read_bool(np, "capture-clear"))
 | 
					 | 
				
			||||||
+                data->capture_clear = true;
 | 
					 | 
				
			||||||
 	return 0;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@ -1,40 +0,0 @@
 | 
				
			|||||||
From 1fbf5f72be35918230b0bbdff3bcf44318f1374a Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Fri, 9 Mar 2018 12:01:00 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] lan78xx: Read initial EEE status from DT
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add two new DT properties:
 | 
					 | 
				
			||||||
* microchip,eee-enabled  - a boolean to enable EEE
 | 
					 | 
				
			||||||
* microchip,tx-lpi-timer - time in microseconds to wait before entering
 | 
					 | 
				
			||||||
                           low power state
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/usb/lan78xx.c | 16 ++++++++++++++++
 | 
					 | 
				
			||||||
 1 file changed, 16 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/usb/lan78xx.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/usb/lan78xx.c
 | 
					 | 
				
			||||||
@@ -2645,6 +2645,22 @@ static int lan78xx_open(struct net_devic
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	netif_dbg(dev, ifup, dev->net, "phy initialised successfully");
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	if (of_property_read_bool(dev->udev->dev.of_node,
 | 
					 | 
				
			||||||
+				  "microchip,eee-enabled")) {
 | 
					 | 
				
			||||||
+		struct ethtool_eee edata;
 | 
					 | 
				
			||||||
+		memset(&edata, 0, sizeof(edata));
 | 
					 | 
				
			||||||
+		edata.cmd = ETHTOOL_SEEE;
 | 
					 | 
				
			||||||
+		edata.advertised = ADVERTISED_1000baseT_Full |
 | 
					 | 
				
			||||||
+				   ADVERTISED_100baseT_Full;
 | 
					 | 
				
			||||||
+		edata.eee_enabled = true;
 | 
					 | 
				
			||||||
+		edata.tx_lpi_enabled = true;
 | 
					 | 
				
			||||||
+		if (of_property_read_u32(dev->udev->dev.of_node,
 | 
					 | 
				
			||||||
+					 "microchip,tx-lpi-timer",
 | 
					 | 
				
			||||||
+					 &edata.tx_lpi_timer))
 | 
					 | 
				
			||||||
+			edata.tx_lpi_timer = 600; /* non-aggressive */
 | 
					 | 
				
			||||||
+		(void)lan78xx_set_eee(net, &edata);
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	/* for Link Check */
 | 
					 | 
				
			||||||
 	if (dev->urb_intr) {
 | 
					 | 
				
			||||||
 		ret = usb_submit_urb(dev->urb_intr, GFP_KERNEL);
 | 
					 | 
				
			||||||
@ -1,32 +0,0 @@
 | 
				
			|||||||
From 32648d9e5440756f571bff70e8c49069dbba5137 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: popcornmix <popcornmix@gmail.com>
 | 
					 | 
				
			||||||
Date: Mon, 14 Jul 2014 22:02:09 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] hid: Reduce default mouse polling interval to 60Hz
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Reduces overhead when using X
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/hid/usbhid/hid-core.c | 6 ++++--
 | 
					 | 
				
			||||||
 1 file changed, 4 insertions(+), 2 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/hid/usbhid/hid-core.c
 | 
					 | 
				
			||||||
+++ b/drivers/hid/usbhid/hid-core.c
 | 
					 | 
				
			||||||
@@ -45,7 +45,7 @@
 | 
					 | 
				
			||||||
  * Module parameters.
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-static unsigned int hid_mousepoll_interval;
 | 
					 | 
				
			||||||
+static unsigned int hid_mousepoll_interval = ~0;
 | 
					 | 
				
			||||||
 module_param_named(mousepoll, hid_mousepoll_interval, uint, 0644);
 | 
					 | 
				
			||||||
 MODULE_PARM_DESC(mousepoll, "Polling interval of mice");
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -1114,7 +1114,9 @@ static int usbhid_start(struct hid_devic
 | 
					 | 
				
			||||||
 		 */
 | 
					 | 
				
			||||||
 		switch (hid->collection->usage) {
 | 
					 | 
				
			||||||
 		case HID_GD_MOUSE:
 | 
					 | 
				
			||||||
-			if (hid_mousepoll_interval > 0)
 | 
					 | 
				
			||||||
+			if (hid_mousepoll_interval == ~0 && interval < 16)
 | 
					 | 
				
			||||||
+				interval = 16;
 | 
					 | 
				
			||||||
+			else if (hid_mousepoll_interval != ~0 && hid_mousepoll_interval != 0)
 | 
					 | 
				
			||||||
 				interval = hid_mousepoll_interval;
 | 
					 | 
				
			||||||
 			break;
 | 
					 | 
				
			||||||
 		case HID_GD_JOYSTICK:
 | 
					 | 
				
			||||||
@ -1,57 +0,0 @@
 | 
				
			|||||||
From a8d5d1b7fbc78132f5def4b8af4f221df83df04e Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Nick Bulleid <nedbulleid@fastmail.com>
 | 
					 | 
				
			||||||
Date: Thu, 10 May 2018 21:57:02 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] Add ability to export gpio used by gpio-poweroff
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Nick Bulleid <nedbulleid@fastmail.com>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Added export feature to gpio-poweroff documentation
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Nick Bulleid <nedbulleid@fastmail.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 .../devicetree/bindings/power/reset/gpio-poweroff.txt    | 1 +
 | 
					 | 
				
			||||||
 drivers/power/reset/gpio-poweroff.c                      | 9 +++++++++
 | 
					 | 
				
			||||||
 2 files changed, 10 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/Documentation/devicetree/bindings/power/reset/gpio-poweroff.txt
 | 
					 | 
				
			||||||
+++ b/Documentation/devicetree/bindings/power/reset/gpio-poweroff.txt
 | 
					 | 
				
			||||||
@@ -31,6 +31,7 @@ Optional properties:
 | 
					 | 
				
			||||||
 - inactive-delay-ms: Delay (default 100) to wait after driving gpio inactive
 | 
					 | 
				
			||||||
 - timeout-ms: Time to wait before asserting a WARN_ON(1). If nothing is
 | 
					 | 
				
			||||||
               specified, 3000 ms is used.
 | 
					 | 
				
			||||||
+- export : Export the GPIO line to the sysfs system
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 Examples:
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
--- a/drivers/power/reset/gpio-poweroff.c
 | 
					 | 
				
			||||||
+++ b/drivers/power/reset/gpio-poweroff.c
 | 
					 | 
				
			||||||
@@ -51,6 +51,7 @@ static int gpio_poweroff_probe(struct pl
 | 
					 | 
				
			||||||
 	bool input = false;
 | 
					 | 
				
			||||||
 	enum gpiod_flags flags;
 | 
					 | 
				
			||||||
 	bool force = false;
 | 
					 | 
				
			||||||
+	bool export = false;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* If a pm_power_off function has already been added, leave it alone */
 | 
					 | 
				
			||||||
 	force = of_property_read_bool(pdev->dev.of_node, "force");
 | 
					 | 
				
			||||||
@@ -76,6 +77,12 @@ static int gpio_poweroff_probe(struct pl
 | 
					 | 
				
			||||||
 	if (IS_ERR(reset_gpio))
 | 
					 | 
				
			||||||
 		return PTR_ERR(reset_gpio);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	export = of_property_read_bool(pdev->dev.of_node, "export");
 | 
					 | 
				
			||||||
+	if (export) {
 | 
					 | 
				
			||||||
+		gpiod_export(reset_gpio, false);
 | 
					 | 
				
			||||||
+		gpiod_export_link(&pdev->dev, "poweroff-gpio", reset_gpio);
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	pm_power_off = &gpio_poweroff_do_poweroff;
 | 
					 | 
				
			||||||
 	return 0;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
@@ -85,6 +92,8 @@ static int gpio_poweroff_remove(struct p
 | 
					 | 
				
			||||||
 	if (pm_power_off == &gpio_poweroff_do_poweroff)
 | 
					 | 
				
			||||||
 		pm_power_off = NULL;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	gpiod_unexport(reset_gpio);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	return 0;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@ -1,84 +0,0 @@
 | 
				
			|||||||
From bf6b5dbb7c8423b9d6bdfec21a9122691f2587d9 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Sat, 12 May 2018 21:35:43 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] firmware/raspberrypi: Notify firmware of a reboot
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Register for reboot notifications, sending RPI_FIRMWARE_NOTIFY_REBOOT
 | 
					 | 
				
			||||||
over the mailbox interface on reception.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/firmware/raspberrypi.c | 40 +++++++++++++++++++++++++++++++++-
 | 
					 | 
				
			||||||
 1 file changed, 39 insertions(+), 1 deletion(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/firmware/raspberrypi.c
 | 
					 | 
				
			||||||
+++ b/drivers/firmware/raspberrypi.c
 | 
					 | 
				
			||||||
@@ -12,6 +12,7 @@
 | 
					 | 
				
			||||||
 #include <linux/module.h>
 | 
					 | 
				
			||||||
 #include <linux/of_platform.h>
 | 
					 | 
				
			||||||
 #include <linux/platform_device.h>
 | 
					 | 
				
			||||||
+#include <linux/reboot.h>
 | 
					 | 
				
			||||||
 #include <linux/slab.h>
 | 
					 | 
				
			||||||
 #include <soc/bcm2835/raspberrypi-firmware.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -180,6 +181,26 @@ int rpi_firmware_property(struct rpi_fir
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 EXPORT_SYMBOL_GPL(rpi_firmware_property);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static int rpi_firmware_notify_reboot(struct notifier_block *nb,
 | 
					 | 
				
			||||||
+				      unsigned long action,
 | 
					 | 
				
			||||||
+				      void *data)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct rpi_firmware *fw;
 | 
					 | 
				
			||||||
+	struct platform_device *pdev = g_pdev;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (!pdev)
 | 
					 | 
				
			||||||
+		return 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	fw = platform_get_drvdata(pdev);
 | 
					 | 
				
			||||||
+	if (!fw)
 | 
					 | 
				
			||||||
+		return 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	(void)rpi_firmware_property(fw, RPI_FIRMWARE_NOTIFY_REBOOT,
 | 
					 | 
				
			||||||
+				    0, 0);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static void
 | 
					 | 
				
			||||||
 rpi_firmware_print_firmware_revision(struct rpi_firmware *fw)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
@@ -356,15 +377,32 @@ static struct platform_driver rpi_firmwa
 | 
					 | 
				
			||||||
 	.remove		= rpi_firmware_remove,
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static struct notifier_block rpi_firmware_reboot_notifier = {
 | 
					 | 
				
			||||||
+	.notifier_call = rpi_firmware_notify_reboot,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static int __init rpi_firmware_init(void)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
-	return platform_driver_register(&rpi_firmware_driver);
 | 
					 | 
				
			||||||
+	int ret = register_reboot_notifier(&rpi_firmware_reboot_notifier);
 | 
					 | 
				
			||||||
+	if (ret)
 | 
					 | 
				
			||||||
+		goto out1;
 | 
					 | 
				
			||||||
+	ret = platform_driver_register(&rpi_firmware_driver);
 | 
					 | 
				
			||||||
+	if (ret)
 | 
					 | 
				
			||||||
+		goto out2;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+out2:
 | 
					 | 
				
			||||||
+	unregister_reboot_notifier(&rpi_firmware_reboot_notifier);
 | 
					 | 
				
			||||||
+out1:
 | 
					 | 
				
			||||||
+	return ret;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 subsys_initcall(rpi_firmware_init);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static void __init rpi_firmware_exit(void)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	platform_driver_unregister(&rpi_firmware_driver);
 | 
					 | 
				
			||||||
+	unregister_reboot_notifier(&rpi_firmware_reboot_notifier);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 module_exit(rpi_firmware_exit);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@ -1,66 +0,0 @@
 | 
				
			|||||||
From 35a38d723d9cd61be4c39defad4e44b1a6acc4b8 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Thu, 14 Jun 2018 11:21:04 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] irqchip: irq-bcm2835: Calc. FIQ_START at boot-time
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
ad83c7cb2f37 ("irqchip/irq-bcm2836: Add support for DT interrupt polarity")
 | 
					 | 
				
			||||||
changed the way that the BCM2836/7 local interrupts are mapped; instead
 | 
					 | 
				
			||||||
of being pre-mapped they are now mapped on-demand. A side effect of this
 | 
					 | 
				
			||||||
change is that the call to irq_of_parse_and_map from armctrl_of_init
 | 
					 | 
				
			||||||
creates a new mapping, forming a gap between the IRQs and the FIQs. This
 | 
					 | 
				
			||||||
 gap breaks the FIQ<->IRQ mapping which up to now has been done by assuming:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
1) that the value of FIQ_START is the same as the number of normal IRQs
 | 
					 | 
				
			||||||
that will be mapped (still true), and
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
2) that this value is also the offset between an IRQ and its equivalent
 | 
					 | 
				
			||||||
FIQ (which is no longer the case).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Remove both assumptions by measuring the interval between the last IRQ
 | 
					 | 
				
			||||||
and the last FIQ, passing it as the parameter to init_FIQ().
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Fixes: https://github.com/raspberrypi/linux/issues/2432
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/irqchip/irq-bcm2835.c | 8 ++++----
 | 
					 | 
				
			||||||
 1 file changed, 4 insertions(+), 4 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/irqchip/irq-bcm2835.c
 | 
					 | 
				
			||||||
+++ b/drivers/irqchip/irq-bcm2835.c
 | 
					 | 
				
			||||||
@@ -74,8 +74,6 @@
 | 
					 | 
				
			||||||
 #define NR_BANKS		3
 | 
					 | 
				
			||||||
 #define IRQS_PER_BANK		32
 | 
					 | 
				
			||||||
 #define NUMBER_IRQS		MAKE_HWIRQ(NR_BANKS, 0)
 | 
					 | 
				
			||||||
-#undef FIQ_START
 | 
					 | 
				
			||||||
-#define FIQ_START		(NR_IRQS_BANK0 + MAKE_HWIRQ(NR_BANKS - 1, 0))
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static const int reg_pending[] __initconst = { 0x00, 0x04, 0x08 };
 | 
					 | 
				
			||||||
 static const int reg_enable[] __initconst = { 0x18, 0x10, 0x14 };
 | 
					 | 
				
			||||||
@@ -203,7 +201,7 @@ static int __init armctrl_of_init(struct
 | 
					 | 
				
			||||||
 				  bool is_2836)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	void __iomem *base;
 | 
					 | 
				
			||||||
-	int irq, b, i;
 | 
					 | 
				
			||||||
+	int irq = 0, last_irq, b, i;
 | 
					 | 
				
			||||||
 	u32 reg;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	base = of_iomap(node, 0);
 | 
					 | 
				
			||||||
@@ -243,6 +241,8 @@ static int __init armctrl_of_init(struct
 | 
					 | 
				
			||||||
 		pr_err(FW_BUG "Bootloader left fiq enabled\n");
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	last_irq = irq;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	if (is_2836) {
 | 
					 | 
				
			||||||
 		int parent_irq = irq_of_parse_and_map(node, 0);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -273,7 +273,7 @@ static int __init armctrl_of_init(struct
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 #ifndef CONFIG_ARM64
 | 
					 | 
				
			||||||
-	init_FIQ(FIQ_START);
 | 
					 | 
				
			||||||
+	init_FIQ(irq - last_irq);
 | 
					 | 
				
			||||||
 #endif
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	return 0;
 | 
					 | 
				
			||||||
@ -1,114 +0,0 @@
 | 
				
			|||||||
From 342afb4c37627f9d8e9d5bb07b3182a5c412c75e Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Thu, 14 Jun 2018 15:07:26 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] of: configfs: Use of_overlay_fdt_apply API call
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The published API to the dynamic overlay application mechanism now
 | 
					 | 
				
			||||||
takes a Flattened Device Tree blob as input so that it can manage the
 | 
					 | 
				
			||||||
lifetime of the unflattened tree. Conveniently, the new API call -
 | 
					 | 
				
			||||||
of_overlay_fdt_apply - is virtually a drop-in replacement for
 | 
					 | 
				
			||||||
create_overlay, which can now be deleted.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/of/configfs.c | 47 +++++++------------------------------------
 | 
					 | 
				
			||||||
 1 file changed, 7 insertions(+), 40 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/of/configfs.c
 | 
					 | 
				
			||||||
+++ b/drivers/of/configfs.c
 | 
					 | 
				
			||||||
@@ -40,41 +40,6 @@ struct cfs_overlay_item {
 | 
					 | 
				
			||||||
 	int			dtbo_size;
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-static int create_overlay(struct cfs_overlay_item *overlay, void *blob)
 | 
					 | 
				
			||||||
-{
 | 
					 | 
				
			||||||
-	int err;
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	/* unflatten the tree */
 | 
					 | 
				
			||||||
-	of_fdt_unflatten_tree(blob, NULL, &overlay->overlay);
 | 
					 | 
				
			||||||
-	if (overlay->overlay == NULL) {
 | 
					 | 
				
			||||||
-		pr_err("%s: failed to unflatten tree\n", __func__);
 | 
					 | 
				
			||||||
-		err = -EINVAL;
 | 
					 | 
				
			||||||
-		goto out_err;
 | 
					 | 
				
			||||||
-	}
 | 
					 | 
				
			||||||
-	pr_debug("%s: unflattened OK\n", __func__);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	/* mark it as detached */
 | 
					 | 
				
			||||||
-	of_node_set_flag(overlay->overlay, OF_DETACHED);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	/* perform resolution */
 | 
					 | 
				
			||||||
-	err = of_resolve_phandles(overlay->overlay);
 | 
					 | 
				
			||||||
-	if (err != 0) {
 | 
					 | 
				
			||||||
-		pr_err("%s: Failed to resolve tree\n", __func__);
 | 
					 | 
				
			||||||
-		goto out_err;
 | 
					 | 
				
			||||||
-	}
 | 
					 | 
				
			||||||
-	pr_debug("%s: resolved OK\n", __func__);
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	err = of_overlay_apply(overlay->overlay, &overlay->ov_id);
 | 
					 | 
				
			||||||
-	if (err < 0) {
 | 
					 | 
				
			||||||
-		pr_err("%s: Failed to create overlay (err=%d)\n",
 | 
					 | 
				
			||||||
-				__func__, err);
 | 
					 | 
				
			||||||
-		goto out_err;
 | 
					 | 
				
			||||||
-	}
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-out_err:
 | 
					 | 
				
			||||||
-	return err;
 | 
					 | 
				
			||||||
-}
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 static inline struct cfs_overlay_item *to_cfs_overlay_item(
 | 
					 | 
				
			||||||
 		struct config_item *item)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
@@ -115,7 +80,8 @@ static ssize_t cfs_overlay_item_path_sto
 | 
					 | 
				
			||||||
 	if (err != 0)
 | 
					 | 
				
			||||||
 		goto out_err;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	err = create_overlay(overlay, (void *)overlay->fw->data);
 | 
					 | 
				
			||||||
+	err = of_overlay_fdt_apply((void *)overlay->fw->data,
 | 
					 | 
				
			||||||
+				   (u32)overlay->fw->size, &overlay->ov_id);
 | 
					 | 
				
			||||||
 	if (err != 0)
 | 
					 | 
				
			||||||
 		goto out_err;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -136,7 +102,7 @@ static ssize_t cfs_overlay_item_status_s
 | 
					 | 
				
			||||||
 	struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	return sprintf(page, "%s\n",
 | 
					 | 
				
			||||||
-			overlay->ov_id >= 0 ? "applied" : "unapplied");
 | 
					 | 
				
			||||||
+			overlay->ov_id > 0 ? "applied" : "unapplied");
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 CONFIGFS_ATTR(cfs_overlay_item_, path);
 | 
					 | 
				
			||||||
@@ -188,7 +154,8 @@ ssize_t cfs_overlay_item_dtbo_write(stru
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	overlay->dtbo_size = count;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	err = create_overlay(overlay, overlay->dtbo);
 | 
					 | 
				
			||||||
+	err = of_overlay_fdt_apply(overlay->dtbo, overlay->dtbo_size,
 | 
					 | 
				
			||||||
+				   &overlay->ov_id);
 | 
					 | 
				
			||||||
 	if (err != 0)
 | 
					 | 
				
			||||||
 		goto out_err;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -198,6 +165,7 @@ out_err:
 | 
					 | 
				
			||||||
 	kfree(overlay->dtbo);
 | 
					 | 
				
			||||||
 	overlay->dtbo = NULL;
 | 
					 | 
				
			||||||
 	overlay->dtbo_size = 0;
 | 
					 | 
				
			||||||
+	overlay->ov_id = 0;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	return err;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
@@ -213,7 +181,7 @@ static void cfs_overlay_release(struct c
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	struct cfs_overlay_item *overlay = to_cfs_overlay_item(item);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if (overlay->ov_id >= 0)
 | 
					 | 
				
			||||||
+	if (overlay->ov_id > 0)
 | 
					 | 
				
			||||||
 		of_overlay_remove(&overlay->ov_id);
 | 
					 | 
				
			||||||
 	if (overlay->fw)
 | 
					 | 
				
			||||||
 		release_firmware(overlay->fw);
 | 
					 | 
				
			||||||
@@ -241,7 +209,6 @@ static struct config_item *cfs_overlay_g
 | 
					 | 
				
			||||||
 	overlay = kzalloc(sizeof(*overlay), GFP_KERNEL);
 | 
					 | 
				
			||||||
 	if (!overlay)
 | 
					 | 
				
			||||||
 		return ERR_PTR(-ENOMEM);
 | 
					 | 
				
			||||||
-	overlay->ov_id = -1;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	config_item_init_type_name(&overlay->item, name, &cfs_overlay_type);
 | 
					 | 
				
			||||||
 	return &overlay->item;
 | 
					 | 
				
			||||||
@ -1,56 +0,0 @@
 | 
				
			|||||||
From 42606db546588b0e0b8084466b9b31ec111640a4 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Dave Stevenson <dave.stevenson@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Wed, 13 Jun 2018 15:21:10 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] net: lan78xx: Disable TCP Segmentation Offload (TSO)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
TSO seems to be having issues when packets are dropped and the
 | 
					 | 
				
			||||||
remote end uses Selective Acknowledge (SACK) to denote that
 | 
					 | 
				
			||||||
data is missing. The missing data is never resent, so the
 | 
					 | 
				
			||||||
connection eventually stalls.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
There is a module parameter of enable_tso added to allow
 | 
					 | 
				
			||||||
further debugging without forcing a rebuild of the kernel.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
https://github.com/raspberrypi/linux/issues/2449
 | 
					 | 
				
			||||||
https://github.com/raspberrypi/linux/issues/2482
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/usb/lan78xx.c | 19 +++++++++++++++++--
 | 
					 | 
				
			||||||
 1 file changed, 17 insertions(+), 2 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/usb/lan78xx.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/usb/lan78xx.c
 | 
					 | 
				
			||||||
@@ -427,6 +427,15 @@ static int msg_level = -1;
 | 
					 | 
				
			||||||
 module_param(msg_level, int, 0);
 | 
					 | 
				
			||||||
 MODULE_PARM_DESC(msg_level, "Override default message level");
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+/* TSO seems to be having some issue with Selective Acknowledge (SACK) that
 | 
					 | 
				
			||||||
+ * results in lost data never being retransmitted.
 | 
					 | 
				
			||||||
+ * Disable it by default now, but adds a module parameter to enable it for
 | 
					 | 
				
			||||||
+ * debug purposes (the full cause is not currently understood).
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+static bool enable_tso;
 | 
					 | 
				
			||||||
+module_param(enable_tso, bool, 0644);
 | 
					 | 
				
			||||||
+MODULE_PARM_DESC(enable_tso, "Enables TCP segmentation offload");
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static int lan78xx_read_reg(struct lan78xx_net *dev, u32 index, u32 *data)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	u32 *buf = kmalloc(sizeof(u32), GFP_KERNEL);
 | 
					 | 
				
			||||||
@@ -2927,8 +2936,14 @@ static int lan78xx_bind(struct lan78xx_n
 | 
					 | 
				
			||||||
 	if (DEFAULT_RX_CSUM_ENABLE)
 | 
					 | 
				
			||||||
 		dev->net->features |= NETIF_F_RXCSUM;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if (DEFAULT_TSO_CSUM_ENABLE)
 | 
					 | 
				
			||||||
-		dev->net->features |= NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_SG;
 | 
					 | 
				
			||||||
+	if (DEFAULT_TSO_CSUM_ENABLE) {
 | 
					 | 
				
			||||||
+		dev->net->features |= NETIF_F_SG;
 | 
					 | 
				
			||||||
+		/* Use module parameter to control TCP segmentation offload as
 | 
					 | 
				
			||||||
+		 * it appears to cause issues.
 | 
					 | 
				
			||||||
+		 */
 | 
					 | 
				
			||||||
+		if (enable_tso)
 | 
					 | 
				
			||||||
+			dev->net->features |= NETIF_F_TSO | NETIF_F_TSO6;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	if (DEFAULT_VLAN_RX_OFFLOAD)
 | 
					 | 
				
			||||||
 		dev->net->features |= NETIF_F_HW_VLAN_CTAG_RX;
 | 
					 | 
				
			||||||
@ -1,64 +0,0 @@
 | 
				
			|||||||
From c6edee4700821a65c9d94ac8e16b488fc652efd8 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Thu, 5 Apr 2018 14:46:11 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] lan78xx: Move enabling of EEE into PHY init code
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Enable EEE mode as soon as possible after connecting to the PHY, and
 | 
					 | 
				
			||||||
before phy_start. This avoids a second link negotiation, which speeds
 | 
					 | 
				
			||||||
up booting and stops the interface failing to become ready.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
See: https://github.com/raspberrypi/linux/issues/2437
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/usb/lan78xx.c | 32 ++++++++++++++++----------------
 | 
					 | 
				
			||||||
 1 file changed, 16 insertions(+), 16 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/usb/lan78xx.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/usb/lan78xx.c
 | 
					 | 
				
			||||||
@@ -2177,6 +2177,22 @@ static int lan78xx_phy_init(struct lan78
 | 
					 | 
				
			||||||
 	mii_adv_to_linkmode_adv_t(fc, mii_adv);
 | 
					 | 
				
			||||||
 	linkmode_or(phydev->advertising, fc, phydev->advertising);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	if (of_property_read_bool(dev->udev->dev.of_node,
 | 
					 | 
				
			||||||
+				  "microchip,eee-enabled")) {
 | 
					 | 
				
			||||||
+		struct ethtool_eee edata;
 | 
					 | 
				
			||||||
+		memset(&edata, 0, sizeof(edata));
 | 
					 | 
				
			||||||
+		edata.cmd = ETHTOOL_SEEE;
 | 
					 | 
				
			||||||
+		edata.advertised = ADVERTISED_1000baseT_Full |
 | 
					 | 
				
			||||||
+				   ADVERTISED_100baseT_Full;
 | 
					 | 
				
			||||||
+		edata.eee_enabled = true;
 | 
					 | 
				
			||||||
+		edata.tx_lpi_enabled = true;
 | 
					 | 
				
			||||||
+		if (of_property_read_u32(dev->udev->dev.of_node,
 | 
					 | 
				
			||||||
+					 "microchip,tx-lpi-timer",
 | 
					 | 
				
			||||||
+					 &edata.tx_lpi_timer))
 | 
					 | 
				
			||||||
+			edata.tx_lpi_timer = 600; /* non-aggressive */
 | 
					 | 
				
			||||||
+		(void)lan78xx_set_eee(dev->net, &edata);
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	if (phydev->mdio.dev.of_node) {
 | 
					 | 
				
			||||||
 		u32 reg;
 | 
					 | 
				
			||||||
 		int len;
 | 
					 | 
				
			||||||
@@ -2654,22 +2670,6 @@ static int lan78xx_open(struct net_devic
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	netif_dbg(dev, ifup, dev->net, "phy initialised successfully");
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if (of_property_read_bool(dev->udev->dev.of_node,
 | 
					 | 
				
			||||||
-				  "microchip,eee-enabled")) {
 | 
					 | 
				
			||||||
-		struct ethtool_eee edata;
 | 
					 | 
				
			||||||
-		memset(&edata, 0, sizeof(edata));
 | 
					 | 
				
			||||||
-		edata.cmd = ETHTOOL_SEEE;
 | 
					 | 
				
			||||||
-		edata.advertised = ADVERTISED_1000baseT_Full |
 | 
					 | 
				
			||||||
-				   ADVERTISED_100baseT_Full;
 | 
					 | 
				
			||||||
-		edata.eee_enabled = true;
 | 
					 | 
				
			||||||
-		edata.tx_lpi_enabled = true;
 | 
					 | 
				
			||||||
-		if (of_property_read_u32(dev->udev->dev.of_node,
 | 
					 | 
				
			||||||
-					 "microchip,tx-lpi-timer",
 | 
					 | 
				
			||||||
-					 &edata.tx_lpi_timer))
 | 
					 | 
				
			||||||
-			edata.tx_lpi_timer = 600; /* non-aggressive */
 | 
					 | 
				
			||||||
-		(void)lan78xx_set_eee(net, &edata);
 | 
					 | 
				
			||||||
-	}
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
 	/* for Link Check */
 | 
					 | 
				
			||||||
 	if (dev->urb_intr) {
 | 
					 | 
				
			||||||
 		ret = usb_submit_urb(dev->urb_intr, GFP_KERNEL);
 | 
					 | 
				
			||||||
@ -1,581 +0,0 @@
 | 
				
			|||||||
From 079bce6210d997fda44d6143b4b394173360bef1 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Serge Schneider <serge@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Mon, 9 Jul 2018 12:54:25 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] Add rpi-poe-fan driver
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Serge Schneider <serge@raspberrypi.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
PoE HAT driver cleanup
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
* Fix undeclared variable in rpi_poe_fan_suspend
 | 
					 | 
				
			||||||
* Add SPDX-License-Identifier
 | 
					 | 
				
			||||||
* Expand PoE acronym in Kconfig help
 | 
					 | 
				
			||||||
* Give clearer error message on of_property_count_u32_elems fail
 | 
					 | 
				
			||||||
* Add documentation
 | 
					 | 
				
			||||||
* Add vendor to of_device_id compatible string.
 | 
					 | 
				
			||||||
* Rename m_data_s struct to fw_data_s
 | 
					 | 
				
			||||||
* Fix typos
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Fixes: #2665
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Serge Schneider <serge@raspberrypi.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
rpi-poe-fan: fix def_pwm1 writes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Serge Schneider <serge@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 .../devicetree/bindings/hwmon/rpi-poe-fan.txt |  55 +++
 | 
					 | 
				
			||||||
 Documentation/hwmon/rpi-poe-fan               |  15 +
 | 
					 | 
				
			||||||
 drivers/hwmon/Kconfig                         |  11 +
 | 
					 | 
				
			||||||
 drivers/hwmon/Makefile                        |   1 +
 | 
					 | 
				
			||||||
 drivers/hwmon/rpi-poe-fan.c                   | 436 ++++++++++++++++++
 | 
					 | 
				
			||||||
 5 files changed, 518 insertions(+)
 | 
					 | 
				
			||||||
 create mode 100644 Documentation/devicetree/bindings/hwmon/rpi-poe-fan.txt
 | 
					 | 
				
			||||||
 create mode 100644 Documentation/hwmon/rpi-poe-fan
 | 
					 | 
				
			||||||
 create mode 100644 drivers/hwmon/rpi-poe-fan.c
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/Documentation/devicetree/bindings/hwmon/rpi-poe-fan.txt
 | 
					 | 
				
			||||||
@@ -0,0 +1,55 @@
 | 
					 | 
				
			||||||
+Bindings for the Raspberry Pi PoE HAT fan
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+Required properties:
 | 
					 | 
				
			||||||
+- compatible	: "raspberrypi,rpi-poe-fan"
 | 
					 | 
				
			||||||
+- firmware	: Reference to the RPi firmware device node
 | 
					 | 
				
			||||||
+- pwms		: the PWM that is used to control the PWM fan
 | 
					 | 
				
			||||||
+- cooling-levels      : PWM duty cycle values in a range from 0 to 255
 | 
					 | 
				
			||||||
+			which correspond to thermal cooling states
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+Example:
 | 
					 | 
				
			||||||
+	fan0: rpi-poe-fan@0 {
 | 
					 | 
				
			||||||
+		compatible = "raspberrypi,rpi-poe-fan";
 | 
					 | 
				
			||||||
+		firmware = <&firmware>;
 | 
					 | 
				
			||||||
+		cooling-min-state = <0>;
 | 
					 | 
				
			||||||
+		cooling-max-state = <3>;
 | 
					 | 
				
			||||||
+		#cooling-cells = <2>;
 | 
					 | 
				
			||||||
+		cooling-levels = <0 50 150 255>;
 | 
					 | 
				
			||||||
+		status = "okay";
 | 
					 | 
				
			||||||
+	};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	thermal-zones {
 | 
					 | 
				
			||||||
+		cpu_thermal: cpu-thermal {
 | 
					 | 
				
			||||||
+			trips {
 | 
					 | 
				
			||||||
+				threshold: trip-point@0 {
 | 
					 | 
				
			||||||
+					temperature = <45000>;
 | 
					 | 
				
			||||||
+					hysteresis = <5000>;
 | 
					 | 
				
			||||||
+					type = "active";
 | 
					 | 
				
			||||||
+				};
 | 
					 | 
				
			||||||
+				target: trip-point@1 {
 | 
					 | 
				
			||||||
+					temperature = <50000>;
 | 
					 | 
				
			||||||
+					hysteresis = <2000>;
 | 
					 | 
				
			||||||
+					type = "active";
 | 
					 | 
				
			||||||
+				};
 | 
					 | 
				
			||||||
+				cpu_hot: cpu_hot@0 {
 | 
					 | 
				
			||||||
+					temperature = <55000>;
 | 
					 | 
				
			||||||
+					hysteresis = <2000>;
 | 
					 | 
				
			||||||
+					type = "active";
 | 
					 | 
				
			||||||
+				};
 | 
					 | 
				
			||||||
+			};
 | 
					 | 
				
			||||||
+			cooling-maps {
 | 
					 | 
				
			||||||
+				map0 {
 | 
					 | 
				
			||||||
+					trip = <&threshold>;
 | 
					 | 
				
			||||||
+					cooling-device = <&fan0 0 1>;
 | 
					 | 
				
			||||||
+				};
 | 
					 | 
				
			||||||
+				map1 {
 | 
					 | 
				
			||||||
+					trip = <&target>;
 | 
					 | 
				
			||||||
+					cooling-device = <&fan0 1 2>;
 | 
					 | 
				
			||||||
+				};
 | 
					 | 
				
			||||||
+				map2 {
 | 
					 | 
				
			||||||
+					trip = <&cpu_hot>;
 | 
					 | 
				
			||||||
+					cooling-device = <&fan0 2 3>;
 | 
					 | 
				
			||||||
+				};
 | 
					 | 
				
			||||||
+			};
 | 
					 | 
				
			||||||
+		};
 | 
					 | 
				
			||||||
+	};
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/Documentation/hwmon/rpi-poe-fan
 | 
					 | 
				
			||||||
@@ -0,0 +1,15 @@
 | 
					 | 
				
			||||||
+Kernel driver rpi-poe-fan
 | 
					 | 
				
			||||||
+=====================
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+This driver enables the use of the Raspberry Pi PoE HAT fan.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+Author: Serge Schneider <serge@raspberrypi.org>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+Description
 | 
					 | 
				
			||||||
+-----------
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+The driver implements a simple interface for driving the Raspberry Pi PoE
 | 
					 | 
				
			||||||
+(Power over Ethernet) HAT fan. The driver passes commands to the Raspberry Pi
 | 
					 | 
				
			||||||
+firmware through the mailbox property interface. The firmware then forwards
 | 
					 | 
				
			||||||
+the commands to the board over I2C on the ID_EEPROM pins. The driver exposes
 | 
					 | 
				
			||||||
+the fan to the user space through the hwmon sysfs interface.
 | 
					 | 
				
			||||||
--- a/drivers/hwmon/Kconfig
 | 
					 | 
				
			||||||
+++ b/drivers/hwmon/Kconfig
 | 
					 | 
				
			||||||
@@ -1489,6 +1489,17 @@ config SENSORS_RASPBERRYPI_HWMON
 | 
					 | 
				
			||||||
 	  This driver can also be built as a module. If so, the module
 | 
					 | 
				
			||||||
 	  will be called raspberrypi-hwmon.
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+config SENSORS_RPI_POE_FAN
 | 
					 | 
				
			||||||
+	tristate "Raspberry Pi PoE HAT fan"
 | 
					 | 
				
			||||||
+	depends on RASPBERRYPI_FIRMWARE
 | 
					 | 
				
			||||||
+	depends on THERMAL || THERMAL=n
 | 
					 | 
				
			||||||
+	help
 | 
					 | 
				
			||||||
+	  If you say yes here you get support for Raspberry Pi PoE (Power over
 | 
					 | 
				
			||||||
+	  Ethernet) HAT fan.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	  This driver can also be built as a module.  If so, the module
 | 
					 | 
				
			||||||
+	  will be called rpi-poe-fan.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 config SENSORS_SL28CPLD
 | 
					 | 
				
			||||||
 	tristate "Kontron sl28cpld hardware monitoring driver"
 | 
					 | 
				
			||||||
 	depends on MFD_SL28CPLD || COMPILE_TEST
 | 
					 | 
				
			||||||
--- a/drivers/hwmon/Makefile
 | 
					 | 
				
			||||||
+++ b/drivers/hwmon/Makefile
 | 
					 | 
				
			||||||
@@ -157,6 +157,7 @@ obj-$(CONFIG_SENSORS_PCF8591)	+= pcf8591
 | 
					 | 
				
			||||||
 obj-$(CONFIG_SENSORS_POWR1220)  += powr1220.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_SENSORS_PWM_FAN)	+= pwm-fan.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON)	+= raspberrypi-hwmon.o
 | 
					 | 
				
			||||||
+obj-$(CONFIG_SENSORS_RPI_POE_FAN)	+= rpi-poe-fan.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_SENSORS_S3C)	+= s3c-hwmon.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_SENSORS_SCH5627)	+= sch5627.o
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/drivers/hwmon/rpi-poe-fan.c
 | 
					 | 
				
			||||||
@@ -0,0 +1,436 @@
 | 
					 | 
				
			||||||
+// SPDX-License-Identifier: GPL-2.0
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ * rpi-poe-fan.c - Hwmon driver for Raspberry Pi PoE HAT fan.
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Copyright (C) 2018 Raspberry Pi (Trading) Ltd.
 | 
					 | 
				
			||||||
+ * Based on pwm-fan.c by Kamil Debski <k.debski@samsung.com>
 | 
					 | 
				
			||||||
+ *
 | 
					 | 
				
			||||||
+ * Author: Serge Schneider <serge@raspberrypi.org>
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#include <linux/hwmon.h>
 | 
					 | 
				
			||||||
+#include <linux/hwmon-sysfs.h>
 | 
					 | 
				
			||||||
+#include <linux/module.h>
 | 
					 | 
				
			||||||
+#include <linux/mutex.h>
 | 
					 | 
				
			||||||
+#include <linux/notifier.h>
 | 
					 | 
				
			||||||
+#include <linux/of.h>
 | 
					 | 
				
			||||||
+#include <linux/platform_device.h>
 | 
					 | 
				
			||||||
+#include <linux/reboot.h>
 | 
					 | 
				
			||||||
+#include <linux/sysfs.h>
 | 
					 | 
				
			||||||
+#include <linux/thermal.h>
 | 
					 | 
				
			||||||
+#include <soc/bcm2835/raspberrypi-firmware.h>
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define MAX_PWM 255
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define POE_CUR_PWM 0x0
 | 
					 | 
				
			||||||
+#define POE_DEF_PWM 0x1
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+struct rpi_poe_fan_ctx {
 | 
					 | 
				
			||||||
+	struct mutex lock;
 | 
					 | 
				
			||||||
+	struct rpi_firmware *fw;
 | 
					 | 
				
			||||||
+	unsigned int pwm_value;
 | 
					 | 
				
			||||||
+	unsigned int def_pwm_value;
 | 
					 | 
				
			||||||
+	unsigned int rpi_poe_fan_state;
 | 
					 | 
				
			||||||
+	unsigned int rpi_poe_fan_max_state;
 | 
					 | 
				
			||||||
+	unsigned int *rpi_poe_fan_cooling_levels;
 | 
					 | 
				
			||||||
+	struct thermal_cooling_device *cdev;
 | 
					 | 
				
			||||||
+	struct notifier_block nb;
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+struct fw_tag_data_s{
 | 
					 | 
				
			||||||
+	u32 reg;
 | 
					 | 
				
			||||||
+	u32 val;
 | 
					 | 
				
			||||||
+	u32 ret;
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int write_reg(struct rpi_firmware *fw, u32 reg, u32 *val){
 | 
					 | 
				
			||||||
+	struct fw_tag_data_s fw_tag_data = {
 | 
					 | 
				
			||||||
+		.reg = reg,
 | 
					 | 
				
			||||||
+		.val = *val
 | 
					 | 
				
			||||||
+	};
 | 
					 | 
				
			||||||
+	int ret;
 | 
					 | 
				
			||||||
+	ret = rpi_firmware_property(fw, RPI_FIRMWARE_SET_POE_HAT_VAL,
 | 
					 | 
				
			||||||
+				    &fw_tag_data, sizeof(fw_tag_data));
 | 
					 | 
				
			||||||
+	if (ret) {
 | 
					 | 
				
			||||||
+		return ret;
 | 
					 | 
				
			||||||
+	} else if (fw_tag_data.ret) {
 | 
					 | 
				
			||||||
+		return -EIO;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int read_reg(struct rpi_firmware *fw, u32 reg, u32 *val){
 | 
					 | 
				
			||||||
+	struct fw_tag_data_s fw_tag_data = {
 | 
					 | 
				
			||||||
+		.reg = reg,
 | 
					 | 
				
			||||||
+	};
 | 
					 | 
				
			||||||
+	int ret;
 | 
					 | 
				
			||||||
+	ret = rpi_firmware_property(fw, RPI_FIRMWARE_GET_POE_HAT_VAL,
 | 
					 | 
				
			||||||
+				    &fw_tag_data, sizeof(fw_tag_data));
 | 
					 | 
				
			||||||
+	if (ret) {
 | 
					 | 
				
			||||||
+		return ret;
 | 
					 | 
				
			||||||
+	} else if (fw_tag_data.ret) {
 | 
					 | 
				
			||||||
+		return -EIO;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	*val = fw_tag_data.val;
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int rpi_poe_reboot(struct notifier_block *nb, unsigned long code,
 | 
					 | 
				
			||||||
+			  void *unused)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct rpi_poe_fan_ctx *ctx = container_of(nb, struct rpi_poe_fan_ctx,
 | 
					 | 
				
			||||||
+						   nb);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (ctx->pwm_value != ctx->def_pwm_value)
 | 
					 | 
				
			||||||
+		write_reg(ctx->fw, POE_CUR_PWM, &ctx->def_pwm_value);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return NOTIFY_DONE;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int  __set_pwm(struct rpi_poe_fan_ctx *ctx, u32 pwm)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int ret = 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	mutex_lock(&ctx->lock);
 | 
					 | 
				
			||||||
+	if (ctx->pwm_value == pwm)
 | 
					 | 
				
			||||||
+		goto exit_set_pwm_err;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ret = write_reg(ctx->fw, POE_CUR_PWM, &pwm);
 | 
					 | 
				
			||||||
+	if (!ret)
 | 
					 | 
				
			||||||
+		ctx->pwm_value = pwm;
 | 
					 | 
				
			||||||
+exit_set_pwm_err:
 | 
					 | 
				
			||||||
+	mutex_unlock(&ctx->lock);
 | 
					 | 
				
			||||||
+	return ret;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int  __set_def_pwm(struct rpi_poe_fan_ctx *ctx, u32 def_pwm)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int ret = 0;
 | 
					 | 
				
			||||||
+	mutex_lock(&ctx->lock);
 | 
					 | 
				
			||||||
+	if (ctx->def_pwm_value == def_pwm)
 | 
					 | 
				
			||||||
+		goto exit_set_def_pwm_err;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ret = write_reg(ctx->fw, POE_DEF_PWM, &def_pwm);
 | 
					 | 
				
			||||||
+	if (!ret)
 | 
					 | 
				
			||||||
+		ctx->def_pwm_value = def_pwm;
 | 
					 | 
				
			||||||
+exit_set_def_pwm_err:
 | 
					 | 
				
			||||||
+	mutex_unlock(&ctx->lock);
 | 
					 | 
				
			||||||
+	return ret;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void rpi_poe_fan_update_state(struct rpi_poe_fan_ctx *ctx,
 | 
					 | 
				
			||||||
+				     unsigned long pwm)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	int i;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	for (i = 0; i < ctx->rpi_poe_fan_max_state; ++i)
 | 
					 | 
				
			||||||
+		if (pwm < ctx->rpi_poe_fan_cooling_levels[i + 1])
 | 
					 | 
				
			||||||
+			break;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ctx->rpi_poe_fan_state = i;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
 | 
					 | 
				
			||||||
+		       const char *buf, size_t count)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct rpi_poe_fan_ctx *ctx = dev_get_drvdata(dev);
 | 
					 | 
				
			||||||
+	unsigned long pwm;
 | 
					 | 
				
			||||||
+	int ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM)
 | 
					 | 
				
			||||||
+		return -EINVAL;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ret = __set_pwm(ctx, pwm);
 | 
					 | 
				
			||||||
+	if (ret)
 | 
					 | 
				
			||||||
+		return ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	rpi_poe_fan_update_state(ctx, pwm);
 | 
					 | 
				
			||||||
+	return count;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static ssize_t set_def_pwm(struct device *dev, struct device_attribute *attr,
 | 
					 | 
				
			||||||
+			   const char *buf, size_t count)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct rpi_poe_fan_ctx *ctx = dev_get_drvdata(dev);
 | 
					 | 
				
			||||||
+	unsigned long def_pwm;
 | 
					 | 
				
			||||||
+	int ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (kstrtoul(buf, 10, &def_pwm) || def_pwm > MAX_PWM)
 | 
					 | 
				
			||||||
+		return -EINVAL;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ret = __set_def_pwm(ctx, def_pwm);
 | 
					 | 
				
			||||||
+	if (ret)
 | 
					 | 
				
			||||||
+		return ret;
 | 
					 | 
				
			||||||
+	return count;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static ssize_t show_pwm(struct device *dev,
 | 
					 | 
				
			||||||
+			struct device_attribute *attr, char *buf)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct rpi_poe_fan_ctx *ctx = dev_get_drvdata(dev);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return sprintf(buf, "%u\n", ctx->pwm_value);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static ssize_t show_def_pwm(struct device *dev,
 | 
					 | 
				
			||||||
+			struct device_attribute *attr, char *buf)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct rpi_poe_fan_ctx *ctx = dev_get_drvdata(dev);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return sprintf(buf, "%u\n", ctx->def_pwm_value);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static SENSOR_DEVICE_ATTR(pwm1, 0644, show_pwm, set_pwm, 0);
 | 
					 | 
				
			||||||
+static SENSOR_DEVICE_ATTR(def_pwm1, 0644, show_def_pwm, set_def_pwm, 1);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct attribute *rpi_poe_fan_attrs[] = {
 | 
					 | 
				
			||||||
+	&sensor_dev_attr_pwm1.dev_attr.attr,
 | 
					 | 
				
			||||||
+	&sensor_dev_attr_def_pwm1.dev_attr.attr,
 | 
					 | 
				
			||||||
+	NULL,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+ATTRIBUTE_GROUPS(rpi_poe_fan);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+/* thermal cooling device callbacks */
 | 
					 | 
				
			||||||
+static int rpi_poe_fan_get_max_state(struct thermal_cooling_device *cdev,
 | 
					 | 
				
			||||||
+				     unsigned long *state)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct rpi_poe_fan_ctx *ctx = cdev->devdata;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (!ctx)
 | 
					 | 
				
			||||||
+		return -EINVAL;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	*state = ctx->rpi_poe_fan_max_state;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int rpi_poe_fan_get_cur_state(struct thermal_cooling_device *cdev,
 | 
					 | 
				
			||||||
+				     unsigned long *state)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct rpi_poe_fan_ctx *ctx = cdev->devdata;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (!ctx)
 | 
					 | 
				
			||||||
+		return -EINVAL;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	*state = ctx->rpi_poe_fan_state;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int rpi_poe_fan_set_cur_state(struct thermal_cooling_device *cdev,
 | 
					 | 
				
			||||||
+				     unsigned long state)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct rpi_poe_fan_ctx *ctx = cdev->devdata;
 | 
					 | 
				
			||||||
+	int ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (!ctx || (state > ctx->rpi_poe_fan_max_state))
 | 
					 | 
				
			||||||
+		return -EINVAL;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (state == ctx->rpi_poe_fan_state)
 | 
					 | 
				
			||||||
+		return 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ret = __set_pwm(ctx, ctx->rpi_poe_fan_cooling_levels[state]);
 | 
					 | 
				
			||||||
+	if (ret) {
 | 
					 | 
				
			||||||
+		dev_err(&cdev->device, "Cannot set pwm!\n");
 | 
					 | 
				
			||||||
+		return ret;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ctx->rpi_poe_fan_state = state;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return ret;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static const struct thermal_cooling_device_ops rpi_poe_fan_cooling_ops = {
 | 
					 | 
				
			||||||
+	.get_max_state = rpi_poe_fan_get_max_state,
 | 
					 | 
				
			||||||
+	.get_cur_state = rpi_poe_fan_get_cur_state,
 | 
					 | 
				
			||||||
+	.set_cur_state = rpi_poe_fan_set_cur_state,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int rpi_poe_fan_of_get_cooling_data(struct device *dev,
 | 
					 | 
				
			||||||
+				       struct rpi_poe_fan_ctx *ctx)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct device_node *np = dev->of_node;
 | 
					 | 
				
			||||||
+	int num, i, ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (!of_find_property(np, "cooling-levels", NULL))
 | 
					 | 
				
			||||||
+		return 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ret = of_property_count_u32_elems(np, "cooling-levels");
 | 
					 | 
				
			||||||
+	if (ret <= 0) {
 | 
					 | 
				
			||||||
+		dev_err(dev, "cooling-levels property missing or invalid: %d\n",
 | 
					 | 
				
			||||||
+			ret);
 | 
					 | 
				
			||||||
+		return ret ? : -EINVAL;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	num = ret;
 | 
					 | 
				
			||||||
+	ctx->rpi_poe_fan_cooling_levels = devm_kzalloc(dev, num * sizeof(u32),
 | 
					 | 
				
			||||||
+						   GFP_KERNEL);
 | 
					 | 
				
			||||||
+	if (!ctx->rpi_poe_fan_cooling_levels)
 | 
					 | 
				
			||||||
+		return -ENOMEM;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ret = of_property_read_u32_array(np, "cooling-levels",
 | 
					 | 
				
			||||||
+					 ctx->rpi_poe_fan_cooling_levels, num);
 | 
					 | 
				
			||||||
+	if (ret) {
 | 
					 | 
				
			||||||
+		dev_err(dev, "Property 'cooling-levels' cannot be read!\n");
 | 
					 | 
				
			||||||
+		return ret;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	for (i = 0; i < num; i++) {
 | 
					 | 
				
			||||||
+		if (ctx->rpi_poe_fan_cooling_levels[i] > MAX_PWM) {
 | 
					 | 
				
			||||||
+			dev_err(dev, "PWM fan state[%d]:%d > %d\n", i,
 | 
					 | 
				
			||||||
+				ctx->rpi_poe_fan_cooling_levels[i], MAX_PWM);
 | 
					 | 
				
			||||||
+			return -EINVAL;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ctx->rpi_poe_fan_max_state = num - 1;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int rpi_poe_fan_probe(struct platform_device *pdev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct thermal_cooling_device *cdev;
 | 
					 | 
				
			||||||
+	struct rpi_poe_fan_ctx *ctx;
 | 
					 | 
				
			||||||
+	struct device *hwmon;
 | 
					 | 
				
			||||||
+	struct device_node *np = pdev->dev.of_node;
 | 
					 | 
				
			||||||
+	struct device_node *fw_node;
 | 
					 | 
				
			||||||
+	int ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	fw_node = of_parse_phandle(np, "firmware", 0);
 | 
					 | 
				
			||||||
+	if (!fw_node) {
 | 
					 | 
				
			||||||
+		dev_err(&pdev->dev, "Missing firmware node\n");
 | 
					 | 
				
			||||||
+		return -ENOENT;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
 | 
					 | 
				
			||||||
+	if (!ctx)
 | 
					 | 
				
			||||||
+		return -ENOMEM;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	mutex_init(&ctx->lock);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ctx->fw = rpi_firmware_get(fw_node);
 | 
					 | 
				
			||||||
+	if (!ctx->fw)
 | 
					 | 
				
			||||||
+		return -EPROBE_DEFER;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	platform_set_drvdata(pdev, ctx);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ctx->nb.notifier_call = rpi_poe_reboot;
 | 
					 | 
				
			||||||
+	ret = register_reboot_notifier(&ctx->nb);
 | 
					 | 
				
			||||||
+	if (ret) {
 | 
					 | 
				
			||||||
+		dev_err(&pdev->dev, "Failed to register reboot notifier: %i\n",
 | 
					 | 
				
			||||||
+			ret);
 | 
					 | 
				
			||||||
+		return ret;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	ret = read_reg(ctx->fw, POE_DEF_PWM, &ctx->def_pwm_value);
 | 
					 | 
				
			||||||
+	if (ret) {
 | 
					 | 
				
			||||||
+		dev_err(&pdev->dev, "Failed to get default PWM value: %i\n",
 | 
					 | 
				
			||||||
+			ret);
 | 
					 | 
				
			||||||
+		goto err;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	ret = read_reg(ctx->fw, POE_CUR_PWM, &ctx->pwm_value);
 | 
					 | 
				
			||||||
+	if (ret) {
 | 
					 | 
				
			||||||
+		dev_err(&pdev->dev, "Failed to get current PWM value: %i\n",
 | 
					 | 
				
			||||||
+			ret);
 | 
					 | 
				
			||||||
+		goto err;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	hwmon = devm_hwmon_device_register_with_groups(&pdev->dev, "rpipoefan",
 | 
					 | 
				
			||||||
+						       ctx, rpi_poe_fan_groups);
 | 
					 | 
				
			||||||
+	if (IS_ERR(hwmon)) {
 | 
					 | 
				
			||||||
+		dev_err(&pdev->dev, "Failed to register hwmon device\n");
 | 
					 | 
				
			||||||
+		ret = PTR_ERR(hwmon);
 | 
					 | 
				
			||||||
+		goto err;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ret = rpi_poe_fan_of_get_cooling_data(&pdev->dev, ctx);
 | 
					 | 
				
			||||||
+	if (ret)
 | 
					 | 
				
			||||||
+		return ret;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	rpi_poe_fan_update_state(ctx, ctx->pwm_value);
 | 
					 | 
				
			||||||
+	if (!IS_ENABLED(CONFIG_THERMAL))
 | 
					 | 
				
			||||||
+		return 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	cdev = thermal_of_cooling_device_register(np,
 | 
					 | 
				
			||||||
+						  "rpi-poe-fan", ctx,
 | 
					 | 
				
			||||||
+						  &rpi_poe_fan_cooling_ops);
 | 
					 | 
				
			||||||
+	if (IS_ERR(cdev)) {
 | 
					 | 
				
			||||||
+		dev_err(&pdev->dev,
 | 
					 | 
				
			||||||
+			"Failed to register rpi-poe-fan as cooling device");
 | 
					 | 
				
			||||||
+		ret = PTR_ERR(cdev);
 | 
					 | 
				
			||||||
+		goto err;
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	ctx->cdev = cdev;
 | 
					 | 
				
			||||||
+	thermal_cdev_update(cdev);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+err:
 | 
					 | 
				
			||||||
+	unregister_reboot_notifier(&ctx->nb);
 | 
					 | 
				
			||||||
+	return ret;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int rpi_poe_fan_remove(struct platform_device *pdev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct rpi_poe_fan_ctx *ctx = platform_get_drvdata(pdev);
 | 
					 | 
				
			||||||
+	u32 value = ctx->def_pwm_value;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	unregister_reboot_notifier(&ctx->nb);
 | 
					 | 
				
			||||||
+	thermal_cooling_device_unregister(ctx->cdev);
 | 
					 | 
				
			||||||
+	if (ctx->pwm_value != value) {
 | 
					 | 
				
			||||||
+		write_reg(ctx->fw, POE_CUR_PWM, &value);
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+	return 0;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#ifdef CONFIG_PM_SLEEP
 | 
					 | 
				
			||||||
+static int rpi_poe_fan_suspend(struct device *dev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct rpi_poe_fan_ctx *ctx = dev_get_drvdata(dev);
 | 
					 | 
				
			||||||
+	u32 value = 0;
 | 
					 | 
				
			||||||
+	int ret = 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (ctx->pwm_value != value)
 | 
					 | 
				
			||||||
+		ret = write_reg(ctx->fw, POE_CUR_PWM, &value);
 | 
					 | 
				
			||||||
+	return ret;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static int rpi_poe_fan_resume(struct device *dev)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct rpi_poe_fan_ctx *ctx = dev_get_drvdata(dev);
 | 
					 | 
				
			||||||
+	u32 value = ctx->pwm_value;
 | 
					 | 
				
			||||||
+	int ret = 0;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (value != 0)
 | 
					 | 
				
			||||||
+		ret = write_reg(ctx->fw, POE_CUR_PWM, &value);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return ret;
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static SIMPLE_DEV_PM_OPS(rpi_poe_fan_pm, rpi_poe_fan_suspend,
 | 
					 | 
				
			||||||
+			 rpi_poe_fan_resume);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static const struct of_device_id of_rpi_poe_fan_match[] = {
 | 
					 | 
				
			||||||
+	{ .compatible = "raspberrypi,rpi-poe-fan", },
 | 
					 | 
				
			||||||
+	{},
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+MODULE_DEVICE_TABLE(of, of_rpi_poe_fan_match);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct platform_driver rpi_poe_fan_driver = {
 | 
					 | 
				
			||||||
+	.probe		= rpi_poe_fan_probe,
 | 
					 | 
				
			||||||
+	.remove		= rpi_poe_fan_remove,
 | 
					 | 
				
			||||||
+	.driver	= {
 | 
					 | 
				
			||||||
+		.name		= "rpi-poe-fan",
 | 
					 | 
				
			||||||
+		.pm		= &rpi_poe_fan_pm,
 | 
					 | 
				
			||||||
+		.of_match_table	= of_rpi_poe_fan_match,
 | 
					 | 
				
			||||||
+	},
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+module_platform_driver(rpi_poe_fan_driver);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
 | 
					 | 
				
			||||||
+MODULE_ALIAS("platform:rpi-poe-fan");
 | 
					 | 
				
			||||||
+MODULE_DESCRIPTION("Raspberry Pi PoE HAT fan driver");
 | 
					 | 
				
			||||||
+MODULE_LICENSE("GPL");
 | 
					 | 
				
			||||||
@ -1,20 +0,0 @@
 | 
				
			|||||||
From 5156d25449f1a76a72be090a595ee7e25cb74bc6 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: popcornmix <popcornmix@gmail.com>
 | 
					 | 
				
			||||||
Date: Mon, 17 Sep 2018 17:31:18 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] cxd2880: CXD2880_SPI_DRV should select DVB_CXD2880
 | 
					 | 
				
			||||||
 with MEDIA_SUBDRV_AUTOSELECT
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/media/spi/Kconfig | 1 +
 | 
					 | 
				
			||||||
 1 file changed, 1 insertion(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/media/spi/Kconfig
 | 
					 | 
				
			||||||
+++ b/drivers/media/spi/Kconfig
 | 
					 | 
				
			||||||
@@ -25,6 +25,7 @@ menu "Media SPI Adapters"
 | 
					 | 
				
			||||||
 config CXD2880_SPI_DRV
 | 
					 | 
				
			||||||
 	tristate "Sony CXD2880 SPI support"
 | 
					 | 
				
			||||||
 	depends on DVB_CORE && SPI
 | 
					 | 
				
			||||||
+	select DVB_CXD2880 if MEDIA_SUBDRV_AUTOSELECT
 | 
					 | 
				
			||||||
 	default m if !MEDIA_SUBDRV_AUTOSELECT
 | 
					 | 
				
			||||||
 	help
 | 
					 | 
				
			||||||
 	  Choose if you would like to have SPI interface support for Sony CXD2880.
 | 
					 | 
				
			||||||
@ -1,75 +0,0 @@
 | 
				
			|||||||
From f6e40957774cd71b4a8ce2190d94ef8e9b289938 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Stefan Wahren <stefan.wahren@i2se.com>
 | 
					 | 
				
			||||||
Date: Sat, 6 Oct 2018 16:46:18 +0200
 | 
					 | 
				
			||||||
Subject: [PATCH] hwmon: raspberrypi: Prevent voltage low warnings from
 | 
					 | 
				
			||||||
 filling log
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Although the correct fix for low voltage warnings is to
 | 
					 | 
				
			||||||
improve the power supply, the current implementation
 | 
					 | 
				
			||||||
of the detection can fill the log if the warning
 | 
					 | 
				
			||||||
happens freqently. This replaces the logging with
 | 
					 | 
				
			||||||
slightly custom ratelimited logging.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: James Hughes <james.hughes@raspberrypi.org>
 | 
					 | 
				
			||||||
Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/hwmon/raspberrypi-hwmon.c | 41 ++++++++++++++++++++++++++++---
 | 
					 | 
				
			||||||
 1 file changed, 37 insertions(+), 4 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/hwmon/raspberrypi-hwmon.c
 | 
					 | 
				
			||||||
+++ b/drivers/hwmon/raspberrypi-hwmon.c
 | 
					 | 
				
			||||||
@@ -15,6 +15,36 @@
 | 
					 | 
				
			||||||
 #include <linux/workqueue.h>
 | 
					 | 
				
			||||||
 #include <soc/bcm2835/raspberrypi-firmware.h>
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+/*
 | 
					 | 
				
			||||||
+ * This section defines some rate limited logging that prevent
 | 
					 | 
				
			||||||
+ * repeated messages at much lower Hz than the default kernel settings.
 | 
					 | 
				
			||||||
+ * It's usually 5s, this is 5 minutes.
 | 
					 | 
				
			||||||
+ * Burst 3 means you may get three messages 'quickly', before
 | 
					 | 
				
			||||||
+ * the ratelimiting kicks in.
 | 
					 | 
				
			||||||
+ */
 | 
					 | 
				
			||||||
+#define LOCAL_RATELIMIT_INTERVAL (5 * 60 * HZ)
 | 
					 | 
				
			||||||
+#define LOCAL_RATELIMIT_BURST 3
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#ifdef CONFIG_PRINTK
 | 
					 | 
				
			||||||
+#define printk_ratelimited_local(fmt, ...)	\
 | 
					 | 
				
			||||||
+({						\
 | 
					 | 
				
			||||||
+	static DEFINE_RATELIMIT_STATE(_rs,	\
 | 
					 | 
				
			||||||
+		LOCAL_RATELIMIT_INTERVAL,	\
 | 
					 | 
				
			||||||
+		LOCAL_RATELIMIT_BURST);		\
 | 
					 | 
				
			||||||
+						\
 | 
					 | 
				
			||||||
+	if (__ratelimit(&_rs))			\
 | 
					 | 
				
			||||||
+		printk(fmt, ##__VA_ARGS__);	\
 | 
					 | 
				
			||||||
+})
 | 
					 | 
				
			||||||
+#else
 | 
					 | 
				
			||||||
+#define printk_ratelimited_local(fmt, ...)	\
 | 
					 | 
				
			||||||
+	no_printk(fmt, ##__VA_ARGS__)
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+#define pr_crit_ratelimited_local(fmt, ...)              \
 | 
					 | 
				
			||||||
+	printk_ratelimited_local(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)
 | 
					 | 
				
			||||||
+#define pr_info_ratelimited_local(fmt, ...)              \
 | 
					 | 
				
			||||||
+printk_ratelimited_local(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 #define UNDERVOLTAGE_STICKY_BIT	BIT(16)
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 struct rpi_hwmon_data {
 | 
					 | 
				
			||||||
@@ -47,10 +77,13 @@ static void rpi_firmware_get_throttled(s
 | 
					 | 
				
			||||||
 	if (new_uv == old_uv)
 | 
					 | 
				
			||||||
 		return;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if (new_uv)
 | 
					 | 
				
			||||||
-		dev_crit(data->hwmon_dev, "Undervoltage detected!\n");
 | 
					 | 
				
			||||||
-	else
 | 
					 | 
				
			||||||
-		dev_info(data->hwmon_dev, "Voltage normalised\n");
 | 
					 | 
				
			||||||
+	if (new_uv) {
 | 
					 | 
				
			||||||
+		pr_crit_ratelimited_local("Under-voltage detected! (0x%08x)\n",
 | 
					 | 
				
			||||||
+					  value);
 | 
					 | 
				
			||||||
+	} else {
 | 
					 | 
				
			||||||
+		pr_info_ratelimited_local("Voltage normalised (0x%08x)\n",
 | 
					 | 
				
			||||||
+					  value);
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	sysfs_notify(&data->hwmon_dev->kobj, NULL, "in0_lcrit_alarm");
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
@ -1,79 +0,0 @@
 | 
				
			|||||||
From 1910584831fe065c50efc3c07b8ea08ff90e6f31 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Stefan Wahren <stefan.wahren@i2se.com>
 | 
					 | 
				
			||||||
Date: Sat, 13 Oct 2018 13:31:21 +0200
 | 
					 | 
				
			||||||
Subject: [PATCH] firmware: raspberrypi: Add backward compatible
 | 
					 | 
				
			||||||
 get_throttled
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Avoid a hard userspace ABI change by adding a compatible get_throttled
 | 
					 | 
				
			||||||
sysfs entry. Its value is now feed by the GET_THROTTLED requests of the
 | 
					 | 
				
			||||||
new hwmon driver. The first access to get_throttled will generate
 | 
					 | 
				
			||||||
a warning.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/firmware/raspberrypi.c | 33 +++++++++++++++++++++++++++++++++
 | 
					 | 
				
			||||||
 1 file changed, 33 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/firmware/raspberrypi.c
 | 
					 | 
				
			||||||
+++ b/drivers/firmware/raspberrypi.c
 | 
					 | 
				
			||||||
@@ -29,6 +29,7 @@ struct rpi_firmware {
 | 
					 | 
				
			||||||
 	struct mbox_chan *chan; /* The property channel. */
 | 
					 | 
				
			||||||
 	struct completion c;
 | 
					 | 
				
			||||||
 	u32 enabled;
 | 
					 | 
				
			||||||
+	u32 get_throttled;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	struct kref consumers;
 | 
					 | 
				
			||||||
 };
 | 
					 | 
				
			||||||
@@ -177,6 +178,12 @@ int rpi_firmware_property(struct rpi_fir
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	kfree(data);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	if ((tag == RPI_FIRMWARE_GET_THROTTLED) &&
 | 
					 | 
				
			||||||
+	     memcmp(&fw->get_throttled, tag_data, sizeof(fw->get_throttled))) {
 | 
					 | 
				
			||||||
+		memcpy(&fw->get_throttled, tag_data, sizeof(fw->get_throttled));
 | 
					 | 
				
			||||||
+		sysfs_notify(&fw->cl.dev->kobj, NULL, "get_throttled");
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	return ret;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 EXPORT_SYMBOL_GPL(rpi_firmware_property);
 | 
					 | 
				
			||||||
@@ -201,6 +208,27 @@ static int rpi_firmware_notify_reboot(st
 | 
					 | 
				
			||||||
 	return 0;
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+static ssize_t get_throttled_show(struct device *dev,
 | 
					 | 
				
			||||||
+				  struct device_attribute *attr, char *buf)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	struct rpi_firmware *fw = dev_get_drvdata(dev);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	WARN_ONCE(1, "deprecated, use hwmon sysfs instead\n");
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	return sprintf(buf, "%x\n", fw->get_throttled);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static DEVICE_ATTR_RO(get_throttled);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static struct attribute *rpi_firmware_dev_attrs[] = {
 | 
					 | 
				
			||||||
+	&dev_attr_get_throttled.attr,
 | 
					 | 
				
			||||||
+	NULL,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static const struct attribute_group rpi_firmware_dev_group = {
 | 
					 | 
				
			||||||
+	.attrs = rpi_firmware_dev_attrs,
 | 
					 | 
				
			||||||
+};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static void
 | 
					 | 
				
			||||||
 rpi_firmware_print_firmware_revision(struct rpi_firmware *fw)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
@@ -230,6 +258,11 @@ rpi_register_hwmon_driver(struct device
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	rpi_hwmon = platform_device_register_data(dev, "raspberrypi-hwmon",
 | 
					 | 
				
			||||||
 						  -1, NULL, 0);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (!IS_ERR_OR_NULL(rpi_hwmon)) {
 | 
					 | 
				
			||||||
+		if (devm_device_add_group(dev, &rpi_firmware_dev_group))
 | 
					 | 
				
			||||||
+			dev_err(dev, "Failed to create get_trottled attr\n");
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static void rpi_register_clk_driver(struct device *dev)
 | 
					 | 
				
			||||||
@ -1,23 +0,0 @@
 | 
				
			|||||||
From 0041d17ed3cd7e4ffc6168164c6020e5dcf7aeb4 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Tue, 6 Nov 2018 12:57:48 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] sc16is7xx: Don't spin if no data received
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
See: https://github.com/raspberrypi/linux/issues/2676
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/tty/serial/sc16is7xx.c | 2 ++
 | 
					 | 
				
			||||||
 1 file changed, 2 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/tty/serial/sc16is7xx.c
 | 
					 | 
				
			||||||
+++ b/drivers/tty/serial/sc16is7xx.c
 | 
					 | 
				
			||||||
@@ -696,6 +696,8 @@ static bool sc16is7xx_port_irq(struct sc
 | 
					 | 
				
			||||||
 			rxlen = sc16is7xx_port_read(port, SC16IS7XX_RXLVL_REG);
 | 
					 | 
				
			||||||
 			if (rxlen)
 | 
					 | 
				
			||||||
 				sc16is7xx_handle_rx(port, rxlen, iir);
 | 
					 | 
				
			||||||
+			else
 | 
					 | 
				
			||||||
+				return false;
 | 
					 | 
				
			||||||
 			break;
 | 
					 | 
				
			||||||
 		case SC16IS7XX_IIR_THRI_SRC:
 | 
					 | 
				
			||||||
 			sc16is7xx_handle_tx(port);
 | 
					 | 
				
			||||||
@ -1,94 +0,0 @@
 | 
				
			|||||||
From 61e2e6bc4c30ceca12a917de93ffa9645e4944f7 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Ram Chandrasekar <rkumbako@codeaurora.org>
 | 
					 | 
				
			||||||
Date: Mon, 7 May 2018 11:54:08 -0600
 | 
					 | 
				
			||||||
Subject: [PATCH] drivers: thermal: step_wise: add support for
 | 
					 | 
				
			||||||
 hysteresis
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Step wise governor increases the mitigation level when the temperature
 | 
					 | 
				
			||||||
goes above a threshold and will decrease the mitigation when the
 | 
					 | 
				
			||||||
temperature falls below the threshold. If it were a case, where the
 | 
					 | 
				
			||||||
temperature hovers around a threshold, the mitigation will be applied
 | 
					 | 
				
			||||||
and removed at every iteration. This reaction to the temperature is
 | 
					 | 
				
			||||||
inefficient for performance.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The use of hysteresis temperature could avoid this ping-pong of
 | 
					 | 
				
			||||||
mitigation by relaxing the mitigation to happen only when the
 | 
					 | 
				
			||||||
temperature goes below this lower hysteresis value.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Ram Chandrasekar <rkumbako@codeaurora.org>
 | 
					 | 
				
			||||||
Signed-off-by: Lina Iyer <ilina@codeaurora.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/thermal/gov_step_wise.c | 33 +++++++++++++++++++++++----------
 | 
					 | 
				
			||||||
 1 file changed, 23 insertions(+), 10 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/thermal/gov_step_wise.c
 | 
					 | 
				
			||||||
+++ b/drivers/thermal/gov_step_wise.c
 | 
					 | 
				
			||||||
@@ -24,7 +24,7 @@
 | 
					 | 
				
			||||||
  *       for this trip point
 | 
					 | 
				
			||||||
  *    d. if the trend is THERMAL_TREND_DROP_FULL, use lower limit
 | 
					 | 
				
			||||||
  *       for this trip point
 | 
					 | 
				
			||||||
- * If the temperature is lower than a trip point,
 | 
					 | 
				
			||||||
+ * If the temperature is lower than a hysteresis temperature,
 | 
					 | 
				
			||||||
  *    a. if the trend is THERMAL_TREND_RAISING, do nothing
 | 
					 | 
				
			||||||
  *    b. if the trend is THERMAL_TREND_DROPPING, use lower cooling
 | 
					 | 
				
			||||||
  *       state for this trip point, if the cooling state already
 | 
					 | 
				
			||||||
@@ -115,7 +115,7 @@ static void update_passive_instance(stru
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
-	int trip_temp;
 | 
					 | 
				
			||||||
+	int trip_temp, hyst_temp;
 | 
					 | 
				
			||||||
 	enum thermal_trip_type trip_type;
 | 
					 | 
				
			||||||
 	enum thermal_trend trend;
 | 
					 | 
				
			||||||
 	struct thermal_instance *instance;
 | 
					 | 
				
			||||||
@@ -123,22 +123,23 @@ static void thermal_zone_trip_update(str
 | 
					 | 
				
			||||||
 	int old_target;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	if (trip == THERMAL_TRIPS_NONE) {
 | 
					 | 
				
			||||||
-		trip_temp = tz->forced_passive;
 | 
					 | 
				
			||||||
+		hyst_temp = trip_temp = tz->forced_passive;
 | 
					 | 
				
			||||||
 		trip_type = THERMAL_TRIPS_NONE;
 | 
					 | 
				
			||||||
 	} else {
 | 
					 | 
				
			||||||
 		tz->ops->get_trip_temp(tz, trip, &trip_temp);
 | 
					 | 
				
			||||||
+		hyst_temp = trip_temp;
 | 
					 | 
				
			||||||
+		if (tz->ops->get_trip_hyst) {
 | 
					 | 
				
			||||||
+			tz->ops->get_trip_hyst(tz, trip, &hyst_temp);
 | 
					 | 
				
			||||||
+			hyst_temp = trip_temp - hyst_temp;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
 		tz->ops->get_trip_type(tz, trip, &trip_type);
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	trend = get_tz_trend(tz, trip);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if (tz->temperature >= trip_temp) {
 | 
					 | 
				
			||||||
-		throttle = true;
 | 
					 | 
				
			||||||
-		trace_thermal_zone_trip(tz, trip, trip_type);
 | 
					 | 
				
			||||||
-	}
 | 
					 | 
				
			||||||
-
 | 
					 | 
				
			||||||
-	dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d\n",
 | 
					 | 
				
			||||||
-				trip, trip_type, trip_temp, trend, throttle);
 | 
					 | 
				
			||||||
+	dev_dbg(&tz->device,
 | 
					 | 
				
			||||||
+		"Trip%d[type=%d,temp=%d,hyst=%d]:trend=%d,throttle=%d\n",
 | 
					 | 
				
			||||||
+		trip, trip_type, trip_temp, hyst_temp, trend, throttle);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	mutex_lock(&tz->lock);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -147,6 +148,18 @@ static void thermal_zone_trip_update(str
 | 
					 | 
				
			||||||
 			continue;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 		old_target = instance->target;
 | 
					 | 
				
			||||||
+		throttle = false;
 | 
					 | 
				
			||||||
+		/*
 | 
					 | 
				
			||||||
+		 * Lower the mitigation only if the temperature
 | 
					 | 
				
			||||||
+		 * goes below the hysteresis temperature.
 | 
					 | 
				
			||||||
+		 */
 | 
					 | 
				
			||||||
+		if (tz->temperature >= trip_temp ||
 | 
					 | 
				
			||||||
+		   (tz->temperature >= hyst_temp &&
 | 
					 | 
				
			||||||
+		   old_target != THERMAL_NO_TARGET)) {
 | 
					 | 
				
			||||||
+			throttle = true;
 | 
					 | 
				
			||||||
+			trace_thermal_zone_trip(tz, trip, trip_type);
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 		instance->target = get_target_state(instance, trend, throttle);
 | 
					 | 
				
			||||||
 		dev_dbg(&instance->cdev->device, "old_target=%d, target=%d\n",
 | 
					 | 
				
			||||||
 					old_target, (int)instance->target);
 | 
					 | 
				
			||||||
@ -1,22 +0,0 @@
 | 
				
			|||||||
From f220efa380854e2423a47f26acde9355ae016ea3 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Serge Schneider <serge@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Tue, 2 Oct 2018 11:14:15 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] drivers: thermal: step_wise: avoid throttling at
 | 
					 | 
				
			||||||
 hysteresis temperature after dropping below it
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Serge Schneider <serge@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/thermal/gov_step_wise.c | 2 +-
 | 
					 | 
				
			||||||
 1 file changed, 1 insertion(+), 1 deletion(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/thermal/gov_step_wise.c
 | 
					 | 
				
			||||||
+++ b/drivers/thermal/gov_step_wise.c
 | 
					 | 
				
			||||||
@@ -155,7 +155,7 @@ static void thermal_zone_trip_update(str
 | 
					 | 
				
			||||||
 		 */
 | 
					 | 
				
			||||||
 		if (tz->temperature >= trip_temp ||
 | 
					 | 
				
			||||||
 		   (tz->temperature >= hyst_temp &&
 | 
					 | 
				
			||||||
-		   old_target != THERMAL_NO_TARGET)) {
 | 
					 | 
				
			||||||
+		   old_target == instance->upper)) {
 | 
					 | 
				
			||||||
 			throttle = true;
 | 
					 | 
				
			||||||
 			trace_thermal_zone_trip(tz, trip, trip_type);
 | 
					 | 
				
			||||||
 		}
 | 
					 | 
				
			||||||
@ -1,47 +0,0 @@
 | 
				
			|||||||
From 87b9a65d3f7102400bff69dcc692d238fe1d6203 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: James Hughes <JamesH65@users.noreply.github.com>
 | 
					 | 
				
			||||||
Date: Fri, 2 Nov 2018 11:55:49 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] Update issue templates (#2736)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 .github/ISSUE_TEMPLATE/bug_report.md | 34 ++++++++++++++++++++++++++++
 | 
					 | 
				
			||||||
 1 file changed, 34 insertions(+)
 | 
					 | 
				
			||||||
 create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- /dev/null
 | 
					 | 
				
			||||||
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
 | 
					 | 
				
			||||||
@@ -0,0 +1,34 @@
 | 
					 | 
				
			||||||
+---
 | 
					 | 
				
			||||||
+name: Bug report
 | 
					 | 
				
			||||||
+about: Create a report to help us fix your issue
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+---
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+**Is this the right place for my bug report?**
 | 
					 | 
				
			||||||
+This repository contains the Linux kernel used on the Raspberry Pi. If you believe that the issue you are seeing is kernel-related, this is the right place. If not, we have other repositories for the GPU firmware at [github.com/raspberrypi/firmware](https://github.com/raspberrypi/firmware) and Raspberry Pi userland applications at [github.com/raspberrypi/userland](https://github.com/raspberrypi/userland). If you have problems with the Raspbian distribution packages, report them in the [github.com/RPi-Distro/repo](https://github.com/RPi-Distro/repo). If you simply have a question, then [the Raspberry Pi forums](https://www.raspberrypi.org/forums) are the best place to ask it.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+**Describe the bug**
 | 
					 | 
				
			||||||
+Add a clear and concise description of what you think the bug is.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+**To reproduce**
 | 
					 | 
				
			||||||
+List the steps required to reproduce the issue.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+**Expected behaviour**
 | 
					 | 
				
			||||||
+Add a clear and concise description of what you expected to happen.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+**Actual behaviour**
 | 
					 | 
				
			||||||
+Add a clear and concise description of what actually happened.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+**System**
 | 
					 | 
				
			||||||
+ Copy and paste the results of the raspinfo command in to this section. Alternatively, copy and paste a pastebin link, or add answers to the following questions:
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+* Which model of Raspberry Pi? e.g. Pi3B+, PiZeroW
 | 
					 | 
				
			||||||
+* Which OS and version (`cat /etc/rpi-issue`)?
 | 
					 | 
				
			||||||
+* Which firmware version (`vcgencmd version`)?
 | 
					 | 
				
			||||||
+* Which kernel version (`uname -a`)?
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+**Logs**
 | 
					 | 
				
			||||||
+If applicable, add the relevant output from `dmesg` or similar.
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+**Additional context**
 | 
					 | 
				
			||||||
+Add any other relevant context for the problem.
 | 
					 | 
				
			||||||
@ -1,91 +0,0 @@
 | 
				
			|||||||
From cbc38ebf8ff6d01cd11270d99762d4216ff4237d Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Mon, 26 Nov 2018 19:46:58 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] net: lan78xx: Support auto-downshift to 100Mb/s
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Ethernet cables with faulty or missing pairs (specifically pairs C and
 | 
					 | 
				
			||||||
D) allow auto-negotiation to 1000Mbs, but do not support the successful
 | 
					 | 
				
			||||||
establishment of a link. Add a DT property, "microchip,downshift-after",
 | 
					 | 
				
			||||||
to configure the number of auto-negotiation failures after which it
 | 
					 | 
				
			||||||
falls back to 100Mbs. Valid values are 2, 3, 4, 5 and 0, where 0 means
 | 
					 | 
				
			||||||
never downshift.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 .../bindings/net/microchip,lan78xx.txt        |  3 +++
 | 
					 | 
				
			||||||
 drivers/net/phy/microchip.c                   | 27 +++++++++++++++++++
 | 
					 | 
				
			||||||
 include/linux/microchipphy.h                  |  8 ++++++
 | 
					 | 
				
			||||||
 3 files changed, 38 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/Documentation/devicetree/bindings/net/microchip,lan78xx.txt
 | 
					 | 
				
			||||||
+++ b/Documentation/devicetree/bindings/net/microchip,lan78xx.txt
 | 
					 | 
				
			||||||
@@ -14,6 +14,9 @@ Optional properties of the embedded PHY:
 | 
					 | 
				
			||||||
 - microchip,led-modes: a 0..4 element vector, with each element configuring
 | 
					 | 
				
			||||||
   the operating mode of an LED. Omitted LEDs are turned off. Allowed values
 | 
					 | 
				
			||||||
   are defined in "include/dt-bindings/net/microchip-lan78xx.h".
 | 
					 | 
				
			||||||
+- microchip,downshift-after: sets the number of failed auto-negotiation
 | 
					 | 
				
			||||||
+  attempts after which the link is downgraded from 1000BASE-T. Should be one of
 | 
					 | 
				
			||||||
+  2, 3, 4, 5 or 0, where 0 means never downshift.
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 Example:
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
--- a/drivers/net/phy/microchip.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/phy/microchip.c
 | 
					 | 
				
			||||||
@@ -217,6 +217,7 @@ static int lan88xx_probe(struct phy_devi
 | 
					 | 
				
			||||||
 	struct device *dev = &phydev->mdio.dev;
 | 
					 | 
				
			||||||
 	struct lan88xx_priv *priv;
 | 
					 | 
				
			||||||
 	u32 led_modes[4];
 | 
					 | 
				
			||||||
+	u32 downshift_after = 0;
 | 
					 | 
				
			||||||
 	int len;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 | 
					 | 
				
			||||||
@@ -246,6 +247,32 @@ static int lan88xx_probe(struct phy_devi
 | 
					 | 
				
			||||||
 		return -EINVAL;
 | 
					 | 
				
			||||||
 	}
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	if (!of_property_read_u32(dev->of_node,
 | 
					 | 
				
			||||||
+				  "microchip,downshift-after",
 | 
					 | 
				
			||||||
+				  &downshift_after)) {
 | 
					 | 
				
			||||||
+		u32 mask = LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_MASK;
 | 
					 | 
				
			||||||
+		u32 val= LAN78XX_PHY_CTRL3_AUTO_DOWNSHIFT;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+		switch (downshift_after) {
 | 
					 | 
				
			||||||
+		case 2:	val |= LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_2;
 | 
					 | 
				
			||||||
+			break;
 | 
					 | 
				
			||||||
+		case 3:	val |= LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_3;
 | 
					 | 
				
			||||||
+			break;
 | 
					 | 
				
			||||||
+		case 4:	val |= LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_4;
 | 
					 | 
				
			||||||
+			break;
 | 
					 | 
				
			||||||
+		case 5:	val |= LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_5;
 | 
					 | 
				
			||||||
+			break;
 | 
					 | 
				
			||||||
+		case 0: // Disable completely
 | 
					 | 
				
			||||||
+			mask = LAN78XX_PHY_CTRL3_AUTO_DOWNSHIFT;
 | 
					 | 
				
			||||||
+			val = 0;
 | 
					 | 
				
			||||||
+			break;
 | 
					 | 
				
			||||||
+		default:
 | 
					 | 
				
			||||||
+			return -EINVAL;
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+		(void)phy_modify_paged(phydev, 1, LAN78XX_PHY_CTRL3,
 | 
					 | 
				
			||||||
+				       mask, val);
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	/* these values can be used to identify internal PHY */
 | 
					 | 
				
			||||||
 	priv->chip_id = phy_read_mmd(phydev, 3, LAN88XX_MMD3_CHIP_ID);
 | 
					 | 
				
			||||||
 	priv->chip_rev = phy_read_mmd(phydev, 3, LAN88XX_MMD3_CHIP_REV);
 | 
					 | 
				
			||||||
--- a/include/linux/microchipphy.h
 | 
					 | 
				
			||||||
+++ b/include/linux/microchipphy.h
 | 
					 | 
				
			||||||
@@ -61,6 +61,14 @@
 | 
					 | 
				
			||||||
 /* Registers specific to the LAN7800/LAN7850 embedded phy */
 | 
					 | 
				
			||||||
 #define LAN78XX_PHY_LED_MODE_SELECT		(0x1D)
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+#define LAN78XX_PHY_CTRL3			(0x14)
 | 
					 | 
				
			||||||
+#define LAN78XX_PHY_CTRL3_AUTO_DOWNSHIFT	(0x0010)
 | 
					 | 
				
			||||||
+#define LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_MASK	(0x000c)
 | 
					 | 
				
			||||||
+#define LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_2	(0x0000)
 | 
					 | 
				
			||||||
+#define LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_3	(0x0004)
 | 
					 | 
				
			||||||
+#define LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_4	(0x0008)
 | 
					 | 
				
			||||||
+#define LAN78XX_PHY_CTRL3_DOWNSHIFT_CTRL_5	(0x000c)
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 /* DSP registers */
 | 
					 | 
				
			||||||
 #define PHY_ARDENNES_MMD_DEV_3_PHY_CFG		(0x806A)
 | 
					 | 
				
			||||||
 #define PHY_ARDENNES_MMD_DEV_3_PHY_CFG_ZD_DLY_EN_	(0x2000)
 | 
					 | 
				
			||||||
@ -1,101 +0,0 @@
 | 
				
			|||||||
From ab759f3138e5ea798e93003e6ce0ffe66ac2970f Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Dave Stevenson <dave.stevenson@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Thu, 10 Jan 2019 17:58:06 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] firmware: raspberrypi: Report the fw variant during
 | 
					 | 
				
			||||||
 probe
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The driver already reported the firmware build date during probe.
 | 
					 | 
				
			||||||
The mailbox calls have been extended to also report the variant
 | 
					 | 
				
			||||||
 1 = standard start.elf
 | 
					 | 
				
			||||||
 2 = start_x.elf (includes camera stack)
 | 
					 | 
				
			||||||
 3 = start_db.elf (includes assert logging)
 | 
					 | 
				
			||||||
 4 = start_cd.elf (cutdown version for smallest memory footprint).
 | 
					 | 
				
			||||||
Log the variant during probe.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
firmware: raspberrypi: Report the fw git hash during probe
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The firmware can now report the git hash from which it was built
 | 
					 | 
				
			||||||
via the mailbox, so report it during probe.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/firmware/raspberrypi.c             | 40 +++++++++++++++++++++-
 | 
					 | 
				
			||||||
 include/soc/bcm2835/raspberrypi-firmware.h |  2 ++
 | 
					 | 
				
			||||||
 2 files changed, 41 insertions(+), 1 deletion(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/firmware/raspberrypi.c
 | 
					 | 
				
			||||||
+++ b/drivers/firmware/raspberrypi.c
 | 
					 | 
				
			||||||
@@ -234,6 +234,15 @@ rpi_firmware_print_firmware_revision(str
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	time64_t date_and_time;
 | 
					 | 
				
			||||||
 	u32 packet;
 | 
					 | 
				
			||||||
+	static const char * const variant_strs[] = {
 | 
					 | 
				
			||||||
+		"unknown",
 | 
					 | 
				
			||||||
+		"start",
 | 
					 | 
				
			||||||
+		"start_x",
 | 
					 | 
				
			||||||
+		"start_db",
 | 
					 | 
				
			||||||
+		"start_cd",
 | 
					 | 
				
			||||||
+	};
 | 
					 | 
				
			||||||
+	const char *variant_str = "cmd unsupported";
 | 
					 | 
				
			||||||
+	u32 variant;
 | 
					 | 
				
			||||||
 	int ret = rpi_firmware_property(fw,
 | 
					 | 
				
			||||||
 					RPI_FIRMWARE_GET_FIRMWARE_REVISION,
 | 
					 | 
				
			||||||
 					&packet, sizeof(packet));
 | 
					 | 
				
			||||||
@@ -243,7 +252,35 @@ rpi_firmware_print_firmware_revision(str
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	/* This is not compatible with y2038 */
 | 
					 | 
				
			||||||
 	date_and_time = packet;
 | 
					 | 
				
			||||||
-	dev_info(fw->cl.dev, "Attached to firmware from %ptT\n", &date_and_time);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	ret = rpi_firmware_property(fw, RPI_FIRMWARE_GET_FIRMWARE_VARIANT,
 | 
					 | 
				
			||||||
+				    &variant, sizeof(variant));
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (!ret) {
 | 
					 | 
				
			||||||
+		if (variant >= ARRAY_SIZE(variant_strs))
 | 
					 | 
				
			||||||
+			variant = 0;
 | 
					 | 
				
			||||||
+		variant_str = variant_strs[variant];
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	dev_info(fw->cl.dev,
 | 
					 | 
				
			||||||
+		 "Attached to firmware from %ptT, variant %s\n",
 | 
					 | 
				
			||||||
+		 &date_and_time, variant_str);
 | 
					 | 
				
			||||||
+}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+static void
 | 
					 | 
				
			||||||
+rpi_firmware_print_firmware_hash(struct rpi_firmware *fw)
 | 
					 | 
				
			||||||
+{
 | 
					 | 
				
			||||||
+	u32 hash[5];
 | 
					 | 
				
			||||||
+	int ret = rpi_firmware_property(fw,
 | 
					 | 
				
			||||||
+					RPI_FIRMWARE_GET_FIRMWARE_HASH,
 | 
					 | 
				
			||||||
+					hash, sizeof(hash));
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	if (ret)
 | 
					 | 
				
			||||||
+		return;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	dev_info(fw->cl.dev,
 | 
					 | 
				
			||||||
+		 "Firmware hash is %08x%08x%08x%08x%08x\n",
 | 
					 | 
				
			||||||
+		 hash[0], hash[1], hash[2], hash[3], hash[4]);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static void
 | 
					 | 
				
			||||||
@@ -332,6 +369,7 @@ static int rpi_firmware_probe(struct pla
 | 
					 | 
				
			||||||
 	g_pdev = pdev;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	rpi_firmware_print_firmware_revision(fw);
 | 
					 | 
				
			||||||
+	rpi_firmware_print_firmware_hash(fw);
 | 
					 | 
				
			||||||
 	rpi_register_hwmon_driver(dev, fw);
 | 
					 | 
				
			||||||
 	rpi_register_clk_driver(dev);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
--- a/include/soc/bcm2835/raspberrypi-firmware.h
 | 
					 | 
				
			||||||
+++ b/include/soc/bcm2835/raspberrypi-firmware.h
 | 
					 | 
				
			||||||
@@ -38,6 +38,8 @@ struct rpi_firmware_property_tag_header
 | 
					 | 
				
			||||||
 enum rpi_firmware_property_tag {
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_PROPERTY_END =                           0,
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_GET_FIRMWARE_REVISION =                  0x00000001,
 | 
					 | 
				
			||||||
+	RPI_FIRMWARE_GET_FIRMWARE_VARIANT =                   0x00000002,
 | 
					 | 
				
			||||||
+	RPI_FIRMWARE_GET_FIRMWARE_HASH =                      0x00000003,
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_SET_CURSOR_INFO =                        0x00008010,
 | 
					 | 
				
			||||||
 	RPI_FIRMWARE_SET_CURSOR_STATE =                       0x00008011,
 | 
					 | 
				
			||||||
@ -1,45 +0,0 @@
 | 
				
			|||||||
From a242ba451fd64f0b9f07605ff0d8f6981aa6e955 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Joshua Emele <jemele@acm.org>
 | 
					 | 
				
			||||||
Date: Wed, 7 Nov 2018 16:07:40 -0800
 | 
					 | 
				
			||||||
Subject: [PATCH] lan78xx: Debounce link events to minimize poll storm
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The bInterval is set to 4 (i.e. 8 microframes => 1ms) and the only bit
 | 
					 | 
				
			||||||
that the driver pays attention to is "link was reset". If there's a
 | 
					 | 
				
			||||||
flapping status bit in that endpoint data, (such as if PHY negotiation
 | 
					 | 
				
			||||||
needs a few tries to get a stable link) then polling at a slower rate
 | 
					 | 
				
			||||||
would act as a de-bounce.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
See: https://github.com/raspberrypi/linux/issues/2447
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/usb/lan78xx.c | 13 ++++++++++++-
 | 
					 | 
				
			||||||
 1 file changed, 12 insertions(+), 1 deletion(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/usb/lan78xx.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/usb/lan78xx.c
 | 
					 | 
				
			||||||
@@ -436,6 +436,11 @@ static bool enable_tso;
 | 
					 | 
				
			||||||
 module_param(enable_tso, bool, 0644);
 | 
					 | 
				
			||||||
 MODULE_PARM_DESC(enable_tso, "Enables TCP segmentation offload");
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+#define INT_URB_MICROFRAMES_PER_MS	8
 | 
					 | 
				
			||||||
+static int int_urb_interval_ms = 8;
 | 
					 | 
				
			||||||
+module_param(int_urb_interval_ms, int, 0);
 | 
					 | 
				
			||||||
+MODULE_PARM_DESC(int_urb_interval_ms, "Override usb interrupt urb interval");
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 static int lan78xx_read_reg(struct lan78xx_net *dev, u32 index, u32 *data)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
 	u32 *buf = kmalloc(sizeof(u32), GFP_KERNEL);
 | 
					 | 
				
			||||||
@@ -3770,7 +3775,13 @@ static int lan78xx_probe(struct usb_inte
 | 
					 | 
				
			||||||
 	netdev->max_mtu = MAX_SINGLE_PACKET_SIZE;
 | 
					 | 
				
			||||||
 	netif_set_gso_max_size(netdev, MAX_SINGLE_PACKET_SIZE - MAX_HEADER);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	period = ep_intr->desc.bInterval;
 | 
					 | 
				
			||||||
+	if (int_urb_interval_ms <= 0)
 | 
					 | 
				
			||||||
+		period = ep_intr->desc.bInterval;
 | 
					 | 
				
			||||||
+	else
 | 
					 | 
				
			||||||
+		period = int_urb_interval_ms * INT_URB_MICROFRAMES_PER_MS;
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+	netif_notice(dev, probe, netdev, "int urb period %d\n", period);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	maxp = usb_maxpacket(dev->udev, dev->pipe_intr, 0);
 | 
					 | 
				
			||||||
 	buf = kmalloc(maxp, GFP_KERNEL);
 | 
					 | 
				
			||||||
 	if (buf) {
 | 
					 | 
				
			||||||
@ -1,26 +0,0 @@
 | 
				
			|||||||
From b65b1e498059fd4b6299c5327b2fccd002512dba Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Tue, 5 Mar 2019 09:51:22 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] lan78xx: EEE support is now a PHY property
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Now that EEE support is a property of the PHY, use the PHY's DT node
 | 
					 | 
				
			||||||
when querying the EEE-related properties.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
See: https://github.com/raspberrypi/linux/issues/2882
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/net/usb/lan78xx.c | 2 +-
 | 
					 | 
				
			||||||
 1 file changed, 1 insertion(+), 1 deletion(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/net/usb/lan78xx.c
 | 
					 | 
				
			||||||
+++ b/drivers/net/usb/lan78xx.c
 | 
					 | 
				
			||||||
@@ -2182,7 +2182,7 @@ static int lan78xx_phy_init(struct lan78
 | 
					 | 
				
			||||||
 	mii_adv_to_linkmode_adv_t(fc, mii_adv);
 | 
					 | 
				
			||||||
 	linkmode_or(phydev->advertising, fc, phydev->advertising);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
-	if (of_property_read_bool(dev->udev->dev.of_node,
 | 
					 | 
				
			||||||
+	if (of_property_read_bool(phydev->mdio.dev.of_node,
 | 
					 | 
				
			||||||
 				  "microchip,eee-enabled")) {
 | 
					 | 
				
			||||||
 		struct ethtool_eee edata;
 | 
					 | 
				
			||||||
 		memset(&edata, 0, sizeof(edata));
 | 
					 | 
				
			||||||
@ -1,48 +0,0 @@
 | 
				
			|||||||
From 1a8d5b7b7503bddd1a78130e2317d16b80fe8cda Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
Date: Fri, 20 Jul 2018 22:03:41 +0100
 | 
					 | 
				
			||||||
Subject: [PATCH] bcm2835-dma: Add support for per-channel flags
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Add the ability to interpret the high bits of the dreq specifier as
 | 
					 | 
				
			||||||
flags to be included in the DMA_CS register. The motivation for this
 | 
					 | 
				
			||||||
change is the ability to set the DISDEBUG flag for SD card transfers
 | 
					 | 
				
			||||||
to avoid corruption when using the VPU debugger.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Elwell <phil@raspberrypi.org>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/dma/bcm2835-dma.c | 10 ++++++++--
 | 
					 | 
				
			||||||
 1 file changed, 8 insertions(+), 2 deletions(-)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/dma/bcm2835-dma.c
 | 
					 | 
				
			||||||
+++ b/drivers/dma/bcm2835-dma.c
 | 
					 | 
				
			||||||
@@ -139,6 +139,10 @@ struct bcm2835_desc {
 | 
					 | 
				
			||||||
 #define BCM2835_DMA_S_DREQ	BIT(10) /* enable SREQ for source */
 | 
					 | 
				
			||||||
 #define BCM2835_DMA_S_IGNORE	BIT(11) /* ignore source reads - read 0 */
 | 
					 | 
				
			||||||
 #define BCM2835_DMA_BURST_LENGTH(x) ((x & 15) << 12)
 | 
					 | 
				
			||||||
+#define BCM2835_DMA_CS_FLAGS(x) (x & (BCM2835_DMA_PRIORITY(15) | \
 | 
					 | 
				
			||||||
+				      BCM2835_DMA_PANIC_PRIORITY(15) | \
 | 
					 | 
				
			||||||
+				      BCM2835_DMA_WAIT_FOR_WRITES | \
 | 
					 | 
				
			||||||
+				      BCM2835_DMA_DIS_DEBUG))
 | 
					 | 
				
			||||||
 #define BCM2835_DMA_PER_MAP(x)	((x & 31) << 16) /* REQ source */
 | 
					 | 
				
			||||||
 #define BCM2835_DMA_WAIT(x)	((x & 31) << 21) /* add DMA-wait cycles */
 | 
					 | 
				
			||||||
 #define BCM2835_DMA_NO_WIDE_BURSTS BIT(26) /* no 2 beat write bursts */
 | 
					 | 
				
			||||||
@@ -452,7 +456,8 @@ static void bcm2835_dma_start_desc(struc
 | 
					 | 
				
			||||||
 	c->desc = d = to_bcm2835_dma_desc(&vd->tx);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	writel(d->cb_list[0].paddr, c->chan_base + BCM2835_DMA_ADDR);
 | 
					 | 
				
			||||||
-	writel(BCM2835_DMA_ACTIVE, c->chan_base + BCM2835_DMA_CS);
 | 
					 | 
				
			||||||
+	writel(BCM2835_DMA_ACTIVE | BCM2835_DMA_CS_FLAGS(c->dreq),
 | 
					 | 
				
			||||||
+	       c->chan_base + BCM2835_DMA_CS);
 | 
					 | 
				
			||||||
 }
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 static irqreturn_t bcm2835_dma_callback(int irq, void *data)
 | 
					 | 
				
			||||||
@@ -479,7 +484,8 @@ static irqreturn_t bcm2835_dma_callback(
 | 
					 | 
				
			||||||
 	 * if this IRQ handler is threaded.) If the channel is finished, it
 | 
					 | 
				
			||||||
 	 * will remain idle despite the ACTIVE flag being set.
 | 
					 | 
				
			||||||
 	 */
 | 
					 | 
				
			||||||
-	writel(BCM2835_DMA_INT | BCM2835_DMA_ACTIVE,
 | 
					 | 
				
			||||||
+	writel(BCM2835_DMA_INT | BCM2835_DMA_ACTIVE |
 | 
					 | 
				
			||||||
+	       BCM2835_DMA_CS_FLAGS(c->dreq),
 | 
					 | 
				
			||||||
 	       c->chan_base + BCM2835_DMA_CS);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	d = c->desc;
 | 
					 | 
				
			||||||
@ -1,50 +0,0 @@
 | 
				
			|||||||
From 4aefed16e3964d161d32a4f25db849ac16046b13 Mon Sep 17 00:00:00 2001
 | 
					 | 
				
			||||||
From: Phil Howard <phil@gadgetoid.com>
 | 
					 | 
				
			||||||
Date: Fri, 29 Mar 2019 10:53:14 +0000
 | 
					 | 
				
			||||||
Subject: [PATCH] rtc: rv3028: Add backup switchover mode support
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Signed-off-by: Phil Howard <phil@pimoroni.com>
 | 
					 | 
				
			||||||
---
 | 
					 | 
				
			||||||
 drivers/rtc/rtc-rv3028.c | 17 +++++++++++++++++
 | 
					 | 
				
			||||||
 1 file changed, 17 insertions(+)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--- a/drivers/rtc/rtc-rv3028.c
 | 
					 | 
				
			||||||
+++ b/drivers/rtc/rtc-rv3028.c
 | 
					 | 
				
			||||||
@@ -80,6 +80,7 @@
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #define RV3028_BACKUP_TCE		BIT(5)
 | 
					 | 
				
			||||||
 #define RV3028_BACKUP_TCR_MASK		GENMASK(1,0)
 | 
					 | 
				
			||||||
+#define RV3028_BACKUP_BSM_MASK		0x0C
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 #define OFFSET_STEP_PPT			953674
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
@@ -789,6 +790,7 @@ static int rv3028_probe(struct i2c_clien
 | 
					 | 
				
			||||||
 	struct rv3028_data *rv3028;
 | 
					 | 
				
			||||||
 	int ret, status;
 | 
					 | 
				
			||||||
 	u32 ohms;
 | 
					 | 
				
			||||||
+	u8 bsm;
 | 
					 | 
				
			||||||
 	struct nvmem_config nvmem_cfg = {
 | 
					 | 
				
			||||||
 		.name = "rv3028_nvram",
 | 
					 | 
				
			||||||
 		.word_size = 1,
 | 
					 | 
				
			||||||
@@ -860,6 +862,21 @@ static int rv3028_probe(struct i2c_clien
 | 
					 | 
				
			||||||
 	if (ret)
 | 
					 | 
				
			||||||
 		return ret;
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+	/* setup backup switchover mode */
 | 
					 | 
				
			||||||
+	if (!device_property_read_u8(&client->dev, "backup-switchover-mode",
 | 
					 | 
				
			||||||
+				     &bsm))  {
 | 
					 | 
				
			||||||
+		if (bsm <= 3) {
 | 
					 | 
				
			||||||
+			ret = regmap_update_bits(rv3028->regmap, RV3028_BACKUP,
 | 
					 | 
				
			||||||
+				RV3028_BACKUP_BSM_MASK,
 | 
					 | 
				
			||||||
+				(bsm & 0x03) << 2);
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
+			if (ret)
 | 
					 | 
				
			||||||
+				return ret;
 | 
					 | 
				
			||||||
+		} else {
 | 
					 | 
				
			||||||
+			dev_warn(&client->dev, "invalid backup switchover mode value\n");
 | 
					 | 
				
			||||||
+		}
 | 
					 | 
				
			||||||
+	}
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 	/* setup trickle charger */
 | 
					 | 
				
			||||||
 	if (!device_property_read_u32(&client->dev, "trickle-resistor-ohms",
 | 
					 | 
				
			||||||
 				      &ohms)) {
 | 
					 | 
				
			||||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue
	
	Block a user