aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/arm/altera/fpga-dma.txt25
-rw-r--r--Documentation/devicetree/bindings/firmware/intel,stratix10-rsu.txt28
-rw-r--r--Documentation/devicetree/bindings/firmware/intel,stratix10-svc.txt57
-rw-r--r--Documentation/devicetree/bindings/fpga/altera-partial-reconfig.txt12
-rw-r--r--Documentation/devicetree/bindings/fpga/fpga-region.txt13
-rw-r--r--Documentation/devicetree/bindings/fpga/intel-stratix10-soc-fpga-mgr.txt10
-rw-r--r--Documentation/devicetree/bindings/misc/altera-hwmutex.txt22
-rw-r--r--Documentation/devicetree/bindings/misc/altera-interrupt-latency-counter.txt49
-rw-r--r--Documentation/devicetree/bindings/misc/altera_sysid.txt11
-rw-r--r--Documentation/devicetree/bindings/net/micrel-ksz90x1.txt98
-rw-r--r--Documentation/devicetree/bindings/tty/newhaven_lcd.txt21
-rw-r--r--Documentation/devicetree/bindings/video/altvipfb.txt22
-rw-r--r--Documentation/devicetree/configfs-overlays.txt31
-rw-r--r--Documentation/fpga/debugfs.txt39
-rw-r--r--arch/arm/boot/dts/Makefile2
-rw-r--r--arch/arm/boot/dts/socfpga.dtsi2
-rw-r--r--arch/arm/boot/dts/socfpga_arria10.dtsi3
-rw-r--r--arch/arm/boot/dts/socfpga_arria10_socdk.dtsi17
-rw-r--r--arch/arm/boot/dts/socfpga_arria10_socdk_qspi.dts2
-rw-r--r--arch/arm/boot/dts/socfpga_arria10_swvp.dts542
-rw-r--r--arch/arm/boot/dts/socfpga_arria5_socdk.dts8
-rw-r--r--arch/arm/boot/dts/socfpga_cyclone5_socdk.dts8
-rw-r--r--arch/arm/boot/dts/socfpga_cyclone5_trcom.dts150
-rw-r--r--arch/arm/mach-socfpga/Kconfig12
-rw-r--r--arch/arm/mach-socfpga/Makefile1
-rw-r--r--arch/arm/mach-socfpga/core.h10
-rw-r--r--arch/arm/mach-socfpga/fpga-dma.c689
-rw-r--r--arch/arm/mach-socfpga/socfpga.c8
-rw-r--r--arch/arm64/Kconfig.platforms5
-rw-r--r--arch/arm64/boot/dts/altera/Makefile3
-rw-r--r--arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi88
-rw-r--r--arch/arm64/boot/dts/altera/socfpga_stratix10_fpga_update.dts17
-rw-r--r--arch/arm64/boot/dts/altera/socfpga_stratix10_socdk.dts55
-rw-r--r--arch/arm64/boot/dts/altera/stratix10_swvp.dts763
-rw-r--r--drivers/clk/socfpga/clk-s10.c8
-rw-r--r--drivers/dma/pl330.c52
-rw-r--r--drivers/fpga/Kconfig20
-rw-r--r--drivers/fpga/Makefile4
-rw-r--r--drivers/fpga/altera-freeze-bridge.c4
-rw-r--r--drivers/fpga/fpga-bridge.c4
-rw-r--r--drivers/fpga/fpga-mgr-debugfs.c235
-rw-r--r--drivers/fpga/fpga-mgr-debugfs.h37
-rw-r--r--drivers/fpga/fpga-mgr.c8
-rw-r--r--drivers/fpga/stratix10-soc.c536
-rw-r--r--drivers/hwmon/Kconfig9
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/altera-a10sr-hwmon.c406
-rw-r--r--drivers/mfd/altera-a10sr.c4
-rw-r--r--drivers/misc/Kconfig41
-rw-r--r--drivers/misc/Makefile7
-rw-r--r--drivers/misc/altera_hwmutex.c320
-rw-r--r--drivers/misc/altera_ilc.c299
-rw-r--r--drivers/misc/altera_sysid.c141
-rw-r--r--drivers/misc/intel-rsu.c387
-rw-r--r--drivers/misc/intel-service.c1042
-rw-r--r--drivers/misc/intel-smc.h310
-rw-r--r--drivers/mtd/spi-nor/spi-nor.c2
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/Kconfig2
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c159
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c2
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/ring_mode.c4
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c10
-rw-r--r--drivers/of/Kconfig8
-rw-r--r--drivers/of/Makefile1
-rw-r--r--drivers/of/configfs.c284
-rw-r--r--drivers/tty/Kconfig19
-rw-r--r--drivers/tty/Makefile1
-rw-r--r--drivers/tty/newhaven_lcd.c636
-rw-r--r--drivers/video/Kconfig9
-rw-r--r--drivers/video/fbdev/Kconfig10
-rw-r--r--drivers/video/fbdev/Makefile1
-rw-r--r--drivers/video/fbdev/altvipfb.c303
-rw-r--r--include/linux/altera_hwmutex.h41
-rw-r--r--include/linux/fpga/fpga-mgr.h3
-rw-r--r--include/linux/intel-service-client.h198
-rw-r--r--include/linux/mfd/altera-a10sr.h58
76 files changed, 8401 insertions, 48 deletions
diff --git a/Documentation/devicetree/bindings/arm/altera/fpga-dma.txt b/Documentation/devicetree/bindings/arm/altera/fpga-dma.txt
new file mode 100644
index 000000000000..a08e9010d4e5
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/altera/fpga-dma.txt
@@ -0,0 +1,25 @@
+Altera FPGA DMA FIFO driver
+
+Required properties:
+- compatible : "altr,fpga-dma";
+
+- reg : CSR and DATA register resource definitions (address and length).
+
+- reg-names : Names of the register resources. Should be "csr", "data".
+
+- dmas : DMA request lines. Should be <&pdma 0 &pdma 1>
+
+- dma-names : Names of DMA request lines. Should be "tx", "rx".
+
+Example:
+
+ fpgadma: fifo {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "altr,fpga-dma";
+ reg = <0xff230000 0x20>, <0xc0011000 0x400>;
+ reg-names = "csr", "data";
+ dmas = <&pdma 0 &pdma 1>;
+ dma-names = "tx", "rx";
+ };
+
diff --git a/Documentation/devicetree/bindings/firmware/intel,stratix10-rsu.txt b/Documentation/devicetree/bindings/firmware/intel,stratix10-rsu.txt
new file mode 100644
index 000000000000..9df6f8987dee
--- /dev/null
+++ b/Documentation/devicetree/bindings/firmware/intel,stratix10-rsu.txt
@@ -0,0 +1,28 @@
+Intel Remote System Update (RSU) for Stratix10 SoC FPGAs
+============================================
+The Intel Remote System Update (RSU) driver exposes interfaces
+accessed through the Intel Service Layer to user space via SysFS
+device attribute nodes. The RSU interfaces report/control some of
+the optional RSU features of the Stratix 10 SoC FPGA.
+
+The RSU feature provides a way for customers to update the boot
+configuration of a Stratix 10 SoC device with significantly reduced
+risk of corrupting the bitstream storage and bricking the system.
+
+Required properties:
+-------------------
+The intel-rsu node has the following mandatory properties and must be located
+under the firmware/svc node.
+
+- compatible: "intel,stratix10-rsu"
+
+Example:
+-------
+
+ firmware {
+ svc {
+ rsu {
+ compatible = "intel,stratix10-rsu";
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/firmware/intel,stratix10-svc.txt b/Documentation/devicetree/bindings/firmware/intel,stratix10-svc.txt
new file mode 100644
index 000000000000..1fa66065acc6
--- /dev/null
+++ b/Documentation/devicetree/bindings/firmware/intel,stratix10-svc.txt
@@ -0,0 +1,57 @@
+Intel Service Layer Driver for Stratix10 SoC
+============================================
+Intel Stratix10 SoC is composed of a 64 bit quad-core ARM Cortex A53 hard
+processor system (HPS) and Secure Device Manager (SDM). When the FPGA is
+configured from HPS, there needs to be a way for HPS to notify SDM the
+location and size of the configuration data. Then SDM will get the
+configuration data from that location and perform the FPGA configuration.
+
+To meet the whole system security needs and support virtual machine requesting
+communication with SDM, only the secure world of software (EL3, Exception
+Layer 3) can interface with SDM. All software entities running on other
+exception layers must channel through the EL3 software whenever it needs
+service from SDM.
+
+Intel Stratix10 service layer driver, running at privileged exception level
+(EL1, Exception Layer 1), interfaces with the service providers and provides
+the services for FPGA configuration, QSPI, Crypto and warm reset. Service layer
+driver also manages secure monitor call (SMC) to communicate with secure monitor
+code running in EL3.
+
+Required properties:
+-------------------
+The svc node has the following mandatory properties, must be located under
+the firmware node.
+
+- compatible: "intel,stratix10-svc"
+- method: smc or hvc
+ smc - Secure Monitor Call
+ hvc - Hypervisor Call
+- memory-region:
+ phandle to the reserved memory node. See
+ Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
+ for details
+
+Example:
+-------
+
+ reserved-memory {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+
+ service_reserved: svcbuffer@0 {
+ compatible = "shared-dma-pool";
+ reg = <0x0 0x0 0x0 0x1000000>;
+ alignment = <0x1000>;
+ no-map;
+ };
+ };
+
+ firmware {
+ svc {
+ compatible = "intel,stratix10-svc";
+ method = "smc";
+ memory-region = <&service_reserved>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/fpga/altera-partial-reconfig.txt b/Documentation/devicetree/bindings/fpga/altera-partial-reconfig.txt
new file mode 100644
index 000000000000..bbbb9cdb3da7
--- /dev/null
+++ b/Documentation/devicetree/bindings/fpga/altera-partial-reconfig.txt
@@ -0,0 +1,12 @@
+Altera Partial Reconfiguration IP Core
+
+Required properties:
+- compatible : should contain "altr,pr-ip-core"
+- reg : base address and size for memory mapped io.
+
+Example:
+
+ fpga_mgr: fpga-mgr@ff20c000 {
+ compatible = "altr,pr-ip-core";
+ reg = <0xff20c000 0x8>;
+ };
diff --git a/Documentation/devicetree/bindings/fpga/fpga-region.txt b/Documentation/devicetree/bindings/fpga/fpga-region.txt
index 6db8aeda461a..f071306d888d 100644
--- a/Documentation/devicetree/bindings/fpga/fpga-region.txt
+++ b/Documentation/devicetree/bindings/fpga/fpga-region.txt
@@ -348,6 +348,17 @@ The Device Tree Overlay will contain:
* child nodes corresponding to hardware that will be loaded in this region of
the FPGA.
+The Device Tree Overlay will optionally contain:
+ * "region-unfreeze-timeout-us"
+ Maximum time in microseconds to wait for bridges to successfully become
+ enabled after the region has been programmed.
+ * "region-freeze-timeout-us"
+ Maximum time in microseconds to wait for bridges to successfully become
+ disabled before the region has been programmed.
+ * "config-complete-timeout-us"
+ Maximum time in microseconds to wait for the FPGA to go to operating state
+ after the region has been programmed.
+
Device Tree Example: Full Reconfiguration without Bridges
=========================================================
@@ -382,6 +393,8 @@ fragment@0 {
#size-cells = <1>;
firmware-name = "zynq-gpio.bin";
+ region-unfreeze-timeout-us = <4>;
+ region-freeze-timeout-us = <4>;
gpio1: gpio@40000000 {
compatible = "xlnx,xps-gpio-1.00.a";
diff --git a/Documentation/devicetree/bindings/fpga/intel-stratix10-soc-fpga-mgr.txt b/Documentation/devicetree/bindings/fpga/intel-stratix10-soc-fpga-mgr.txt
new file mode 100644
index 000000000000..78de68975b47
--- /dev/null
+++ b/Documentation/devicetree/bindings/fpga/intel-stratix10-soc-fpga-mgr.txt
@@ -0,0 +1,10 @@
+Intel Stratix10 SoC FPGA Manager
+
+Required properties:
+- compatible : should contain "intel,stratix10-soc-fpga-mgr"
+
+Example:
+
+ fpga_mgr: fpga-mgr@0 {
+ compatible = "intel,stratix10-soc-fpga-mgr";
+ };
diff --git a/Documentation/devicetree/bindings/misc/altera-hwmutex.txt b/Documentation/devicetree/bindings/misc/altera-hwmutex.txt
new file mode 100644
index 000000000000..6a583d08ece4
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/altera-hwmutex.txt
@@ -0,0 +1,22 @@
+Altera hardware mutex
+Altera hardware mutex can provide hardware assistance for synchronization and
+mutual exclusion between processors in asymmetric/symmetric multiprocessing
+(AMP/SMP) system or multi processes/threads in uniprocessor system.
+
+Required properties:
+- compatible : "altr,mutex-1.0".
+- reg : physical base address of the mutex and length of memory mapped
+ region.
+
+Example:
+ mutex0: mutex0@0x100 {
+ compatible = "altr,hwmutex-1.0";
+ reg = <0x100 0x8>;
+ };
+
+Example of mutex's client node that includes mutex phandle.
+ mclient0: mclient0@0x200 {
+ compatible = "client-1.0";
+ reg = <0x200 0x10>;
+ mutex = <&mutex0>;
+ };
diff --git a/Documentation/devicetree/bindings/misc/altera-interrupt-latency-counter.txt b/Documentation/devicetree/bindings/misc/altera-interrupt-latency-counter.txt
new file mode 100644
index 000000000000..09f682057616
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/altera-interrupt-latency-counter.txt
@@ -0,0 +1,49 @@
+Altera Interrupt Latency Counter soft IP
+Altera Interrupt Latency Counter IP core driver provides a sysfs interface
+for user to obtain interrupt latency values from Altera Interrupt Latency
+Counter soft IP.
+
+The sysfs interface is located at path,
+/sys/bus/platform/devices/{addr}.ilc/ilc_data/{int_#}
+with
+- {addr} = the base address of the soft ip
+- {int_#} = the interrupt number
+
+Example use case:
+# cat /sys/bus/platform/devices/c0010000.ilc/ilc_data/40
+
+Required properties:
+- compatible :
+ - "altr,ilc-1.0"
+- reg :
+ - physical base address of the soft ip and length of memory mapped region
+- interrupt-parent :
+ - interrupt source phandle similiar to the interrupt source node
+- interrupts :
+ -interrupt number. The interrupt specifier format depends on the interrupt
+ controller parent
+
+Altera specific properties:
+- altr,sw-fifo-depth :
+ - define software fifo depth needed to record latency values
+
+Note:
+- For edge triggered interrupt, the order of loading the ILC driver relative
+ to driver of the actual interrupt source affects the meaning of the ILC
+ values. If the ILC driver is loaded first, then the count values represent
+ the time to the start of the interrupt handler of the of the interrupt source.
+ If the order is switched, then the counts represent the time to finish the
+ interrupt handler for the interrupt source.
+
+- The driver for the interrupt source must be changed to request a shared irq.
+
+Example:
+ interrupt_latency_counter_0: intc@0x10000000 {
+ compatible = "altr,ilc-1.0";
+ reg = <0x10000000 0x00000100>;
+ interrupt-parent = < &interrupt_parent >;
+ interrupts = < 0 1 4 >;
+ altr,sw-fifo-depth = < 32 >;
+ };
+
+
diff --git a/Documentation/devicetree/bindings/misc/altera_sysid.txt b/Documentation/devicetree/bindings/misc/altera_sysid.txt
new file mode 100644
index 000000000000..c3bbd576b74b
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/altera_sysid.txt
@@ -0,0 +1,11 @@
+Altera Sysid IP core driver
+
+Required properties:
+- compatible: altr,sysid-1.0
+
+Example:
+
+sysid_qsys: sysid@0x10000 {
+ compatible = "altr,sysid-1.0";
+ reg = < 0x10000 0x00000008 >;
+};
diff --git a/Documentation/devicetree/bindings/net/micrel-ksz90x1.txt b/Documentation/devicetree/bindings/net/micrel-ksz90x1.txt
index e22d8cfea687..35970132d4ff 100644
--- a/Documentation/devicetree/bindings/net/micrel-ksz90x1.txt
+++ b/Documentation/devicetree/bindings/net/micrel-ksz90x1.txt
@@ -12,7 +12,7 @@ and therefore may overwrite them.
KSZ9021:
All skew control options are specified in picoseconds. The minimum
- value is 0, the maximum value is 3000, and it is incremented by 200ps
+ value is 0, the maximum value is 1800, and it is incremented by 120ps
steps.
Optional properties:
@@ -37,6 +37,71 @@ KSZ9031:
step is 60ps. The default value is the neutral setting, so setting
rxc-skew-ps=<0> actually results in -900 picoseconds adjustment.
+ The KSZ9031 hardware supports a range of skew values from negative to
+ positive, where the specific range is property dependent. All values
+ specified in the devicetree are offset by the minimum value so they
+ can be represented as positive integers in the devicetree since it's
+ difficult to represent a negative number in the devictree.
+
+ The following 5-bit values table apply to rxc-skew-ps and txc-skew-ps.
+
+ Pad Skew Value Delay (ps) Devicetree Value
+ ------------------------------------------------------
+ 0_0000 -900ps 0
+ 0_0001 -840ps 60
+ 0_0010 -780ps 120
+ 0_0011 -720ps 180
+ 0_0100 -660ps 240
+ 0_0101 -600ps 300
+ 0_0110 -540ps 360
+ 0_0111 -480ps 420
+ 0_1000 -420ps 480
+ 0_1001 -360ps 540
+ 0_1010 -300ps 600
+ 0_1011 -240ps 660
+ 0_1100 -180ps 720
+ 0_1101 -120ps 780
+ 0_1110 -60ps 840
+ 0_1111 0ps 900
+ 1_0000 60ps 960
+ 1_0001 120ps 1020
+ 1_0010 180ps 1080
+ 1_0011 240ps 1140
+ 1_0100 300ps 1200
+ 1_0101 360ps 1260
+ 1_0110 420ps 1320
+ 1_0111 480ps 1380
+ 1_1000 540ps 1440
+ 1_1001 600ps 1500
+ 1_1010 660ps 1560
+ 1_1011 720ps 1620
+ 1_1100 780ps 1680
+ 1_1101 840ps 1740
+ 1_1110 900ps 1800
+ 1_1111 960ps 1860
+
+ The following 4-bit values table apply to the txdX-skew-ps, rxdX-skew-ps
+ data pads, and the rxdv-skew-ps, txen-skew-ps control pads.
+
+ Pad Skew Value Delay (ps) Devicetree Value
+ ------------------------------------------------------
+ 0000 -420ps 0
+ 0001 -360ps 60
+ 0010 -300ps 120
+ 0011 -240ps 180
+ 0100 -180ps 240
+ 0101 -120ps 300
+ 0110 -60ps 360
+ 0111 0ps 420
+ 1000 60ps 480
+ 1001 120ps 540
+ 1010 180ps 600
+ 1011 240ps 660
+ 1100 300ps 720
+ 1101 360ps 780
+ 1110 420ps 840
+ 1111 480ps 900
+
Optional properties:
Maximum value of 1860, default value 900:
@@ -66,11 +131,21 @@ KSZ9031:
Examples:
+ /* Attach to an Ethernet device with autodetected PHY */
+ &enet {
+ rxc-skew-ps = <1800>;
+ rxdv-skew-ps = <0>;
+ txc-skew-ps = <1800>;
+ txen-skew-ps = <0>;
+ status = "okay";
+ };
+
+ /* Attach to an explicitly-specified PHY */
mdio {
phy0: ethernet-phy@0 {
- rxc-skew-ps = <3000>;
+ rxc-skew-ps = <1800>;
rxdv-skew-ps = <0>;
- txc-skew-ps = <3000>;
+ txc-skew-ps = <1800>;
txen-skew-ps = <0>;
reg = <0>;
};
@@ -79,3 +154,20 @@ Examples:
phy = <&phy0>;
phy-mode = "rgmii-id";
};
+
+References
+
+ Micrel ksz9021rl/rn Data Sheet, Revision 1.2. Dated 2/13/2014.
+ http://www.micrel.com/_PDF/Ethernet/datasheets/ksz9021rl-rn_ds.pdf
+
+ Micrel ksz9031rnx Data Sheet, Revision 2.1. Dated 11/20/2014.
+ http://www.micrel.com/_PDF/Ethernet/datasheets/KSZ9031RNX.pdf
+
+Notes:
+
+ Note that a previous version of the Micrel ksz9021rl/rn Data Sheet
+ was missing extended register 106 (transmit data pad skews), and
+ incorrectly specified the ps per step as 200ps/step instead of
+ 120ps/step. The latest update to this document reflects the latest
+ revision of the Micrel specification even though usage in the kernel
+ still reflects that incorrect document.
diff --git a/Documentation/devicetree/bindings/tty/newhaven_lcd.txt b/Documentation/devicetree/bindings/tty/newhaven_lcd.txt
new file mode 100644
index 000000000000..5ff0438640d6
--- /dev/null
+++ b/Documentation/devicetree/bindings/tty/newhaven_lcd.txt
@@ -0,0 +1,21 @@
+* TTY on a Newhaven NHD‐0216K3Z‐NSW‐BBW LCD connected to I2C
+
+Required properties:
+- compatible: Should be "newhaven,nhd‐0216k3z‐nsw‐bbw";
+- reg: i2c address
+- height: should be 2 lines
+- width: should be 16 characters
+- brightness: backlight brightness. Range is 1 to 8, where
+ 1=OFF and 8=maximum brightness.
+
+Example:
+
+&i2c0 {
+ lcd: lcd@28 {
+ compatible = "newhaven,nhd‐0216k3z‐nsw‐bbw";
+ reg = <0x28>;
+ height = <2>;
+ width = <16>;
+ brightness = <8>;
+ };
+
diff --git a/Documentation/devicetree/bindings/video/altvipfb.txt b/Documentation/devicetree/bindings/video/altvipfb.txt
new file mode 100644
index 000000000000..5e376184ba33
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/altvipfb.txt
@@ -0,0 +1,22 @@
+Altera Video and Image Processing(VIP) Frame Reader bindings
+
+Required properties:
+- compatible: "altr,vip-frame-reader-9.1" or "altr,vip-frame-reader-1.0"
+- reg: Physical base address and length of the framebuffer controller's
+ registers.
+- max-width: The width of the framebuffer in pixels.
+- max-height: The height of the framebuffer in pixels.
+- bits-per-color: only "8" is currently supported
+- mem-word-width = the bus width of the avalon master port on the frame reader
+
+Example:
+
+alt_vip_vfr_0: vip@0xff260000 {
+ compatible = "altr,vip-frame-reader-1.0";
+ reg = <0xff260000 0x00000080>;
+ max-width = <1024>;
+ max-height = <768>;
+ bits-per-color = <8>;
+ mem-word-width = <128>;
+};
+
diff --git a/Documentation/devicetree/configfs-overlays.txt b/Documentation/devicetree/configfs-overlays.txt
new file mode 100644
index 000000000000..185d85ef52e4
--- /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 rationale for 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).
diff --git a/Documentation/fpga/debugfs.txt b/Documentation/fpga/debugfs.txt
new file mode 100644
index 000000000000..b01950f76e20
--- /dev/null
+++ b/Documentation/fpga/debugfs.txt
@@ -0,0 +1,39 @@
+FPGA Manager DebugFS interface for FPGA reprogramming.
+
+Alan Tull 2016
+
+Each FPGA gets its own directory such as <debugfs>/fpga_manager/fpga0 and
+three files:
+
+ - [RW] flags: flags as defined in fpga-mgr.h. For example:
+
+ $ echo 1 > /sys/kernel/debug/fpga_manager/fpga0/flags
+
+ - [RW] config_complete_timeout_us: time out in microseconds to wait for
+ FPGA to go to operating state after
+ region has been programmed.
+
+ $ echo 4 > /sys/kernel/debug/fpga_manager/fpga0/config_complete_timeout_us
+
+ - [RW] firmware_name: Name of an FPGA image firmware file. Writing initiates
+ a complete FPGA programming cycle. Note that the image
+ file must be in a directory on the firmware search path
+ such as /lib/firmware.
+
+ $ echo image.rbf > /sys/kernel/debug/fpga_manager/fpga0/firmware_name
+
+ - [WO] image: Raw FPGA image data. Writing the FPGA image data will
+ initiate a complete FPGA programming cycle. Data must
+ be written in one chunk, for example:
+
+ $ dd bs=10M if=./image.rbf of=/sys/kernel/debug/fpga_manager/fpga0/image
+ (where image.rbf < 10M)
+
+To program the FPGA, write the flags (if needed), then use either the
+firmware_name or image file to program.
+
+This interface does not handle bridges or loading/unloading of soft IP device
+drivers. This makes it really easy to mess things up by doing things like
+reprogramming the hardware out from under a driver or reprogramming while a
+bridge is enabled, causing gunk to go out on a cpu bus. It should go without
+saying that this interface is for debug only. Not intended for production use.
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 37a3de760d40..8382337df6b2 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -868,12 +868,14 @@ dtb-$(CONFIG_ARCH_SOCFPGA) += \
socfpga_arria10_socdk_nand.dtb \
socfpga_arria10_socdk_qspi.dtb \
socfpga_arria10_socdk_sdmmc.dtb \
+ socfpga_arria10_swvp.dtb \
socfpga_cyclone5_mcvevk.dtb \
socfpga_cyclone5_socdk.dtb \
socfpga_cyclone5_de0_sockit.dtb \
socfpga_cyclone5_sockit.dtb \
socfpga_cyclone5_socrates.dtb \
socfpga_cyclone5_sodia.dtb \
+ socfpga_cyclone5_trcom.dtb \
socfpga_cyclone5_vining_fpga.dtb \
socfpga_vt.dtb
dtb-$(CONFIG_ARCH_SPEAR13XX) += \
diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi
index b38f8c240558..28ecb4bdf5aa 100644
--- a/arch/arm/boot/dts/socfpga.dtsi
+++ b/arch/arm/boot/dts/socfpga.dtsi
@@ -101,7 +101,7 @@
};
};
- base_fpga_region {
+ base_fpga_region: base-fpga-region {
compatible = "fpga-region";
fpga-mgr = <&fpgamgr0>;
diff --git a/arch/arm/boot/dts/socfpga_arria10.dtsi b/arch/arm/boot/dts/socfpga_arria10.dtsi
index bd1985694bca..a2326687b27a 100644
--- a/arch/arm/boot/dts/socfpga_arria10.dtsi
+++ b/arch/arm/boot/dts/socfpga_arria10.dtsi
@@ -79,6 +79,7 @@
#dma-requests = <32>;
clocks = <&l4_main_clk>;
clock-names = "apb_pclk";
+ microcode-cached;
};
};
@@ -748,7 +749,7 @@
timer@ffffc600 {
compatible = "arm,cortex-a9-twd-timer";
reg = <0xffffc600 0x100>;
- interrupts = <1 13 0xf04>;
+ interrupts = <1 13 0xf01>;
clocks = <&mpu_periph_clk>;
};
diff --git a/arch/arm/boot/dts/socfpga_arria10_socdk.dtsi b/arch/arm/boot/dts/socfpga_arria10_socdk.dtsi
index 64cc86a98771..2a7466891d0e 100644
--- a/arch/arm/boot/dts/socfpga_arria10_socdk.dtsi
+++ b/arch/arm/boot/dts/socfpga_arria10_socdk.dtsi
@@ -126,6 +126,10 @@
compatible = "altr,a10sr-reset";
#reset-cells = <1>;
};
+
+ ps_alarm {
+ compatible = "altr,a10sr-hwmon";
+ };
};
};
@@ -140,6 +144,14 @@
i2c-sda-falling-time-ns = <6000>;
i2c-scl-falling-time-ns = <6000>;
+ lcd: lcd@28 {
+ compatible = "newhaven,nhd-0216k3z-nsw-bbw";
+ reg = <0x28>;
+ height = <2>;
+ width = <16>;
+ brightness = <8>;
+ };
+
eeprom@51 {
compatible = "atmel,24c32";
reg = <0x51>;
@@ -151,6 +163,11 @@
reg = <0x68>;
};
+ max@4c {
+ compatible = "max1619";
+ reg = <0x4c>;
+ };
+
ltc@5c {
compatible = "ltc2977";
reg = <0x5c>;
diff --git a/arch/arm/boot/dts/socfpga_arria10_socdk_qspi.dts b/arch/arm/boot/dts/socfpga_arria10_socdk_qspi.dts
index beb2fc6b9eb6..0939fa7f6943 100644
--- a/arch/arm/boot/dts/socfpga_arria10_socdk_qspi.dts
+++ b/arch/arm/boot/dts/socfpga_arria10_socdk_qspi.dts
@@ -30,7 +30,7 @@
m25p,fast-read;
cdns,page-size = <256>;
cdns,block-size = <16>;
- cdns,read-delay = <4>;
+ cdns,read-delay = <3>;
cdns,tshsl-ns = <50>;
cdns,tsd2d-ns = <50>;
cdns,tchsh-ns = <4>;
diff --git a/arch/arm/boot/dts/socfpga_arria10_swvp.dts b/arch/arm/boot/dts/socfpga_arria10_swvp.dts
new file mode 100644
index 000000000000..c1a315524ba1
--- /dev/null
+++ b/arch/arm/boot/dts/socfpga_arria10_swvp.dts
@@ -0,0 +1,542 @@
+/*
+ * Copyright (C) 2015 Altera Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/dts-v1/;
+
+/ {
+ #address-cells = <0x1>;
+ #size-cells = <0x1>;
+ model = "Altera SOCFPGA Arria 10";
+ compatible = "altr,socfpga-arria10", "altr,socfpga";
+
+ chosen {
+ bootargs = "console=ttyS1,115200 rootwait earlyprintk root=/dev/mmcblk0p2";
+ };
+
+ aliases {
+ ethernet0 = "/soc/ethernet@ff800000";
+ serial0 = "/soc/serial0@ffc02000";
+ serial1 = "/soc/serial1@ffc02100";
+ timer0 = "/soc/timer0@ffc02700";
+ timer1 = "/soc/timer1@ffc02800";
+ timer2 = "/soc/timer2@ffd00000";
+ timer3 = "/soc/timer3@ffd00100";
+ };
+
+ memory {
+ device_type = "memory";
+ reg = <0x0 0x40000000>;
+ };
+
+ cpus {
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+
+ cpu@0 {
+ compatible = "arm,cortex-a9";
+ device_type = "cpu";
+ reg = <0x0>;
+ next-level-cache = <0x1>;
+ };
+
+ cpu@1 {
+ compatible = "arm,cortex-a9";
+ device_type = "cpu";
+ reg = <0x1>;
+ next-level-cache = <0x1>;
+ };
+ };
+
+ intc@ffffd000 {
+ compatible = "arm,cortex-a9-gic";
+ #interrupt-cells = <0x3>;
+ interrupt-controller;
+ reg = <0xffffd000 0x1000 0xffffc100 0x100>;
+ linux,phandle = <0x2>;
+ phandle = <0x2>;
+ };
+
+ soc {
+ #address-cells = <0x1>;
+ #size-cells = <0x1>;
+ compatible = "simple-bus";
+ device_type = "soc";
+ interrupt-parent = <0x2>;
+ ranges;
+
+ clkmgr@ffd04000 {
+ compatible = "altr,clk-mgr";
+ reg = <0xffd04000 0x1000>;
+
+ clocks {
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+
+ cb_intosc_hs_div2_clk {
+ #clock-cells = <0x0>;
+ compatible = "fixed-clock";
+ linux,phandle = <0xb>;
+ phandle = <0xb>;
+ };
+
+ cb_intosc_ls_clk {
+ #clock-cells = <0x0>;
+ compatible = "fixed-clock";
+ linux,phandle = <0x4>;
+ phandle = <0x4>;
+ };
+
+ f2s_free_clk {
+ #clock-cells = <0x0>;
+ compatible = "fixed-clock";
+ linux,phandle = <0x5>;
+ phandle = <0x5>;
+ };
+
+ osc1 {
+ #clock-cells = <0x0>;
+ compatible = "fixed-clock";
+ clock-frequency = <0x17d7840>;
+ linux,phandle = <0x3>;
+ phandle = <0x3>;
+ };
+
+ main_pll {
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-pll-clock";
+ clocks = <0x3 0x4 0x5>;
+ reg = <0x40>;
+ linux,phandle = <0x6>;
+ phandle = <0x6>;
+
+ main_mpu_base_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <0x6>;
+ div-reg = <0x140 0x0 0xb>;
+ linux,phandle = <0x9>;
+ phandle = <0x9>;
+ };
+
+ main_noc_base_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <0x6>;
+ div-reg = <0x144 0x0 0xb>;
+ linux,phandle = <0xc>;
+ phandle = <0xc>;
+ };
+
+ main_emaca_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <0x6>;
+ reg = <0x68>;
+ };
+
+ main_emacb_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <0x6>;
+ reg = <0x6c>;
+ };
+
+ main_emac_ptp_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <0x6>;
+ reg = <0x70>;
+ };
+
+ main_gpio_db_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <0x6>;
+ reg = <0x74>;
+ };
+
+ main_sdmmc_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <0x6>;
+ reg = <0x78>;
+ linux,phandle = <0x10>;
+ phandle = <0x10>;
+ };
+
+ main_s2f_usr0_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <0x6>;
+ reg = <0x7c>;
+ };
+
+ main_s2f_usr1_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <0x6>;
+ reg = <0x80>;
+ linux,phandle = <0xe>;
+ phandle = <0xe>;
+ };
+
+ main_hmc_pll_ref_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <0x6>;
+ reg = <0x84>;
+ };
+
+ main_periph_ref_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <0x6>;
+ reg = <0x9c>;
+ linux,phandle = <0x7>;
+ phandle = <0x7>;
+ };
+ };
+
+ periph_pll {
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-pll-clock";
+ clocks = <0x3 0x4 0x5 0x7>;
+ reg = <0xc0>;
+ linux,phandle = <0x8>;
+ phandle = <0x8>;
+
+ peri_mpu_base_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <0x8>;
+ div-reg = <0x140 0x10 0xb>;
+ linux,phandle = <0xa>;
+ phandle = <0xa>;
+ };
+
+ peri_noc_base_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <0x8>;
+ div-reg = <0x144 0x10 0xb>;
+ linux,phandle = <0xd>;
+ phandle = <0xd>;
+ };
+
+ peri_emaca_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <0x8>;
+ reg = <0xe8>;
+ };
+
+ peri_emacb_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <0x8>;
+ reg = <0xec>;
+ };
+
+ peri_emac_ptp_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <0x8>;
+ reg = <0xf0>;
+ };
+
+ peri_gpio_db_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <0x8>;
+ reg = <0xf4>;
+ };
+
+ peri_sdmmc_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <0x8>;
+ reg = <0xf8>;
+ linux,phandle = <0x11>;
+ phandle = <0x11>;
+ };
+
+ peri_s2f_usr0_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <0x8>;
+ reg = <0xfc>;
+ };
+
+ peri_s2f_usr1_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <0x8>;
+ reg = <0x100>;
+ linux,phandle = <0xf>;
+ phandle = <0xf>;
+ };
+
+ peri_hmc_pll_ref_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <0x8>;
+ reg = <0x104>;
+ };
+ };
+
+ mpu_free_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <0x9 0xa 0x3 0xb 0x5>;
+ reg = <0x60>;
+ linux,phandle = <0x13>;
+ phandle = <0x13>;
+ };
+
+ noc_free_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <0xc 0xd 0x3 0xb 0x5>;
+ reg = <0x64>;
+ linux,phandle = <0x12>;
+ phandle = <0x12>;
+ };
+
+ s2f_user1_free_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <0xe 0xf 0x3 0xb 0x5>;
+ reg = <0x104>;
+ };
+
+ sdmmc_free_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <0x10 0x11 0x3 0xb 0x5>;
+ fixed-divider = <0x4>;
+ reg = <0xf8>;
+ linux,phandle = <0x14>;
+ phandle = <0x14>;
+ };
+
+ l4_sys_free_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <0x12>;
+ fixed-divider = <0x4>;
+ };
+
+ l4_main_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-gate-clk";
+ clocks = <0x12>;
+ div-reg = <0xa8 0x0 0x2>;
+ clk-gate = <0x48 0x1>;
+ linux,phandle = <0x15>;
+ phandle = <0x15>;
+ };
+
+ l4_mp_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-gate-clk";
+ clocks = <0x12>;
+ div-reg = <0xa8 0x8 0x2>;
+ clk-gate = <0x48 0x2>;
+ linux,phandle = <0x16>;
+ phandle = <0x16>;
+ };
+
+ l4_sp_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-gate-clk";
+ clocks = <0x12>;
+ div-reg = <0xa8 0x10 0x2>;
+ clk-gate = <0x48 0x3>;
+ };
+
+ mpu_periph_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-gate-clk";
+ clocks = <0x13>;
+ fixed-divider = <0x4>;
+ clk-gate = <0x48 0x0>;
+ };
+
+ sdmmc_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-gate-clk";
+ clocks = <0x14>;
+ clk-gate = <0xc8 0x5>;
+ };
+
+ qspi_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-gate-clk";
+ clocks = <0x15>;
+ clk-gate = <0xc8 0xb>;
+ };
+
+ nand_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-gate-clk";
+ clocks = <0x16>;
+ clk-gate = <0xc8 0xa>;
+ };
+
+ spi_m_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-gate-clk";
+ clocks = <0x15>;
+ clk-gate = <0xc8 0x9>;
+ };
+
+ usb_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-gate-clk";
+ clocks = <0x16>;
+ clk-gate = <0xc8 0x8>;
+ };
+
+ s2f_usr1_clk {
+ #clock-cells = <0x0>;
+ compatible = "altr,socfpga-a10-gate-clk";
+ clocks = <0xf>;
+ clk-gate = <0xc8 0x6>;
+ };
+ };
+ };
+
+ l2-cache@fffff000 {
+ compatible = "arm,pl310-cache";
+ reg = <0xfffff000 0x1000>;
+ interrupts = <0x0 0x12 0x4>;
+ cache-unified;
+ cache-level = <0x2>;
+ linux,phandle = <0x1>;
+ phandle = <0x1>;
+ };
+
+ dwmmc0@ff808000 {
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+ compatible = "altr,socfpga-dw-mshc";
+ reg = <0xff808000 0x1000>;
+ interrupts = <0x0 0x62 0x4>;
+ fifo-depth = <0x400>;
+ status = "okay";
+ num-slots = <0x1>;
+ supports-highspeed;
+ broken-cd;
+ altr,dw-mshc-ciu-div = <0x3>;
+ altr,dw-mshc-sdr-timing = <0x0 0x3>;
+ clocks = <0x16 0x14>;
+ clock-names = "biu", "ciu";
+ clock-freq-min-max = <0x61a80 0x17d7840>;
+ pwr-en = <0x0>;
+ clock-frequency = <0x17d7840>;
+
+ slot@0 {
+ reg = <0x0>;
+ bus-width = <0x4>;
+ };
+ };
+
+ rstmgr@ffd05000 {
+ #reset-cells = <0x1>;
+ compatible = "altr,rst-mgr";
+ reg = <0xffd05000 0x100>;
+ };
+
+ sysmgr@ffd06000 {
+ compatible = "altr,sys-mgr", "syscon";
+ reg = <0xffd06000 0x300>;
+ cpu1-start-addr = <0xffd06230>;
+ };
+
+ timer@ffffc600 {
+ compatible = "arm,cortex-a9-twd-timer";
+ reg = <0xffffc600 0x100>;
+ interrupts = <0x1 0xd 0xf04>;
+ clock-frequency = <0x5f5e100>;
+ };
+
+ ethernet@ff800000 {
+ compatible = "altr,socfpga-stmmac", "snps,dwmac-3.72a", "snps,dwmac";
+ reg = <0xff800000 0x2000>;
+ interrupts = <0x0 0x5c 0x4>;
+ interrupt-names = "macirq";
+ mac-address = [00 00 00 00 00 00];
+ clocks = <0x16>;
+ clock-names = "stmmaceth";
+ status = "okay";
+ phy-mode = "rgmii";
+ phy-addr = <0xffffffff>;
+ snps,max-mtu = <0x0>;
+ };
+
+ timer0@ffc02700 {
+ compatible = "snps,dw-apb-timer-sp";
+ interrupts = <0x0 0x73 0x4>;
+ reg = <0xffc02700 0x100>;
+ clock-frequency = <0x17d7840>;
+ };
+
+ timer1@ffc02800 {
+ compatible = "snps,dw-apb-timer-sp";
+ interrupts = <0x0 0x74 0x4>;
+ reg = <0xffc02800 0x100>;
+ clock-frequency = <0x17d7840>;
+ };
+
+ timer2@ffd00000 {
+ compatible = "snps,dw-apb-timer-osc";
+ interrupts = <0x0 0x75 0x4>;
+ reg = <0xffd00000 0x100>;
+ clock-frequency = <0x17d7840>;
+ };
+
+ timer3@ffd00100 {
+ compatible = "snps,dw-apb-timer-osc";
+ interrupts = <0x0 0x76 0x4>;
+ reg = <0xffd01000 0x100>;
+ clock-frequency = <0x17d7840>;
+ };
+
+ serial0@ffc02000 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0xffc02000 0x100>;
+ interrupts = <0x0 0x6e 0x4>;
+ reg-shift = <0x2>;
+ reg-io-width = <0x4>;
+ status = "okay";
+ clock-frequency = <0x5f5e100>;
+ };
+
+ serial1@ffc02100 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0xffc02100 0x100>;
+ interrupts = <0x0 0x6f 0x4>;
+ reg-shift = <0x2>;
+ reg-io-width = <0x4>;
+ status = "okay";
+ clock-frequency = <0x5f5e100>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/socfpga_arria5_socdk.dts b/arch/arm/boot/dts/socfpga_arria5_socdk.dts
index aac4feea86f3..1bb3a2450773 100644
--- a/arch/arm/boot/dts/socfpga_arria5_socdk.dts
+++ b/arch/arm/boot/dts/socfpga_arria5_socdk.dts
@@ -107,6 +107,14 @@
i2c-sda-falling-time-ns = <5000>;
i2c-scl-falling-time-ns = <5000>;
+ lcd: lcd@28 {
+ compatible = "newhaven,nhd-0216k3z-nsw-bbw";
+ reg = <0x28>;
+ height = <2>;
+ width = <16>;
+ brightness = <8>;
+ };
+
eeprom@51 {
compatible = "atmel,24c32";
reg = <0x51>;
diff --git a/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts b/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts
index 155829f9eba1..44f0644317dc 100644
--- a/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts
+++ b/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts
@@ -111,6 +111,14 @@
i2c-sda-falling-time-ns = <5000>;
i2c-scl-falling-time-ns = <5000>;
+ lcd: lcd@28 {
+ compatible = "newhaven,nhd-0216k3z-nsw-bbw";
+ reg = <0x28>;
+ height = <2>;
+ width = <16>;
+ brightness = <8>;
+ };
+
eeprom@51 {
compatible = "atmel,24c32";
reg = <0x51>;
diff --git a/arch/arm/boot/dts/socfpga_cyclone5_trcom.dts b/arch/arm/boot/dts/socfpga_cyclone5_trcom.dts
new file mode 100644
index 000000000000..eec02323ae44
--- /dev/null
+++ b/arch/arm/boot/dts/socfpga_cyclone5_trcom.dts
@@ -0,0 +1,150 @@
+/*
+ * Copyright Altera Corporation (C) 2012,2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "socfpga_cyclone5.dtsi"
+
+/ {
+ model = "Altera SOCFPGA Cyclone V SoC Development Kit";
+ compatible = "altr,socfpga-cyclone5", "altr,socfpga";
+
+ chosen {
+ bootargs = "console=ttyS0,115200";
+ };
+
+ memory {
+ name = "memory";
+ device_type = "memory";
+ reg = <0x0 0x40000000>; /* 1GB */
+ };
+
+ aliases {
+ /* this allow the ethaddr uboot environmnet variable contents
+ * to be added to the gmac1 device tree blob.
+ */
+ ethernet0 = &gmac1;
+ };
+
+ regulator_3_3v: 3-3-v-regulator {
+ compatible = "regulator-fixed";
+ regulator-name = "3.3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ leds {
+ compatible = "gpio-leds";
+ hps0 {
+ label = "hps_led0";
+ gpios = <&portb 15 1>;
+ };
+
+ hps1 {
+ label = "hps_led1";
+ gpios = <&portb 14 1>;
+ };
+
+ hps2 {
+ label = "hps_led2";
+ gpios = <&portb 13 1>;
+ };
+
+ hps3 {
+ label = "hps_led3";
+ gpios = <&portb 12 1>;
+ };
+ };
+};
+
+&can0 {
+ status = "okay";
+};
+
+&gmac1 {
+ status = "okay";
+ phy-mode = "rgmii";
+
+ rxd0-skew-ps = <0>;
+ rxd1-skew-ps = <0>;
+ rxd2-skew-ps = <0>;
+ rxd3-skew-ps = <0>;
+ txen-skew-ps = <0>;
+ txc-skew-ps = <2600>;
+ rxdv-skew-ps = <0>;
+ rxc-skew-ps = <2000>;
+ max-frame-size = <3800>;
+};
+
+&gpio0 {
+ status = "okay";
+};
+
+&gpio1 {
+ status = "okay";
+};
+
+&gpio2 {
+ status = "okay";
+};
+
+&i2c0 {
+ status = "okay";
+ clock-frequency = <100000>;
+
+ /*
+ * adjust the falling times to decrease the i2c frequency to 50Khz
+ * because the LCD module does not work at the standard 100Khz
+ */
+ i2c-sda-falling-time-ns = <5000>;
+ i2c-scl-falling-time-ns = <5000>;
+
+ lcd: lcd@28 {
+ compatible = "newhaven,nhd-0216k3z-nsw-bbw";
+ reg = <0x28>;
+ height = <2>;
+ width = <16>;
+ brightness = <8>;
+ };
+
+ eeprom@51 {
+ compatible = "atmel,24c32";
+ reg = <0x51>;
+ pagesize = <32>;
+ };
+
+ rtc@68 {
+ compatible = "dallas,ds1339";
+ reg = <0x68>;
+ };
+};
+
+&mmc0 {
+ cd-gpios = <&portb 18 0>;
+ vmmc-supply = <&regulator_3_3v>;
+ vqmmc-supply = <&regulator_3_3v>;
+};
+
+&uart0 {
+ status = "okay";
+};
+
+&osc1 {
+ clock-frequency = <19200000>;
+};
+
+&nand0 {
+ status = "okay";
+};
+
diff --git a/arch/arm/mach-socfpga/Kconfig b/arch/arm/mach-socfpga/Kconfig
index 4adb901dd5eb..c1681da181d0 100644
--- a/arch/arm/mach-socfpga/Kconfig
+++ b/arch/arm/mach-socfpga/Kconfig
@@ -11,6 +11,13 @@ menuconfig ARCH_SOCFPGA
select HAVE_ARM_TWD if SMP
select MFD_SYSCON
select PCI_DOMAINS if PCI
+ select ARM_ERRATA_754322
+ select ARM_ERRATA_764369 if SMP
+ select ARM_ERRATA_775420
+ select PL310_ERRATA_588369
+ select PL310_ERRATA_727915
+ select PL310_ERRATA_753970 if PL310
+ select PL310_ERRATA_769419
if ARCH_SOCFPGA
config SOCFPGA_SUSPEND
@@ -19,3 +26,8 @@ config SOCFPGA_SUSPEND
Select this if you want to enable Suspend-to-RAM on SOCFPGA
platforms.
endif
+config FPGADMA
+ tristate "FPGA DMA FIFO driver"
+ depends on DMA_ENGINE
+ help
+ Sample FPGA DMA driver, for testing with special FPGA FIFO image
diff --git a/arch/arm/mach-socfpga/Makefile b/arch/arm/mach-socfpga/Makefile
index 9ec31fad7136..01ad570d7c8e 100644
--- a/arch/arm/mach-socfpga/Makefile
+++ b/arch/arm/mach-socfpga/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_SMP) += headsmp.o platsmp.o
obj-$(CONFIG_SOCFPGA_SUSPEND) += pm.o self-refresh.o
obj-$(CONFIG_EDAC_ALTERA_L2C) += l2_cache.o
obj-$(CONFIG_EDAC_ALTERA_OCRAM) += ocram.o
+obj-$(CONFIG_FPGADMA) += fpga-dma.o
diff --git a/arch/arm/mach-socfpga/core.h b/arch/arm/mach-socfpga/core.h
index 65e1817d8afe..45433a467956 100644
--- a/arch/arm/mach-socfpga/core.h
+++ b/arch/arm/mach-socfpga/core.h
@@ -34,12 +34,15 @@
#define RSTMGR_MPUMODRST_CPU1 0x2 /* CPU1 Reset */
-extern void socfpga_init_clocks(void);
-extern void socfpga_sysmgr_init(void);
void socfpga_init_l2_ecc(void);
void socfpga_init_ocram_ecc(void);
void socfpga_init_arria10_l2_ecc(void);
void socfpga_init_arria10_ocram_ecc(void);
+#define SYSMGR_SILICON_ID1_OFFSET 0x0
+#define SYSMGR_SILICON_ID1_REV_SHIFT 0
+#define SYSMGR_SILICON_ID1_REV_MASK 0x0000FFFF
+#define SYSMGR_SILICON_ID1_ID_SHIFT 16
+#define SYSMGR_SILICON_ID1_ID_MASK 0xFFFF0000
extern void __iomem *sys_manager_base_addr;
extern void __iomem *rst_manager_base_addr;
@@ -54,4 +57,7 @@ extern unsigned long socfpga_cpu1start_addr;
#define SOCFPGA_SCU_VIRT_BASE 0xfee00000
+/* Clock manager defines */
+#define SOCFPGA_ENABLE_PLL_REG 0xA0
+
#endif
diff --git a/arch/arm/mach-socfpga/fpga-dma.c b/arch/arm/mach-socfpga/fpga-dma.c
new file mode 100644
index 000000000000..23ed0a0d8e28
--- /dev/null
+++ b/arch/arm/mach-socfpga/fpga-dma.c
@@ -0,0 +1,689 @@
+/*
+ * FPGA DMA transfer module
+ *
+ * Copyright Altera Corporation (C) 2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/cdev.h>
+#include <linux/completion.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/pm.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+
+/****************************************************************************/
+
+static unsigned int max_burst_words = 16;
+module_param(max_burst_words, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(max_burst_words, "Size of a burst in words "
+ "(in this case a word is 64 bits)");
+
+static int timeout = 1000;
+module_param(timeout, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(timeout, "Transfer Timeout in msec (default: 1000), "
+ "Pass -1 for infinite timeout");
+
+#define ALT_FPGADMA_DATA_WRITE 0x00
+#define ALT_FPGADMA_DATA_READ 0x08
+
+#define ALT_FPGADMA_CSR_WR_WTRMK 0x00
+#define ALT_FPGADMA_CSR_RD_WTRMK 0x04
+#define ALT_FPGADMA_CSR_BURST 0x08
+#define ALT_FPGADMA_CSR_FIFO_STATUS 0x0C
+#define ALT_FPGADMA_CSR_DATA_WIDTH 0x10
+#define ALT_FPGADMA_CSR_FIFO_DEPTH 0x14
+#define ALT_FPGADMA_CSR_FIFO_CLEAR 0x18
+#define ALT_FPGADMA_CSR_ZERO 0x1C
+
+#define ALT_FPGADMA_CSR_BURST_TX_SINGLE (1 << 0)
+#define ALT_FPGADMA_CSR_BURST_TX_BURST (1 << 1)
+#define ALT_FPGADMA_CSR_BURST_RX_SINGLE (1 << 2)
+#define ALT_FPGADMA_CSR_BURST_RX_BURST (1 << 3)
+
+#define ALT_FPGADMA_FIFO_FULL (1 << 25)
+#define ALT_FPGADMA_FIFO_EMPTY (1 << 24)
+#define ALT_FPGADMA_FIFO_USED_MASK ((1 << 24)-1)
+
+struct fpga_dma_pdata {
+
+ struct platform_device *pdev;
+
+ struct dentry *root;
+
+ unsigned int data_reg_phy;
+ void __iomem *data_reg;
+ void __iomem *csr_reg;
+
+ unsigned int fifo_size_bytes;
+ unsigned int fifo_depth;
+ unsigned int data_width;
+ unsigned int data_width_bytes;
+ unsigned char *read_buf;
+ unsigned char *write_buf;
+
+ struct dma_chan *txchan;
+ struct dma_chan *rxchan;
+ dma_addr_t tx_dma_addr;
+ dma_addr_t rx_dma_addr;
+ dma_cookie_t rx_cookie;
+ dma_cookie_t tx_cookie;
+};
+
+static DECLARE_COMPLETION(dma_read_complete);
+static DECLARE_COMPLETION(dma_write_complete);
+
+#define IS_DMA_READ (true)
+#define IS_DMA_WRITE (false)
+
+static int fpga_dma_dma_start_rx(struct platform_device *pdev,
+ unsigned datalen, unsigned char *databuf,
+ u32 burst_size);
+static int fpga_dma_dma_start_tx(struct platform_device *pdev,
+ unsigned datalen, unsigned char *databuf,
+ u32 burst_size);
+
+/* --------------------------------------------------------------------- */
+
+static void dump_csr(struct fpga_dma_pdata *pdata)
+{
+ dev_info(&pdata->pdev->dev, "ALT_FPGADMA_CSR_WR_WTRMK %08x\n",
+ readl(pdata->csr_reg + ALT_FPGADMA_CSR_WR_WTRMK));
+ dev_info(&pdata->pdev->dev, "ALT_FPGADMA_CSR_RD_WTRMK %08x\n",
+ readl(pdata->csr_reg + ALT_FPGADMA_CSR_RD_WTRMK));
+ dev_info(&pdata->pdev->dev, "ALT_FPGADMA_CSR_BURST %08x\n",
+ readl(pdata->csr_reg + ALT_FPGADMA_CSR_BURST));
+ dev_info(&pdata->pdev->dev, "ALT_FPGADMA_CSR_FIFO_STATUS %08x\n",
+ readl(pdata->csr_reg + ALT_FPGADMA_CSR_FIFO_STATUS));
+ dev_info(&pdata->pdev->dev, "ALT_FPGADMA_CSR_DATA_WIDTH %08x\n",
+ readl(pdata->csr_reg + ALT_FPGADMA_CSR_DATA_WIDTH));
+ dev_info(&pdata->pdev->dev, "ALT_FPGADMA_CSR_FIFO_DEPTH %08x\n",
+ readl(pdata->csr_reg + ALT_FPGADMA_CSR_FIFO_DEPTH));
+ dev_info(&pdata->pdev->dev, "ALT_FPGADMA_CSR_ZERO %08x\n",
+ readl(pdata->csr_reg + ALT_FPGADMA_CSR_ZERO));
+}
+
+/* --------------------------------------------------------------------- */
+
+static void recalc_burst_and_words(struct fpga_dma_pdata *pdata,
+ int *burst_size, int *num_words)
+{
+ /* adjust size and maxburst so that total bytes transferred
+ is a multiple of burst length and width */
+ if (*num_words < max_burst_words) {
+ /* we have only a few words left, make it our burst size */
+ *burst_size = *num_words;
+ } else {
+ /* here we may not transfer all words to FIFO, but next
+ call will pick them up... */
+ *num_words = max_burst_words * (*num_words / max_burst_words);
+ *burst_size = max_burst_words;
+ }
+}
+
+static int word_to_bytes(struct fpga_dma_pdata *pdata, int num_bytes)
+{
+ return (num_bytes + pdata->data_width_bytes - 1)
+ / pdata->data_width_bytes;
+}
+
+static ssize_t dbgfs_write_dma(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct fpga_dma_pdata *pdata = file->private_data;
+ int ret = 0;
+ int bytes_to_transfer;
+ int num_words;
+ u32 burst_size;
+ int pad_index;
+
+ *ppos = 0;
+
+ /* get user data into kernel buffer */
+ bytes_to_transfer = simple_write_to_buffer(pdata->write_buf,
+ pdata->fifo_size_bytes, ppos,
+ user_buf, count);
+ pad_index = bytes_to_transfer;
+
+ num_words = word_to_bytes(pdata, bytes_to_transfer);
+ recalc_burst_and_words(pdata, &burst_size, &num_words);
+ /* we sometimes send more than asked for, padded with zeros */
+ bytes_to_transfer = num_words * pdata->data_width_bytes;
+ for (; pad_index < bytes_to_transfer; pad_index++)
+ pdata->write_buf[pad_index] = 0;
+
+ ret = fpga_dma_dma_start_tx(pdata->pdev,
+ bytes_to_transfer, pdata->write_buf,
+ burst_size);
+ if (ret) {
+ dev_err(&pdata->pdev->dev, "Error starting TX DMA %d\n", ret);
+ return ret;
+ }
+
+ if (!wait_for_completion_timeout(&dma_write_complete,
+ msecs_to_jiffies(timeout))) {
+ dev_err(&pdata->pdev->dev, "Timeout waiting for TX DMA!\n");
+ dev_err(&pdata->pdev->dev,
+ "count %d burst_size %d num_words %d bytes_to_transfer %d\n",
+ count, burst_size, num_words, bytes_to_transfer);
+ dmaengine_terminate_all(pdata->txchan);
+ return -ETIMEDOUT;
+ }
+
+ return bytes_to_transfer;
+}
+
+static ssize_t dbgfs_read_dma(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct fpga_dma_pdata *pdata = file->private_data;
+ int ret;
+ int num_words;
+ int num_bytes;
+ u32 burst_size;
+
+ num_words = readl(pdata->csr_reg + ALT_FPGADMA_CSR_FIFO_STATUS);
+ num_words &= ALT_FPGADMA_FIFO_USED_MASK;
+
+ num_bytes = num_words * pdata->data_width_bytes;
+ if (num_bytes > count) {
+ dev_dbg(&pdata->pdev->dev,
+ "dbgfs_read_dma num_bytes %d > count %d\n",
+ num_bytes, count);
+ num_bytes = count;
+ num_words = num_bytes / (pdata->data_width_bytes);
+ }
+ if (num_bytes > pdata->fifo_size_bytes) {
+ dev_dbg(&pdata->pdev->dev,
+ "dbgfs_read_dma num_bytes %d > pdata->fifo_size_bytes %d\n",
+ num_bytes, pdata->fifo_size_bytes);
+ num_bytes = pdata->fifo_size_bytes;
+ num_words = num_bytes / (pdata->data_width_bytes);
+ }
+
+ recalc_burst_and_words(pdata, &burst_size, &num_words);
+ num_bytes = num_words * pdata->data_width_bytes;
+
+ if (num_bytes > 0) {
+ ret = fpga_dma_dma_start_rx(pdata->pdev, num_bytes,
+ pdata->read_buf, burst_size);
+ if (ret) {
+ dev_err(&pdata->pdev->dev,
+ "Error starting RX DMA %d\n", ret);
+ return ret;
+ }
+
+ if (!wait_for_completion_timeout(&dma_read_complete,
+ msecs_to_jiffies(timeout))) {
+ dev_err(&pdata->pdev->dev,
+ "Timeout waiting for RX DMA!\n");
+ dmaengine_terminate_all(pdata->rxchan);
+ return -ETIMEDOUT;
+ }
+ *ppos = 0;
+ }
+ return simple_read_from_buffer(user_buf, count, ppos,
+ pdata->read_buf, num_bytes);
+}
+
+static const struct file_operations dbgfs_dma_fops = {
+ .write = dbgfs_write_dma,
+ .read = dbgfs_read_dma,
+ .open = simple_open,
+ .llseek = no_llseek,
+};
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t dbgfs_read_csr(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct fpga_dma_pdata *pdata = file->private_data;
+ dump_csr(pdata);
+ return 0;
+}
+
+static const struct file_operations dbgfs_csr_fops = {
+ .read = dbgfs_read_csr,
+ .open = simple_open,
+ .llseek = no_llseek,
+};
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t dbgfs_write_clear(struct file *file,
+ const char __user *user_buf, size_t count,
+ loff_t *ppos)
+{
+ struct fpga_dma_pdata *pdata = file->private_data;
+ writel(1, pdata->csr_reg + ALT_FPGADMA_CSR_FIFO_CLEAR);
+ return count;
+}
+
+static const struct file_operations dbgfs_clear_fops = {
+ .write = dbgfs_write_clear,
+ .open = simple_open,
+ .llseek = no_llseek,
+};
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t dbgfs_write_wrwtrmk(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct fpga_dma_pdata *pdata = file->private_data;
+ char buf[32];
+ unsigned long val;
+ int ret;
+
+ memset(buf, 0, sizeof(buf));
+
+ if (copy_from_user(buf, user_buf, min(count, (sizeof(buf) - 1))))
+ return -EFAULT;
+
+ ret = kstrtoul(buf, 16, &val);
+ if (ret)
+ return ret;
+
+ writel(val, pdata->csr_reg + ALT_FPGADMA_CSR_WR_WTRMK);
+ return count;
+}
+
+static const struct file_operations dbgfs_wrwtrmk_fops = {
+ .write = dbgfs_write_wrwtrmk,
+ .open = simple_open,
+ .llseek = no_llseek,
+};
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t dbgfs_write_rdwtrmk(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct fpga_dma_pdata *pdata = file->private_data;
+ char buf[32];
+ int ret;
+ unsigned long val;
+
+ memset(buf, 0, sizeof(buf));
+
+ if (copy_from_user(buf, user_buf, min(count, (sizeof(buf) - 1))))
+ return -EFAULT;
+
+ ret = kstrtoul(buf, 16, &val);
+ if (ret)
+ return ret;
+
+ writel(val, pdata->csr_reg + ALT_FPGADMA_CSR_RD_WTRMK);
+ return count;
+}
+
+static const struct file_operations dbgfs_rdwtrmk_fops = {
+ .write = dbgfs_write_rdwtrmk,
+ .open = simple_open,
+ .llseek = no_llseek,
+};
+
+/* --------------------------------------------------------------------- */
+
+static int fpga_dma_register_dbgfs(struct fpga_dma_pdata *pdata)
+{
+ struct dentry *d;
+
+ d = debugfs_create_dir("fpga_dma", NULL);
+ if (IS_ERR(d))
+ return PTR_ERR(d);
+ if (!d) {
+ dev_err(&pdata->pdev->dev, "Failed to initialize debugfs\n");
+ return -ENOMEM;
+ }
+
+ pdata->root = d;
+
+ debugfs_create_file("dma", S_IWUSR | S_IRUGO, pdata->root, pdata,
+ &dbgfs_dma_fops);
+
+ debugfs_create_file("csr", S_IRUGO, pdata->root, pdata,
+ &dbgfs_csr_fops);
+
+ debugfs_create_file("clear", S_IWUSR, pdata->root, pdata,
+ &dbgfs_clear_fops);
+
+ debugfs_create_file("wrwtrmk", S_IWUSR, pdata->root, pdata,
+ &dbgfs_wrwtrmk_fops);
+
+ debugfs_create_file("rdwtrmk", S_IWUSR, pdata->root, pdata,
+ &dbgfs_rdwtrmk_fops);
+
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void fpga_dma_dma_rx_done(void *arg)
+{
+ complete(&dma_read_complete);
+}
+
+static void fpga_dma_dma_tx_done(void *arg)
+{
+ complete(&dma_write_complete);
+}
+
+static void fpga_dma_dma_cleanup(struct platform_device *pdev,
+ unsigned datalen, bool do_read)
+{
+ struct fpga_dma_pdata *pdata = platform_get_drvdata(pdev);
+ if (do_read)
+ dma_unmap_single(&pdev->dev, pdata->rx_dma_addr,
+ datalen, DMA_FROM_DEVICE);
+ else
+ dma_unmap_single(&pdev->dev, pdata->tx_dma_addr,
+ datalen, DMA_TO_DEVICE);
+}
+
+static int fpga_dma_dma_start_rx(struct platform_device *pdev,
+ unsigned datalen, unsigned char *databuf,
+ u32 burst_size)
+{
+ struct fpga_dma_pdata *pdata = platform_get_drvdata(pdev);
+ struct dma_chan *dmachan;
+ struct dma_slave_config dmaconf;
+ struct dma_async_tx_descriptor *dmadesc = NULL;
+
+ int num_words;
+
+ num_words = word_to_bytes(pdata, datalen);
+
+ dmachan = pdata->rxchan;
+ memset(&dmaconf, 0, sizeof(dmaconf));
+ dmaconf.direction = DMA_DEV_TO_MEM;
+ dmaconf.src_addr = pdata->data_reg_phy + ALT_FPGADMA_DATA_READ;
+ dmaconf.src_addr_width = 8;
+ dmaconf.src_maxburst = burst_size;
+
+ pdata->rx_dma_addr = dma_map_single(&pdev->dev,
+ databuf, datalen, DMA_FROM_DEVICE);
+ if (dma_mapping_error(&pdev->dev, pdata->rx_dma_addr)) {
+ dev_err(&pdev->dev, "dma_map_single for RX failed\n");
+ return -EINVAL;
+ }
+
+ /* set up slave config */
+ dmaengine_slave_config(dmachan, &dmaconf);
+
+ /* get dmadesc */
+ dmadesc = dmaengine_prep_slave_single(dmachan,
+ pdata->rx_dma_addr,
+ datalen,
+ dmaconf.direction,
+ DMA_PREP_INTERRUPT);
+ if (!dmadesc) {
+ fpga_dma_dma_cleanup(pdev, datalen, IS_DMA_READ);
+ return -ENOMEM;
+ }
+ dmadesc->callback = fpga_dma_dma_rx_done;
+ dmadesc->callback_param = pdata;
+
+ /* start DMA */
+ pdata->rx_cookie = dmaengine_submit(dmadesc);
+ if (dma_submit_error(pdata->rx_cookie))
+ dev_err(&pdev->dev, "rx_cookie error on dmaengine_submit\n");
+ dma_async_issue_pending(dmachan);
+
+ return 0;
+}
+
+static int fpga_dma_dma_start_tx(struct platform_device *pdev,
+ unsigned datalen, unsigned char *databuf,
+ u32 burst_size)
+{
+ struct fpga_dma_pdata *pdata = platform_get_drvdata(pdev);
+ struct dma_chan *dmachan;
+ struct dma_slave_config dmaconf;
+ struct dma_async_tx_descriptor *dmadesc = NULL;
+
+ int num_words;
+
+ num_words = word_to_bytes(pdata, datalen);
+
+ dmachan = pdata->txchan;
+ memset(&dmaconf, 0, sizeof(dmaconf));
+ dmaconf.direction = DMA_MEM_TO_DEV;
+ dmaconf.dst_addr = pdata->data_reg_phy + ALT_FPGADMA_DATA_WRITE;
+ dmaconf.dst_addr_width = 8;
+ dmaconf.dst_maxburst = burst_size;
+ pdata->tx_dma_addr = dma_map_single(&pdev->dev,
+ databuf, datalen, DMA_TO_DEVICE);
+ if (dma_mapping_error(&pdev->dev, pdata->tx_dma_addr)) {
+ dev_err(&pdev->dev, "dma_map_single for TX failed\n");
+ return -EINVAL;
+ }
+
+ /* set up slave config */
+ dmaengine_slave_config(dmachan, &dmaconf);
+
+ /* get dmadesc */
+ dmadesc = dmaengine_prep_slave_single(dmachan,
+ pdata->tx_dma_addr,
+ datalen,
+ dmaconf.direction,
+ DMA_PREP_INTERRUPT);
+ if (!dmadesc) {
+ fpga_dma_dma_cleanup(pdev, datalen, IS_DMA_WRITE);
+ return -ENOMEM;
+ }
+ dmadesc->callback = fpga_dma_dma_tx_done;
+ dmadesc->callback_param = pdata;
+
+ /* start DMA */
+ pdata->tx_cookie = dmaengine_submit(dmadesc);
+ if (dma_submit_error(pdata->tx_cookie))
+ dev_err(&pdev->dev, "tx_cookie error on dmaengine_submit\n");
+ dma_async_issue_pending(dmachan);
+
+ return 0;
+}
+
+static void fpga_dma_dma_shutdown(struct fpga_dma_pdata *pdata)
+{
+ if (pdata->txchan) {
+ dmaengine_terminate_all(pdata->txchan);
+ dma_release_channel(pdata->txchan);
+ }
+ if (pdata->rxchan) {
+ dmaengine_terminate_all(pdata->rxchan);
+ dma_release_channel(pdata->rxchan);
+ }
+ pdata->rxchan = pdata->txchan = NULL;
+}
+
+static int fpga_dma_dma_init(struct fpga_dma_pdata *pdata)
+{
+ struct platform_device *pdev = pdata->pdev;
+
+ pdata->txchan = dma_request_slave_channel(&pdev->dev, "tx");
+ if (pdata->txchan)
+ dev_dbg(&pdev->dev, "TX channel %s %d selected\n",
+ dma_chan_name(pdata->txchan), pdata->txchan->chan_id);
+ else
+ dev_err(&pdev->dev, "could not get TX dma channel\n");
+
+ pdata->rxchan = dma_request_slave_channel(&pdev->dev, "rx");
+ if (pdata->rxchan)
+ dev_dbg(&pdev->dev, "RX channel %s %d selected\n",
+ dma_chan_name(pdata->rxchan), pdata->rxchan->chan_id);
+ else
+ dev_err(&pdev->dev, "could not get RX dma channel\n");
+
+ if (!pdata->rxchan && !pdata->txchan)
+ /* both channels not there, maybe it's
+ bcs dma isn't loaded... */
+ return -EPROBE_DEFER;
+
+ if (!pdata->rxchan || !pdata->txchan)
+ return -ENOMEM;
+
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void __iomem *request_and_map(struct platform_device *pdev,
+ const struct resource *res)
+{
+ void __iomem *ptr;
+
+ if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
+ pdev->name)) {
+ dev_err(&pdev->dev, "unable to request %s\n", res->name);
+ return NULL;
+ }
+
+ ptr = devm_ioremap_nocache(&pdev->dev, res->start, resource_size(res));
+ if (!ptr)
+ dev_err(&pdev->dev, "ioremap_nocache of %s failed!", res->name);
+
+ return ptr;
+}
+
+static int fpga_dma_remove(struct platform_device *pdev)
+{
+ struct fpga_dma_pdata *pdata = platform_get_drvdata(pdev);
+ dev_dbg(&pdev->dev, "fpga_dma_remove\n");
+ debugfs_remove_recursive(pdata->root);
+ fpga_dma_dma_shutdown(pdata);
+ return 0;
+}
+
+static int fpga_dma_probe(struct platform_device *pdev)
+{
+ struct resource *csr_reg, *data_reg;
+ struct fpga_dma_pdata *pdata;
+ int ret;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(struct fpga_dma_pdata),
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ csr_reg = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr");
+ data_reg = platform_get_resource_byname(pdev, IORESOURCE_MEM, "data");
+ if (!csr_reg || !data_reg) {
+ dev_err(&pdev->dev, "registers not completely defined\n");
+ return -EINVAL;
+ }
+
+ pdata->csr_reg = request_and_map(pdev, csr_reg);
+ if (!pdata->csr_reg)
+ return -ENOMEM;
+
+ pdata->data_reg = request_and_map(pdev, data_reg);
+ if (!pdata->data_reg)
+ return -ENOMEM;
+ pdata->data_reg_phy = data_reg->start;
+
+ /* read HW and calculate fifo size in bytes */
+ pdata->fifo_depth = readl(pdata->csr_reg + ALT_FPGADMA_CSR_FIFO_DEPTH);
+ pdata->data_width = readl(pdata->csr_reg + ALT_FPGADMA_CSR_DATA_WIDTH);
+ /* 64-bit bus to FIFO */
+ pdata->data_width_bytes = pdata->data_width / sizeof(u64);
+ pdata->fifo_size_bytes = pdata->fifo_depth * pdata->data_width_bytes;
+
+ pdata->read_buf = devm_kzalloc(&pdev->dev, pdata->fifo_size_bytes,
+ GFP_KERNEL);
+ if (!pdata->read_buf)
+ return -ENOMEM;
+
+ pdata->write_buf = devm_kzalloc(&pdev->dev, pdata->fifo_size_bytes,
+ GFP_KERNEL);
+ if (!pdata->write_buf)
+ return -ENOMEM;
+
+ ret = fpga_dma_register_dbgfs(pdata);
+ if (ret)
+ return ret;
+
+ pdata->pdev = pdev;
+ platform_set_drvdata(pdev, pdata);
+
+ ret = fpga_dma_dma_init(pdata);
+ if (ret) {
+ fpga_dma_remove(pdev);
+ return ret;
+ }
+
+ /* OK almost ready, set up the watermarks */
+ /* we may need to tweak this for single/burst, etc */
+ writel(pdata->fifo_depth - max_burst_words,
+ pdata->csr_reg + ALT_FPGADMA_CSR_WR_WTRMK);
+ /* we use read watermark of 0 so that rx_burst line
+ is always asserted, i.e. no single-only requests */
+ writel(0, pdata->csr_reg + ALT_FPGADMA_CSR_RD_WTRMK);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id fpga_dma_of_match[] = {
+ {.compatible = "altr,fpga-dma",},
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, fpga_dma_of_match);
+#endif
+
+static struct platform_driver fpga_dma_driver = {
+ .probe = fpga_dma_probe,
+ .remove = fpga_dma_remove,
+ .driver = {
+ .name = "fpga_dma",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(fpga_dma_of_match),
+ },
+};
+
+static int __init fpga_dma_init(void)
+{
+ return platform_driver_probe(&fpga_dma_driver, fpga_dma_probe);
+}
+
+static void __exit fpga_dma_exit(void)
+{
+ platform_driver_unregister(&fpga_dma_driver);
+}
+
+late_initcall(fpga_dma_init);
+module_exit(fpga_dma_exit);
+
+MODULE_AUTHOR("Graham Moore (Altera)");
+MODULE_DESCRIPTION("Altera FPGA DMA Example Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-socfpga/socfpga.c b/arch/arm/mach-socfpga/socfpga.c
index dde14f7bf2c3..5bb877e04a54 100644
--- a/arch/arm/mach-socfpga/socfpga.c
+++ b/arch/arm/mach-socfpga/socfpga.c
@@ -31,6 +31,7 @@ void __iomem *sys_manager_base_addr;
void __iomem *rst_manager_base_addr;
void __iomem *sdr_ctl_base_addr;
unsigned long socfpga_cpu1start_addr;
+void __iomem *clkmgr_base_addr;
void __init socfpga_sysmgr_init(void)
{
@@ -51,6 +52,10 @@ void __init socfpga_sysmgr_init(void)
np = of_find_compatible_node(NULL, NULL, "altr,rst-mgr");
rst_manager_base_addr = of_iomap(np, 0);
+ np = of_find_compatible_node(NULL, NULL, "altr,clk-mgr");
+ clkmgr_base_addr = of_iomap(np, 0);
+ WARN_ON(!clkmgr_base_addr);
+
np = of_find_compatible_node(NULL, NULL, "altr,sdr-ctl");
sdr_ctl_base_addr = of_iomap(np, 0);
}
@@ -80,6 +85,9 @@ static void socfpga_cyclone5_restart(enum reboot_mode mode, const char *cmd)
{
u32 temp;
+ /* Turn on all periph PLL clocks */
+ writel(0xffff, clkmgr_base_addr + SOCFPGA_ENABLE_PLL_REG);
+
temp = readl(rst_manager_base_addr + SOCFPGA_RSTMGR_CTRL);
if (mode == REBOOT_HARD)
diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
index 21a715ad8222..65404985f84e 100644
--- a/arch/arm64/Kconfig.platforms
+++ b/arch/arm64/Kconfig.platforms
@@ -226,6 +226,11 @@ config ARCH_STRATIX10
help
This enables support for Altera's Stratix 10 SoCFPGA Family.
+config ARCH_STRATIX10SWVP
+ bool "Altera Stratix10 Software Virtual Platform"
+ help
+ This enables support for Altera Stratix10 Software Virtual Platform
+
config ARCH_TEGRA
bool "NVIDIA Tegra SoC Family"
select ARCH_HAS_RESET_CONTROLLER
diff --git a/arch/arm64/boot/dts/altera/Makefile b/arch/arm64/boot/dts/altera/Makefile
index 68ba0882a8bb..dae9ba404d18 100644
--- a/arch/arm64/boot/dts/altera/Makefile
+++ b/arch/arm64/boot/dts/altera/Makefile
@@ -1 +1,2 @@
-dtb-$(CONFIG_ARCH_STRATIX10) += socfpga_stratix10_socdk.dtb
+dtb-$(CONFIG_ARCH_STRATIX10) += socfpga_stratix10_socdk.dtb socfpga_stratix10_fpga_update.dtb
+dtb-$(CONFIG_ARCH_STRATIX10SWVP) += stratix10_swvp.dtb
diff --git a/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi b/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi
index 6c8bd13d64b8..1072baf452e0 100644
--- a/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi
+++ b/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi
@@ -24,6 +24,19 @@
#address-cells = <2>;
#size-cells = <2>;
+ reserved-memory {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+
+ service_reserved: svcbuffer@0 {
+ compatible = "shared-dma-pool";
+ reg = <0x0 0x0 0x0 0x1000000>;
+ alignment = <0x1000>;
+ no-map;
+ };
+ };
+
cpus {
#address-cells = <1>;
#size-cells = <0>;
@@ -59,10 +72,10 @@
pmu {
compatible = "arm,armv8-pmuv3";
- interrupts = <0 120 8>,
- <0 121 8>,
- <0 122 8>,
- <0 123 8>;
+ interrupts = <0 170 4>,
+ <0 171 4>,
+ <0 172 4>,
+ <0 173 4>;
interrupt-affinity = <&cpu0>,
<&cpu1>,
<&cpu2>,
@@ -93,6 +106,14 @@
interrupt-parent = <&intc>;
ranges = <0 0 0 0xffffffff>;
+ base_fpga_region {
+ #address-cells = <0x1>;
+ #size-cells = <0x1>;
+
+ compatible = "fpga-region";
+ fpga-mgr = <&fpga_mgr>;
+ };
+
clkmgr: clock-controller@ffd10000 {
compatible = "intel,stratix10-clkmgr";
reg = <0xffd10000 0x1000>;
@@ -119,16 +140,27 @@
#clock-cells = <0>;
compatible = "fixed-clock";
};
+
+ qspi_clk: qspi-clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <200000000>;
+ };
+ };
+
+ fpga_mgr: fpga-mgr@0 {
+ compatible = "intel,stratix10-soc-fpga-mgr";
};
gmac0: ethernet@ff800000 {
compatible = "altr,socfpga-stmmac", "snps,dwmac-3.74a", "snps,dwmac";
+ altr,sysmgr-syscon = <&sysmgr 0x44 0>;
reg = <0xff800000 0x2000>;
interrupts = <0 90 4>;
interrupt-names = "macirq";
mac-address = [00 00 00 00 00 00];
- resets = <&rst EMAC0_RESET>;
- reset-names = "stmmaceth";
+ resets = <&rst EMAC0_RESET>, <&rst EMAC0_OCP_RESET>;
+ reset-names = "stmmaceth", "stmmaceth-ocp";
clocks = <&clkmgr STRATIX10_EMAC0_CLK>;
clock-names = "stmmaceth";
tx-fifo-depth = <16384>;
@@ -139,12 +171,13 @@
gmac1: ethernet@ff802000 {
compatible = "altr,socfpga-stmmac", "snps,dwmac-3.74a", "snps,dwmac";
+ altr,sysmgr-syscon = <&sysmgr 0x48 0>;
reg = <0xff802000 0x2000>;
interrupts = <0 91 4>;
interrupt-names = "macirq";
mac-address = [00 00 00 00 00 00];
- resets = <&rst EMAC1_RESET>;
- reset-names = "stmmaceth";
+ resets = <&rst EMAC1_RESET>, <&rst EMAC1_OCP_RESET>;
+ reset-names = "stmmaceth", "stmmaceth-ocp";
clocks = <&clkmgr STRATIX10_EMAC1_CLK>;
clock-names = "stmmaceth";
tx-fifo-depth = <16384>;
@@ -155,12 +188,13 @@
gmac2: ethernet@ff804000 {
compatible = "altr,socfpga-stmmac", "snps,dwmac-3.74a", "snps,dwmac";
+ altr,sysmgr-syscon = <&sysmgr 0x4c 0>;
reg = <0xff804000 0x2000>;
interrupts = <0 92 4>;
interrupt-names = "macirq";
mac-address = [00 00 00 00 00 00];
- resets = <&rst EMAC2_RESET>;
- reset-names = "stmmaceth";
+ resets = <&rst EMAC2_RESET>, <&rst EMAC2_OCP_RESET>;
+ reset-names = "stmmaceth", "stmmaceth-ocp";
clocks = <&clkmgr STRATIX10_EMAC2_CLK>;
clock-names = "stmmaceth";
tx-fifo-depth = <16384>;
@@ -337,6 +371,8 @@
sysmgr: sysmgr@ffd12000 {
compatible = "altr,sys-mgr", "syscon";
reg = <0xffd12000 0x228>;
+ interrupts = <0x0 0x10 0x4>;
+ cpu1-start-addr = <0xffd06230>;
};
/* Local timer */
@@ -416,6 +452,7 @@
phy-names = "usb2-phy";
resets = <&rst USB0_RESET>, <&rst USB0_OCP_RESET>;
reset-names = "dwc2", "dwc2-ecc";
+ clocks = <&clkmgr STRATIX10_USB_CLK>;
status = "disabled";
};
@@ -427,6 +464,7 @@
phy-names = "usb2-phy";
resets = <&rst USB1_RESET>, <&rst USB1_OCP_RESET>;
reset-names = "dwc2", "dwc2-ecc";
+ clocks = <&clkmgr STRATIX10_USB_CLK>;
status = "disabled";
};
@@ -435,6 +473,7 @@
reg = <0xffd00200 0x100>;
interrupts = <0 117 4>;
resets = <&rst WATCHDOG0_RESET>;
+ clocks = <&clkmgr STRATIX10_L4_SYS_FREE_CLK>;
status = "disabled";
};
@@ -443,6 +482,7 @@
reg = <0xffd00300 0x100>;
interrupts = <0 118 4>;
resets = <&rst WATCHDOG1_RESET>;
+ clocks = <&clkmgr STRATIX10_L4_SYS_FREE_CLK>;
status = "disabled";
};
@@ -451,6 +491,7 @@
reg = <0xffd00400 0x100>;
interrupts = <0 125 4>;
resets = <&rst WATCHDOG2_RESET>;
+ clocks = <&clkmgr STRATIX10_L4_SYS_FREE_CLK>;
status = "disabled";
};
@@ -459,6 +500,7 @@
reg = <0xffd00500 0x100>;
interrupts = <0 126 4>;
resets = <&rst WATCHDOG3_RESET>;
+ clocks = <&clkmgr STRATIX10_L4_SYS_FREE_CLK>;
status = "disabled";
};
@@ -473,5 +515,31 @@
interrupts = <16 4>, <48 4>;
};
};
+
+ qspi: spi@ff8d2000 {
+ compatible = "cdns,qspi-nor";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0xff8d2000 0x100>,
+ <0xff900000 0x100000>;
+ interrupts = <0 3 4>;
+ cdns,fifo-depth = <128>;
+ cdns,fifo-width = <4>;
+ cdns,trigger-address = <0x00000000>;
+ clocks = <&qspi_clk>;
+
+ status = "disabled";
+ };
+
+ firmware {
+ svc {
+ compatible = "intel,stratix10-svc";
+ method = "smc";
+ memory-region = <&service_reserved>;
+ rsu {
+ compatible = "intel,stratix10-rsu";
+ };
+ };
+ };
};
};
diff --git a/arch/arm64/boot/dts/altera/socfpga_stratix10_fpga_update.dts b/arch/arm64/boot/dts/altera/socfpga_stratix10_fpga_update.dts
new file mode 100644
index 000000000000..c7811cc92091
--- /dev/null
+++ b/arch/arm64/boot/dts/altera/socfpga_stratix10_fpga_update.dts
@@ -0,0 +1,17 @@
+/dts-v1/;
+/plugin/;
+/ {
+ fragment@0 {
+ target-path = "/soc/base_fpga_region";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ __overlay__ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ firmware-name = "soc_s10_fpga_config.rbf";
+ config-complete-timeout-us = <2000000>;
+ };
+ };
+};
+
diff --git a/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk.dts b/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk.dts
index fb1b9ddd9f51..444402fae6d3 100644
--- a/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk.dts
+++ b/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk.dts
@@ -64,6 +64,22 @@
clock-frequency = <25000000>;
};
};
+
+ gpio_fpga: gpio@f9001080 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "snps,dw-apb-gpio";
+ reg = <0xf9001080 0x4>;
+ status = "disabled";
+
+ portfpga: gpio-controller@0 {
+ compatible = "snps,dw-apb-gpio-port";
+ gpio-controller;
+ #gpio-cells = <2>;
+ snps,nr-gpios = <8>;
+ reg = <0>;
+ };
+ };
};
};
@@ -77,6 +93,7 @@
phy-handle = <&phy0>;
max-frame-size = <9000>;
+ snps,multicast-filter-bins = <256>;
mdio0 {
#address-cells = <1>;
@@ -124,6 +141,8 @@
&i2c1 {
status = "okay";
clock-frequency = <100000>;
+ i2c-sda-falling-time-ns = <890>; /* hcnt */
+ i2c-sdl-falling-time-ns = <890>; /* lcnt */
adc@14 {
compatible = "lltc,ltc2497";
@@ -147,3 +166,39 @@
reg = <0x68>;
};
};
+
+&qspi {
+ status = "okay";
+ flash@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "n25q00a";
+ reg = <0>;
+ spi-max-frequency = <50000000>;
+
+ m25p,fast-read;
+ cdns,page-size = <256>;
+ cdns,block-size = <16>;
+ cdns,read-delay = <1>;
+ cdns,tshsl-ns = <50>;
+ cdns,tsd2d-ns = <50>;
+ cdns,tchsh-ns = <4>;
+ cdns,tslch-ns = <4>;
+
+ partitions {
+ compatible = "fixed-partitions";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ qspi_boot: partition@0 {
+ label = "Boot and fpga data";
+ reg = <0x00910000 0x036F0000>;
+ };
+
+ qspi_rootfs: partition@4000000 {
+ label = "Root Filesystem - JFFS2";
+ reg = <0x4000000 0x4000000>;
+ };
+ };
+ };
+};
diff --git a/arch/arm64/boot/dts/altera/stratix10_swvp.dts b/arch/arm64/boot/dts/altera/stratix10_swvp.dts
new file mode 100644
index 000000000000..69052cf056f7
--- /dev/null
+++ b/arch/arm64/boot/dts/altera/stratix10_swvp.dts
@@ -0,0 +1,763 @@
+/*
+ * Copyright (C), 2017 Intel Corporation. All rights reserved.
+ * Copyright (C), 2015 Altera Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <dt-bindings/reset/altr,rst-mgr-s10.h>
+
+/dts-v1/;
+
+/ {
+ #address-cells = <0x1>;
+ #size-cells = <0x1>;
+ model = "Altera SOCFPGA Stratix 10 SWVP";
+ compatible = "arm,foundation-aarch64", "arm,vexpress";
+ interrupt-parent = <&gic>;
+
+ aliases {
+ serial0 = "/soc/serial0@ffc02000";
+ serial1 = "/soc/serial1@ffc02100";
+
+ timer0 = "/soc/timer0@ffc03000";
+ timer1 = "/soc/timer1@ffc03100";
+ timer2 = "/soc/timer2@ffd00000";
+ timer3 = "/soc/timer3@ffd00100";
+
+ ethernet0 = "/soc/ethernet@ff800000";
+ ethernet1 = "/soc/ethernet@ff802000";
+ ethernet2 = "/soc/ethernet@ff804000";
+ };
+
+ memory {
+ device_type = "memory";
+ reg = <0x0 0x80000000>;
+ };
+
+ chosen {
+ bootargs = "rdinit=/sbin/init ip=dhcp mem=2048M";
+ stdout-path = "serial1:115200n8";
+ linux,initrd-start = <0x10000000>;
+ linux,initrd-end = <0x125c8324>;
+ };
+
+ cpus {
+ #address-cells = <0x2>;
+ #size-cells = <0x0>;
+
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53", "arm,armv8";
+ reg = <0x0 0x0>;
+ enable-method = "spin-table";
+ cpu-release-addr = <0x0 0x0000fff8>;
+ };
+
+ cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53", "arm,armv8";
+ reg = <0x0 0x1>;
+ enable-method = "spin-table";
+ cpu-release-addr = <0x0 0x0000fff8>;
+ };
+
+ cpu@2 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53", "arm,armv8";
+ reg = <0x0 0x2>;
+ enable-method = "spin-table";
+ cpu-release-addr = <0x0 0x0000fff8>;
+ };
+
+ cpu@3 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53", "arm,armv8";
+ reg = <0x0 0x3>;
+ enable-method = "spin-table";
+ cpu-release-addr = <0x0 0x0000fff8>;
+ };
+ };
+
+ gic: interrupt-controller@fffc1000 {
+ compatible = "arm,gic-400", "arm,cortex-a15-gic";
+ #interrupt-cells = <3>;
+ #address-cells = <0>;
+ interrupt-controller;
+ reg = <0xfffc1000 0x1000>,
+ <0xfffc2000 0x1000>,
+ <0xfffc4000 0x2000>,
+ <0xfffc6000 0x2000>;
+ };
+
+ soc {
+ #address-cells = <0x1>;
+ #size-cells = <0x1>;
+ compatible = "simple-bus";
+ device_type = "soc";
+ interrupt-parent = <0x2>;
+ ranges;
+
+ usbphy0: usbphy@0 {
+ #phy-cells = <0>;
+ compatible = "usb-nop-xceiv";
+ status = "okay";
+ };
+
+ usb0: usb@ffb00000 {
+ compatible = "snps,dwc2";
+ reg = <0xffb00000 0x40000>;
+ interrupts = <0x0 0x5d 0x4>;
+ clocks = <&usb_clk>;
+ clock-names = "otg";
+ resets = <&rst USB0_RESET>;
+ reset-names = "dwc2";
+ dr_mode = "host";
+ status = "okay";
+ };
+
+ usb1: usb@ffb40000 {
+ compatible = "snps,dwc2";
+ reg = <0xffb40000 0x40000>;
+ interrupts = <0x0 0x5e 0x4>;
+ clocks = <&usb_clk>;
+ clock-names = "otg";
+ resets = <&rst USB0_RESET>;
+ reset-names = "dwc2";
+ dr_mode = "host";
+ status = "okay";
+ };
+
+ spi0: spi@ffda4000 {
+ compatible = "snps,dw-apb-ssi";
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+ reg = <0xffda4000 0x1000>;
+ interrupts = <0x0 0x65 4>;
+ num-chipselect = <4>;
+ bus-num = <0x0>;
+ status = "disabled";
+ };
+
+ spi1: spi@ffda5000 {
+ compatible = "snps,dw-apb-ssi";
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+ reg = <0xffda5000 0x1000>;
+ interrupts = <0x0 0x66 0x4>;
+ num-chipselect = <0x4>;
+ bus-num = <0x0>;
+ status = "disabled";
+ };
+
+ clkmgr@ffd10000 {
+ compatible = "altr,clk-mgr";
+ reg = <0xffd10000 0x1000>;
+ interrupts = <0 0x77 0x4>;
+
+ clocks {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cb_intosc_hs_div2_clk: cb_intosc_hs_div2_clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ };
+
+ cb_intosc_ls_clk: cb_intosc_ls_clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ };
+
+ f2s_free_clk: f2s_free_clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ };
+
+ osc1: osc1 {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <25000000>;
+ };
+
+ main_pll: main_pll {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-pll-clock";
+ clocks = <&osc1>, <&cb_intosc_ls_clk>,
+ <&f2s_free_clk>;
+ reg = <0x40>;
+
+ main_mpu_base_clk: main_mpu_base_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <&main_pll>;
+ div-reg = <0x140 0 11>;
+ };
+
+ main_noc_base_clk: main_noc_base_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <&main_pll>;
+ div-reg = <0x144 0 11>;
+ };
+
+ main_emaca_clk: main_emaca_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <&main_pll>;
+ reg = <0x68>;
+ };
+
+ main_emacb_clk: main_emacb_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <&main_pll>;
+ reg = <0x6c>;
+ };
+
+ main_emac_ptp_clk: main_emac_ptp_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <&main_pll>;
+ reg = <0x70>;
+ };
+
+ main_gpio_db_clk: main_gpio_db_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <&main_pll>;
+ reg = <0x74>;
+ };
+
+ main_sdmmc_clk: main_sdmmc_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <&main_pll>;
+ reg = <0x78>;
+ };
+
+ main_s2f_usr0_clk: main_s2f_usr0_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <&main_pll>;
+ reg = <0x7c>;
+ };
+
+ main_s2f_usr1_clk: main_s2f_usr1_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <&main_pll>;
+ reg = <0x80>;
+ };
+
+ main_hmc_pll_ref_clk: main_hmc_pll_ref_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <&main_pll>;
+ reg = <0x84>;
+ };
+
+ main_periph_ref_clk: main_periph_ref_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <&main_pll>;
+ reg = <0x9c>;
+ };
+ };
+
+ periph_pll: periph_pll {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-pll-clock";
+ clocks = <&osc1>, <&cb_intosc_ls_clk>,
+ <&f2s_free_clk>, <&main_periph_ref_clk>;
+ reg = <0xc0>;
+
+ peri_mpu_base_clk: peri_mpu_base_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <&periph_pll>;
+ div-reg = <0x140 16 11>;
+ };
+
+ peri_noc_base_clk: peri_noc_base_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <&periph_pll>;
+ div-reg = <0x144 16 11>;
+ };
+
+ peri_emaca_clk: peri_emaca_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <&periph_pll>;
+ reg = <0xe8>;
+ };
+
+ peri_emacb_clk: peri_emacb_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <&periph_pll>;
+ reg = <0xec>;
+ };
+
+ peri_emac_ptp_clk: peri_emac_ptp_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <&periph_pll>;
+ reg = <0xf0>;
+ };
+
+ peri_gpio_db_clk: peri_gpio_db_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <&periph_pll>;
+ reg = <0xf4>;
+ };
+
+ peri_sdmmc_clk: peri_sdmmc_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <&periph_pll>;
+ reg = <0xf8>;
+ };
+
+ peri_s2f_usr0_clk: peri_s2f_usr0_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <&periph_pll>;
+ reg = <0xfc>;
+ };
+
+ peri_s2f_usr1_clk: peri_s2f_usr1_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <&periph_pll>;
+ reg = <0x100>;
+ };
+
+ peri_hmc_pll_ref_clk: peri_hmc_pll_ref_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <&periph_pll>;
+ reg = <0x104>;
+ };
+ };
+
+ mpu_free_clk: mpu_free_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <&main_mpu_base_clk>, <&peri_mpu_base_clk>,
+ <&osc1>, <&cb_intosc_hs_div2_clk>,
+ <&f2s_free_clk>;
+ reg = <0x60>;
+ };
+
+ noc_free_clk: noc_free_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <&main_noc_base_clk>, <&peri_noc_base_clk>,
+ <&osc1>, <&cb_intosc_hs_div2_clk>,
+ <&f2s_free_clk>;
+ reg = <0x64>;
+ };
+
+ s2f_user1_free_clk: s2f_user1_free_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <&main_s2f_usr1_clk>, <&peri_s2f_usr1_clk>,
+ <&osc1>, <&cb_intosc_hs_div2_clk>,
+ <&f2s_free_clk>;
+ reg = <0x104>;
+ };
+
+ sdmmc_free_clk: sdmmc_free_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <&main_sdmmc_clk>, <&peri_sdmmc_clk>,
+ <&osc1>, <&cb_intosc_hs_div2_clk>,
+ <&f2s_free_clk>;
+ fixed-divider = <4>;
+ reg = <0xf8>;
+ };
+
+ l4_sys_free_clk: l4_sys_free_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-perip-clk";
+ clocks = <&noc_free_clk>;
+ fixed-divider = <4>;
+ };
+
+ l4_main_clk: l4_main_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-gate-clk";
+ clocks = <&noc_free_clk>;
+ div-reg = <0xa8 0 2>;
+ clk-gate = <0x48 1>;
+ };
+
+ l4_mp_clk: l4_mp_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-gate-clk";
+ clocks = <&noc_free_clk>;
+ div-reg = <0xa8 8 2>;
+ clk-gate = <0x48 2>;
+ };
+
+ l4_sp_clk: l4_sp_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-gate-clk";
+ clocks = <&noc_free_clk>;
+ div-reg = <0xa8 16 2>;
+ clk-gate = <0x48 3>;
+ };
+
+ mpu_periph_clk: mpu_periph_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-gate-clk";
+ clocks = <&mpu_free_clk>;
+ fixed-divider = <4>;
+ clk-gate = <0x48 0>;
+ };
+
+ sdmmc_clk: sdmmc_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-gate-clk";
+ clocks = <&sdmmc_free_clk>;
+ clk-gate = <0xc8 5>;
+ };
+
+ qspi_clk: qspi_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-gate-clk";
+ clocks = <&l4_main_clk>;
+ clk-gate = <0xc8 11>;
+ };
+
+ nand_clk: nand_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-gate-clk";
+ clocks = <&l4_mp_clk>;
+ clk-gate = <0xc8 10>;
+ };
+
+ spi_m_clk: spi_m_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-gate-clk";
+ clocks = <&l4_main_clk>;
+ clk-gate = <0xc8 9>;
+ };
+
+ usb_clk: usb_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-gate-clk";
+ clocks = <&l4_mp_clk>;
+ clk-gate = <0xc8 8>;
+ };
+
+ s2f_usr1_clk: s2f_usr1_clk {
+ #clock-cells = <0>;
+ compatible = "altr,socfpga-a10-gate-clk";
+ clocks = <&peri_s2f_usr1_clk>;
+ clk-gate = <0xc8 6>;
+ };
+
+ clk24mhz: clk24mhz {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <24000000>;
+ clock-output-names = "clk24mhz";
+ };
+ };
+ };
+
+ mmc: dwmmc0@ff808000 {
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+ compatible = "altr,socfpga-dw-mshc";
+ reg = <0xff808000 0x1000>;
+ interrupts = <0x0 0x60 0x4>;
+ fifo-depth = <0x400>;
+ status = "okay";
+ num-slots = <0x1>;
+ broken-cd;
+ altr,dw-mshc-ciu-div = <0x3>;
+ altr,dw-mshc-sdr-timing = <0x0 0x3>;
+ clocks = <&l4_mp_clk>, <&sdmmc_free_clk>;
+ clock-names = "biu", "ciu";
+ clock-freq-min-max = <400000 25000000>;
+ pwr-en = <0x0>;
+ clock-frequency = <25000000>;
+
+ slot@0 {
+ reg = <0x0>;
+ bus-width = <0x4>;
+ };
+ };
+
+ ethernet@ff800000 {
+ compatible = "altr,socfpga-stmmac", "snps,dwmac-3.72a", "snps,dwmac";
+ altr,sysmgr-syscon = <&sysmgr 0x44 0>;
+ reg = <0xff800000 0x2000>;
+ interrupts = <0x0 0x5a 0x4>;
+ interrupt-names = "macirq";
+ mac-address = [00 00 00 00 00 00];
+ clocks = <&clk24mhz>;
+ clock-names = "stmmaceth";
+ status = "okay";
+ phy-mode = "rgmii";
+ phy-addr = <0xffffffff>;
+ snps,max-mtu = <0x0>;
+ resets = <&rst EMAC0_RESET>;
+ reset-names = "stmmaceth";
+ };
+
+ ethernet@ff802000 {
+ compatible = "altr,socfpga-stmmac", "snps,dwmac-3.72a", "snps,dwmac";
+ altr,sysmgr-syscon = <&sysmgr 0x48 0>;
+ reg = <0xff802000 0x2000>;
+ interrupts = <0x0 0x5b 0x4>;
+ interrupt-names = "macirq";
+ mac-address = [00 00 00 00 00 00];
+ clocks = <&clk24mhz>;
+ clock-names = "stmmaceth";
+ status = "okay";
+ phy-mode = "rgmii";
+ phy-addr = <0xffffffff>;
+ resets = <&rst EMAC1_RESET>;
+ reset-names = "stmmaceth";
+ };
+
+ ethernet@ff804000 {
+ compatible = "altr,socfpga-stmmac", "snps,dwmac-3.72a", "snps,dwmac";
+ altr,sysmgr-syscon = <&sysmgr 0x4c 0>;
+ reg = <0xff804000 0x2000>;
+ interrupts = <0x0 0x5c 0x4>;
+ interrupt-names = "macirq";
+ mac-address = [00 00 00 00 00 00];
+ clocks = <&clk24mhz>;
+ clock-names = "stmmaceth";
+ status = "okay";
+ phy-mode = "rgmii";
+ phy-addr = <0xffffffff>;
+ resets = <&rst EMAC2_RESET>;
+ reset-names = "stmmaceth";
+ };
+
+ gpio@ffc03200 {
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+ compatible = "snps,dw-apb-gpio";
+ reg = <0xffc03200 0x100>;
+ status = "disabled";
+
+ gpio-controller@0 {
+ compatible = "snps,dw-apb-gpio-port";
+ gpio-controller;
+ #gpio-cells = <0x2>;
+ snps,nr-gpios = <0x1d>;
+ reg = <0x0>;
+ interrupt-controller;
+ #interrupt-cells = <0x2>;
+ interrupts = <0x0 0x6e 0x4>;
+ };
+ };
+
+ gpio@ffc03300 {
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+ compatible = "snps,dw-apb-gpio";
+ reg = <0xffc03300 0x100>;
+ status = "disabled";
+
+ gpio-controller@0 {
+ compatible = "snps,dw-apb-gpio-port";
+ gpio-controller;
+ #gpio-cells = <0x2>;
+ snps,nr-gpios = <0x1d>;
+ reg = <0x0>;
+ interrupt-controller;
+ #interrupt-cells = <0x2>;
+ interrupts = <0x0 0x6f 0x4>;
+ };
+ };
+
+ fpgamgr@0xffcfe400 {
+ compatible = "altr,socfpga-a10-fpga-mgr";
+ transport = "mmio";
+ reg = <0xffd03000 0x1000 0xffcfe400 0x400>;
+ };
+
+ i2c0: i2c@ffc02800 {
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+ compatible = "snps,designware-i2c";
+ reg = <0xffc02800 0x100>;
+ interrupts = <0 0x67 4>;
+ status = "disabled";
+ };
+
+ i2c1: i2c@ffc02900 {
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+ compatible = "snps,designware-i2c";
+ reg = <0xffc02900 0x100>;
+ interrupts = <0 0x68 4>;
+ status = "disabled";
+ };
+
+ i2c2: i2c@ffc02a00 {
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+ compatible = "snps,designware-i2c";
+ reg = <0xffc02a00 0x100>;
+ interrupts = <0 0x69 4>;
+ status = "disabled";
+ };
+
+ i2c3: i2c@ffc02b00 {
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+ compatible = "snps,designware-i2c";
+ reg = <0xffc02b00 0x100>;
+ interrupts = <0 0x6a 4>;
+ status = "disabled";
+ };
+
+ i2c4: i2c@ffc02c00 {
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+ compatible = "snps,designware-i2c";
+ reg = <0xffc02c00 0x100>;
+ interrupts = <0 0x6b 4>;
+ status = "disabled";
+ };
+
+ l2-cache@fffff000 {
+ compatible = "arm,pl310-cache";
+ reg = <0xfffff000 0x1000>;
+ interrupts = <0x0 0x12 0x4>;
+ cache-unified;
+ cache-level = <0x2>;
+ linux,phandle = <0x1>;
+ phandle = <0x1>;
+ };
+
+ sram@ffe00000 {
+ compatible = "mmio-sram";
+ reg = <0xffe00000 0x100000>;
+ };
+
+ rst: rstmgr@ffd11000 {
+ #reset-cells = <0x1>;
+ compatible = "altr,rst-mgr";
+ reg = <0xffd11000 0x1000>;
+ altr,modrst-offset = <0x20>;
+ };
+
+ sysmgr: sysmgr@ffd12000 {
+ compatible = "altr,sys-mgr", "syscon";
+ reg = <0xffd12000 0x1000>;
+ interrupts = <0x0 0x10 0x4>;
+ cpu1-start-addr = <0xffd06230>;
+ };
+
+ timer {
+ compatible = "arm,armv8-timer";
+ interrupts = <1 13 0xff01>,
+ <1 14 0xff01>,
+ <1 11 0xff01>,
+ <1 10 0xff01>;
+ clock-frequency = <24000000>;
+ };
+
+ timer0@ffc03000 {
+ compatible = "snps,dw-apb-timer-sp";
+ interrupts = <0x0 0x71 0x4>;
+ reg = <0xffc03000 0x100>;
+ clock-frequency = <0x17d7840>;
+ };
+
+ timer1@ffc03100 {
+ compatible = "snps,dw-apb-timer-sp";
+ interrupts = <0x0 0x72 0x4>;
+ reg = <0xffc03100 0x100>;
+ clock-frequency = <0x17d7840>;
+ };
+
+ timer2@ffd00000 {
+ compatible = "snps,dw-apb-timer-osc";
+ interrupts = <0x0 0x73 0x4>;
+ reg = <0xffd00000 0x100>;
+ clock-frequency = <0x17d7840>;
+ };
+
+ timer3@ffd00100 {
+ compatible = "snps,dw-apb-timer-osc";
+ interrupts = <0x0 0x74 0x4>;
+ reg = <0xffd01000 0x100>;
+ clock-frequency = <0x17d7840>;
+ };
+
+ serial0@ffc02000 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0xffc02000 0x100>;
+ interrupts = <0x0 0x6c 0x4>;
+ reg-shift = <0x2>;
+ reg-io-width = <0x4>;
+ status = "okay";
+ clock-frequency = <0x5f5e100>;
+ };
+
+ serial1@ffc02100 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0xffc02100 0x100>;
+ interrupts = <0x0 0x6d 0x4>;
+ reg-shift = <0x2>;
+ reg-io-width = <0x4>;
+ status = "okay";
+ clock-frequency = <0x5f5e100>;
+ };
+
+ watchdog@ffd00200 {
+ compatible = "snps,dw-wdt";
+ reg = <0xffd00200 0x100>;
+ interrupts = <0x0 0x75 0x4>;
+ status = "disabled";
+ };
+
+ watchdog@ffd00300 {
+ compatible = "snps,dw-wdt";
+ reg = <0xffd00300 0x100>;
+ interrupts = <0x0 0x76 0x4>;
+ status = "disabled";
+ };
+
+ watchdog@ffd00400 {
+ compatible = "snps,dw-wdt";
+ reg = <0xffd00400 0x100>;
+ interrupts = <0x0 0x7d 0x4>;
+ status = "disabled";
+ };
+
+ watchdog@ffd00500 {
+ compatible = "snps,dw-wdt";
+ reg = <0xffd00500 0x100>;
+ interrupts = <0x0 0x7e 0x4>;
+ status = "disabled";
+ };
+ };
+};
diff --git a/drivers/clk/socfpga/clk-s10.c b/drivers/clk/socfpga/clk-s10.c
index 72714633e39c..2f4301883a39 100644
--- a/drivers/clk/socfpga/clk-s10.c
+++ b/drivers/clk/socfpga/clk-s10.c
@@ -28,7 +28,7 @@ static const char * const emaca_free_mux[] = {"peri_emaca_clk", "boot_clk"};
static const char * const emacb_free_mux[] = {"peri_emacb_clk", "boot_clk"};
static const char * const emac_ptp_free_mux[] = {"peri_emac_ptp_clk", "boot_clk"};
static const char * const gpio_db_free_mux[] = {"peri_gpio_db_clk", "boot_clk"};
-static const char * const sdmmc_free_mux[] = {"peri_sdmmc_clk", "boot_clk"};
+static const char * const sdmmc_free_mux[] = {"main_sdmmc_clk", "boot_clk"};
static const char * const s2f_usr1_free_mux[] = {"peri_s2f_usr1_clk", "boot_clk"};
static const char * const psi_ref_free_mux[] = {"peri_psi_ref_clk", "boot_clk"};
static const char * const mpu_mux[] = { "mpu_free_clk", "boot_clk",};
@@ -37,6 +37,10 @@ static const char * const s2f_usr0_mux[] = {"f2s_free_clk", "boot_clk"};
static const char * const emac_mux[] = {"emaca_free_clk", "emacb_free_clk"};
static const char * const noc_mux[] = {"noc_free_clk", "boot_clk"};
+static const char * const mpu_free_mux[] = {"main_mpu_base_clk",
+ "peri_mpu_base_clk",
+ "osc1", "cb_intosc_hs_div2_clk",
+ "f2s_free_clk"};
/* clocks in AO (always on) controller */
static const struct stratix10_pll_clock s10_pll_clks[] = {
{ STRATIX10_BOOT_CLK, "boot_clk", boot_mux, ARRAY_SIZE(boot_mux), 0,
@@ -57,7 +61,7 @@ static const struct stratix10_perip_c_clock s10_main_perip_c_clks[] = {
};
static const struct stratix10_perip_cnt_clock s10_main_perip_cnt_clks[] = {
- { STRATIX10_MPU_FREE_CLK, "mpu_free_clk", NULL, cntr_mux, ARRAY_SIZE(cntr_mux),
+ { STRATIX10_MPU_FREE_CLK, "mpu_free_clk", NULL, mpu_free_mux, ARRAY_SIZE(cntr_mux),
0, 0x48, 0, 0, 0},
{ STRATIX10_NOC_FREE_CLK, "noc_free_clk", NULL, noc_free_mux, ARRAY_SIZE(noc_free_mux),
0, 0x4C, 0, 0, 0},
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 89d20eb2b9f5..4d55f5fda60e 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -470,6 +470,8 @@ struct pl330_dmac {
/* Size of MicroCode buffers for each channel. */
unsigned mcbufsz;
+ /* True if microcode must reside in cached memory. */
+ bool microcode_cached;
/* ioremap'ed address of PL330 registers. */
void __iomem *base;
/* Populated by the PL330 core driver during pl330_add */
@@ -1894,19 +1896,44 @@ static int dmac_alloc_threads(struct pl330_dmac *pl330)
return 0;
}
+static void *alloc_pl330_microcode_mem(struct pl330_dmac *pl330)
+{
+ int chans = pl330->pcfg.num_chan;
+
+ if (pl330->microcode_cached) {
+ pl330->mcode_cpu = kzalloc(chans * pl330->mcbufsz,
+ GFP_KERNEL);
+ pl330->mcode_bus = virt_to_phys(pl330->mcode_cpu);
+ } else
+ pl330->mcode_cpu =
+ dma_alloc_coherent(pl330->ddma.dev,
+ chans * pl330->mcbufsz,
+ &pl330->mcode_bus, GFP_KERNEL);
+
+ return pl330->mcode_cpu;
+}
+
+static void free_pl330_microcode_mem(struct pl330_dmac *pl330)
+{
+ int chans = pl330->pcfg.num_chan;
+
+ if (pl330->microcode_cached)
+ kfree(pl330->mcode_cpu);
+ else
+ dma_free_coherent(pl330->ddma.dev,
+ chans * pl330->mcbufsz,
+ pl330->mcode_cpu, pl330->mcode_bus);
+}
+
static int dmac_alloc_resources(struct pl330_dmac *pl330)
{
- int chans = pl330->pcfg.num_chan;
int ret;
/*
* Alloc MicroCode buffer for 'chans' Channel threads.
* A channel's buffer offset is (Channel_Id * MCODE_BUFF_PERCHAN)
*/
- pl330->mcode_cpu = dma_alloc_attrs(pl330->ddma.dev,
- chans * pl330->mcbufsz,
- &pl330->mcode_bus, GFP_KERNEL,
- DMA_ATTR_PRIVILEGED);
+ pl330->mcode_cpu = alloc_pl330_microcode_mem(pl330);
if (!pl330->mcode_cpu) {
dev_err(pl330->ddma.dev, "%s:%d Can't allocate memory!\n",
__func__, __LINE__);
@@ -1917,9 +1944,7 @@ static int dmac_alloc_resources(struct pl330_dmac *pl330)
if (ret) {
dev_err(pl330->ddma.dev, "%s:%d Can't to create channels for DMAC!\n",
__func__, __LINE__);
- dma_free_coherent(pl330->ddma.dev,
- chans * pl330->mcbufsz,
- pl330->mcode_cpu, pl330->mcode_bus);
+ free_pl330_microcode_mem(pl330);
return ret;
}
@@ -1998,9 +2023,7 @@ static void pl330_del(struct pl330_dmac *pl330)
/* Free DMAC resources */
dmac_free_threads(pl330);
- dma_free_coherent(pl330->ddma.dev,
- pl330->pcfg.num_chan * pl330->mcbufsz, pl330->mcode_cpu,
- pl330->mcode_bus);
+ free_pl330_microcode_mem(pl330);
}
/* forward declaration */
@@ -2948,7 +2971,12 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
pl330->mcbufsz = 0;
- /* get quirk */
+ if (adev->dev.of_node)
+ pl330->microcode_cached =
+ of_property_read_bool(adev->dev.of_node,
+ "microcode-cached");
+
+ /* get quirk */
for (i = 0; i < ARRAY_SIZE(of_quirks); i++)
if (of_property_read_bool(np, of_quirks[i].quirk))
pl330->quirks |= of_quirks[i].id;
diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index ee9c5420c47f..f7b5775f748a 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -9,6 +9,13 @@ menuconfig FPGA
kernel. The FPGA framework adds a FPGA manager class and FPGA
manager drivers.
+config FPGA_MGR_DEBUG_FS
+ bool "FPGA Manager DebugFS"
+ depends on FPGA && DEBUG_FS
+ help
+ Say Y here if you want to expose a DebugFS interface for the
+ FPGA Manager Framework.
+
if FPGA
config FPGA_MGR_SOCFPGA
@@ -56,6 +63,19 @@ config FPGA_MGR_ZYNQ_FPGA
help
FPGA manager driver support for Xilinx Zynq FPGAs.
+config FPGA_MGR_STRATIX10_SOC
+ tristate "Intel Stratix10 SoC FPGA Manager"
+ depends on (ARCH_STRATIX10 && INTEL_SERVICE)
+ help
+ FPGA manager driver support for the Intel Stratix10 SoC.
+
+config FPGA_MGR_TS73XX
+ tristate "Technologic Systems TS-73xx SBC FPGA Manager"
+ depends on ARCH_EP93XX && MACH_TS72XX
+ help
+ FPGA manager driver support for the Altera Cyclone II FPGA
+ present on the TS-73xx SBC boards
+
config FPGA_MGR_XILINX_SPI
tristate "Xilinx Configuration over Slave Serial (SPI)"
depends on SPI
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index f9803dad6919..bed105d45378 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -4,7 +4,8 @@
#
# Core FPGA Manager Framework
-obj-$(CONFIG_FPGA) += fpga-mgr.o
+fpga_mgr-objs := fpga-mgr.o fpga-mgr-debugfs.o
+obj-$(CONFIG_FPGA) += fpga_mgr.o
# FPGA Manager Drivers
obj-$(CONFIG_FPGA_MGR_ALTERA_CVP) += altera-cvp.o
@@ -13,6 +14,7 @@ obj-$(CONFIG_FPGA_MGR_ICE40_SPI) += ice40-spi.o
obj-$(CONFIG_FPGA_MGR_MACHXO2_SPI) += machxo2-spi.o
obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o
obj-$(CONFIG_FPGA_MGR_SOCFPGA_A10) += socfpga-a10.o
+obj-$(CONFIG_FPGA_MGR_STRATIX10_SOC) += stratix10-soc.o
obj-$(CONFIG_FPGA_MGR_TS73XX) += ts73xx-fpga.o
obj-$(CONFIG_FPGA_MGR_XILINX_SPI) += xilinx-spi.o
obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o
diff --git a/drivers/fpga/altera-freeze-bridge.c b/drivers/fpga/altera-freeze-bridge.c
index ffd586c48ecf..bc986dbeee8d 100644
--- a/drivers/fpga/altera-freeze-bridge.c
+++ b/drivers/fpga/altera-freeze-bridge.c
@@ -26,8 +26,6 @@
#define FREEZE_CSR_CTRL_RESET_REQ BIT(1)
#define FREEZE_CSR_CTRL_UNFREEZE_REQ BIT(2)
-#define FREEZE_BRIDGE_NAME "freeze"
-
struct altera_freeze_br_data {
struct device *dev;
void __iomem *base_addr;
@@ -245,7 +243,7 @@ static int altera_freeze_br_probe(struct platform_device *pdev)
priv->base_addr = base_addr;
- br = fpga_bridge_create(dev, FREEZE_BRIDGE_NAME,
+ return fpga_bridge_register(dev, dev_name(dev),
&altera_freeze_br_br_ops, priv);
if (!br)
return -ENOMEM;
diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c
index 24b8f98b73ec..00047bf6bfd7 100644
--- a/drivers/fpga/fpga-bridge.c
+++ b/drivers/fpga/fpga-bridge.c
@@ -292,7 +292,7 @@ static ssize_t name_show(struct device *dev,
{
struct fpga_bridge *bridge = to_fpga_bridge(dev);
- return sprintf(buf, "%s\n", bridge->name);
+ return scnprintf(buf, PAGE_SIZE, "%s\n", bridge->name);
}
static ssize_t state_show(struct device *dev,
@@ -304,7 +304,7 @@ static ssize_t state_show(struct device *dev,
if (bridge->br_ops && bridge->br_ops->enable_show)
enable = bridge->br_ops->enable_show(bridge);
- return sprintf(buf, "%s\n", enable ? "enabled" : "disabled");
+ return scnprintf(buf, 10, "%s\n", enable ? "enabled" : "disabled");
}
static DEVICE_ATTR_RO(name);
diff --git a/drivers/fpga/fpga-mgr-debugfs.c b/drivers/fpga/fpga-mgr-debugfs.c
new file mode 100644
index 000000000000..6a47cc6326ca
--- /dev/null
+++ b/drivers/fpga/fpga-mgr-debugfs.c
@@ -0,0 +1,235 @@
+/*
+ * FPGA Manager DebugFS
+ *
+ * Copyright (C) 2016 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/debugfs.h>
+#include <linux/fpga/fpga-mgr.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#if IS_ENABLED(CONFIG_FPGA_MGR_DEBUG_FS)
+
+static struct dentry *fpga_mgr_debugfs_root;
+
+struct fpga_mgr_debugfs {
+ struct dentry *debugfs_dir;
+ struct fpga_image_info *info;
+};
+
+static ssize_t fpga_mgr_firmware_write_file(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct fpga_manager *mgr = file->private_data;
+ struct fpga_mgr_debugfs *debugfs = mgr->debugfs;
+ char *buf;
+ int ret;
+
+ ret = fpga_mgr_lock(mgr);
+ if (ret) {
+ dev_err(&mgr->dev, "FPGA manager is busy\n");
+ return -EBUSY;
+ }
+
+ buf = devm_kzalloc(&mgr->dev, count, GFP_KERNEL);
+ if (!buf) {
+ fpga_mgr_unlock(mgr);
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(buf, user_buf, count)) {
+ fpga_mgr_unlock(mgr);
+ devm_kfree(&mgr->dev, buf);
+ return -EFAULT;
+ }
+
+ buf[count] = 0;
+ if (buf[count - 1] == '\n')
+ buf[count - 1] = 0;
+
+ /* Release previous firmware name (if any). Save current one. */
+ if (debugfs->info->firmware_name)
+ devm_kfree(&mgr->dev, debugfs->info->firmware_name);
+ debugfs->info->firmware_name = buf;
+
+ ret = fpga_mgr_load(mgr, debugfs->info);
+ if (ret)
+ dev_err(&mgr->dev,
+ "fpga_mgr_load returned with value %d\n", ret);
+
+ fpga_mgr_unlock(mgr);
+
+ return count;
+}
+
+static ssize_t fpga_mgr_firmware_read_file(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct fpga_manager *mgr = file->private_data;
+ struct fpga_mgr_debugfs *debugfs = mgr->debugfs;
+ char *buf;
+ int ret;
+
+ if (!debugfs->info->firmware_name)
+ return 0;
+
+ buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = snprintf(buf, PAGE_SIZE, "%s\n", debugfs->info->firmware_name);
+ if (ret < 0) {
+ kfree(buf);
+ return ret;
+ }
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+ kfree(buf);
+
+ return ret;
+}
+
+static const struct file_operations fpga_mgr_firmware_fops = {
+ .open = simple_open,
+ .read = fpga_mgr_firmware_read_file,
+ .write = fpga_mgr_firmware_write_file,
+ .llseek = default_llseek,
+};
+
+static ssize_t fpga_mgr_image_write_file(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct fpga_manager *mgr = file->private_data;
+ struct fpga_mgr_debugfs *debugfs = mgr->debugfs;
+ char *buf;
+ int ret;
+
+ dev_info(&mgr->dev, "writing %zu bytes to %s\n", count, mgr->name);
+
+ ret = fpga_mgr_lock(mgr);
+ if (ret) {
+ dev_err(&mgr->dev, "FPGA manager is busy\n");
+ return -EBUSY;
+ }
+
+ buf = kzalloc(count, GFP_KERNEL);
+ if (!buf) {
+ fpga_mgr_unlock(mgr);
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(buf, user_buf, count)) {
+ fpga_mgr_unlock(mgr);
+ kfree(buf);
+ return -EFAULT;
+ }
+
+ /* If firmware interface was previously used, forget it. */
+ if (debugfs->info->firmware_name)
+ devm_kfree(&mgr->dev, debugfs->info->firmware_name);
+ debugfs->info->firmware_name = NULL;
+
+ debugfs->info->buf = buf;
+ debugfs->info->count = count;
+
+ ret = fpga_mgr_load(mgr, debugfs->info);
+ if (ret)
+ dev_err(&mgr->dev,
+ "fpga_mgr_buf_load returned with value %d\n", ret);
+
+ fpga_mgr_unlock(mgr);
+
+ debugfs->info->buf = NULL;
+ debugfs->info->count = 0;
+
+ kfree(buf);
+
+ return count;
+}
+
+static const struct file_operations fpga_mgr_image_fops = {
+ .open = simple_open,
+ .write = fpga_mgr_image_write_file,
+ .llseek = default_llseek,
+};
+
+void fpga_mgr_debugfs_add(struct fpga_manager *mgr)
+{
+ struct fpga_mgr_debugfs *debugfs;
+ struct fpga_image_info *info;
+
+ if (!fpga_mgr_debugfs_root)
+ return;
+
+ debugfs = kzalloc(sizeof(*debugfs), GFP_KERNEL);
+ if (!debugfs)
+ return;
+
+ info = fpga_image_info_alloc(&mgr->dev);
+ if (!info) {
+ kfree(debugfs);
+ return;
+ }
+ debugfs->info = info;
+
+ debugfs->debugfs_dir = debugfs_create_dir(dev_name(&mgr->dev),
+ fpga_mgr_debugfs_root);
+
+ debugfs_create_file("firmware_name", 0600, debugfs->debugfs_dir, mgr,
+ &fpga_mgr_firmware_fops);
+
+ debugfs_create_file("image", 0200, debugfs->debugfs_dir, mgr,
+ &fpga_mgr_image_fops);
+
+ debugfs_create_u32("flags", 0600, debugfs->debugfs_dir, &info->flags);
+
+ debugfs_create_u32("config_complete_timeout_us", 0600,
+ debugfs->debugfs_dir,
+ &info->config_complete_timeout_us);
+
+ mgr->debugfs = debugfs;
+}
+
+void fpga_mgr_debugfs_remove(struct fpga_manager *mgr)
+{
+ struct fpga_mgr_debugfs *debugfs = mgr->debugfs;
+
+ if (!fpga_mgr_debugfs_root)
+ return;
+
+ debugfs_remove_recursive(debugfs->debugfs_dir);
+
+ /* this function also frees debugfs->info->firmware_name */
+ fpga_image_info_free(debugfs->info);
+
+ kfree(debugfs);
+}
+
+void fpga_mgr_debugfs_init(void)
+{
+ fpga_mgr_debugfs_root = debugfs_create_dir("fpga_manager", NULL);
+ if (!fpga_mgr_debugfs_root)
+ pr_warn("fpga_mgr: Failed to create debugfs root\n");
+}
+
+void fpga_mgr_debugfs_uninit(void)
+{
+ debugfs_remove_recursive(fpga_mgr_debugfs_root);
+}
+
+#endif /* CONFIG_FPGA_MGR_DEBUG_FS */
diff --git a/drivers/fpga/fpga-mgr-debugfs.h b/drivers/fpga/fpga-mgr-debugfs.h
new file mode 100644
index 000000000000..2546c829d6cd
--- /dev/null
+++ b/drivers/fpga/fpga-mgr-debugfs.h
@@ -0,0 +1,37 @@
+/*
+ * FPGA Manager DebugFS
+ *
+ * Copyright (C) 2016 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _LINUX_FPGA_MGR_DEBUGFS_H
+#define _LINUX_FPGA_MGR_DEBUGFS_H
+
+#if IS_ENABLED(CONFIG_FPGA_MGR_DEBUG_FS)
+
+void fpga_mgr_debugfs_add(struct fpga_manager *mgr);
+void fpga_mgr_debugfs_remove(struct fpga_manager *mgr);
+void fpga_mgr_debugfs_init(void);
+void fpga_mgr_debugfs_uninit(void);
+
+#else
+
+void fpga_mgr_debugfs_add(struct fpga_manager *mgr) {}
+void fpga_mgr_debugfs_remove(struct fpga_manager *mgr) {}
+void fpga_mgr_debugfs_init(void) {}
+void fpga_mgr_debugfs_uninit(void) {}
+
+#endif /* CONFIG_FPGA_MGR_DEBUG_FS */
+
+#endif /*_LINUX_FPGA_MGR_DEBUGFS_H */
diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
index c1564cf827fe..67c668bb033c 100644
--- a/drivers/fpga/fpga-mgr.c
+++ b/drivers/fpga/fpga-mgr.c
@@ -17,6 +17,7 @@
#include <linux/slab.h>
#include <linux/scatterlist.h>
#include <linux/highmem.h>
+#include "fpga-mgr-debugfs.h"
static DEFINE_IDA(fpga_mgr_ida);
static struct class *fpga_mgr_class;
@@ -621,6 +622,8 @@ int fpga_mgr_register(struct fpga_manager *mgr)
if (ret)
goto error_device;
+ fpga_mgr_debugfs_add(mgr);
+
dev_info(&mgr->dev, "%s registered\n", mgr->name);
return 0;
@@ -640,6 +643,8 @@ void fpga_mgr_unregister(struct fpga_manager *mgr)
{
dev_info(&mgr->dev, "%s %s\n", __func__, mgr->name);
+ fpga_mgr_debugfs_remove(mgr);
+
/*
* If the low level driver provides a method for putting fpga into
* a desired state upon unregister, do it.
@@ -669,11 +674,14 @@ static int __init fpga_mgr_class_init(void)
fpga_mgr_class->dev_groups = fpga_mgr_groups;
fpga_mgr_class->dev_release = fpga_mgr_dev_release;
+ fpga_mgr_debugfs_init();
+
return 0;
}
static void __exit fpga_mgr_class_exit(void)
{
+ fpga_mgr_debugfs_uninit();
class_destroy(fpga_mgr_class);
ida_destroy(&fpga_mgr_ida);
}
diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
new file mode 100644
index 000000000000..729312ef87ef
--- /dev/null
+++ b/drivers/fpga/stratix10-soc.c
@@ -0,0 +1,536 @@
+/*
+ * FPGA Manager Driver for Intel Stratix10 SoC
+ *
+ * Copyright (C) 2018 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/completion.h>
+#include <linux/fpga/fpga-mgr.h>
+#include <linux/intel-service-client.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+/*
+ * FPGA programming requires a higher level of privilege (EL3), per the SoC
+ * design.
+ */
+#define NUM_SVC_BUFS 4
+#define SVC_BUF_SIZE SZ_512K
+
+/* Indicates buffer is in use if set */
+#define SVC_BUF_LOCK 0
+
+#define S10_BUFFER_TIMEOUT (msecs_to_jiffies(SVC_RECONFIG_BUFFER_TIMEOUT_MS))
+#define S10_RECONFIG_TIMEOUT (msecs_to_jiffies(SVC_RECONFIG_REQUEST_TIMEOUT_MS))
+
+/**
+ * struct s10_svc_buf
+ * @buf: virtual address of buf provided by service layer
+ * @lock: locked if buffer is in use
+ */
+struct s10_svc_buf {
+ char *buf;
+ unsigned long lock;
+};
+
+struct s10_priv {
+ struct intel_svc_chan *chan;
+ struct intel_svc_client client;
+ struct completion status_return_completion;
+ struct s10_svc_buf svc_bufs[NUM_SVC_BUFS];
+ unsigned long status;
+};
+
+static int s10_svc_send_msg(struct s10_priv *priv,
+ enum intel_svc_command_code command,
+ void *payload, u32 payload_length)
+{
+ struct intel_svc_chan *chan = priv->chan;
+ struct intel_svc_client_msg msg;
+ int ret;
+
+ pr_debug("%s cmd=%d payload=%p legnth=%d\n",
+ __func__, command, payload, payload_length);
+
+ msg.command = command;
+ msg.payload = payload;
+ msg.payload_length = payload_length;
+
+ ret = intel_svc_send(chan, &msg);
+ pr_debug("intel_svc_send returned status %d\n", ret);
+
+ return ret;
+}
+
+/**
+ * s10_free_buffers
+ * Free buffers allocated from the service layer's pool that are not in use.
+ * @mgr: fpga manager struct
+ * Free all buffers that are not in use.
+ * Return true when all buffers are freed.
+ */
+static bool s10_free_buffers(struct fpga_manager *mgr)
+{
+ struct s10_priv *priv = mgr->priv;
+ uint num_free = 0;
+ uint i;
+
+ for (i = 0; i < NUM_SVC_BUFS; i++) {
+ if (!priv->svc_bufs[i].buf) {
+ num_free++;
+ continue;
+ }
+
+ if (!test_and_set_bit_lock(SVC_BUF_LOCK,
+ &priv->svc_bufs[i].lock)) {
+ intel_svc_free_memory(priv->chan,
+ priv->svc_bufs[i].buf);
+ priv->svc_bufs[i].buf = NULL;
+ num_free++;
+ }
+ }
+
+ return num_free == NUM_SVC_BUFS;
+}
+
+/**
+ * s10_free_buffer_count
+ * Count how many buffers are not in use.
+ * @mgr: fpga manager struct
+ * Return # of buffers that are not in use.
+ */
+static uint s10_free_buffer_count(struct fpga_manager *mgr)
+{
+ struct s10_priv *priv = mgr->priv;
+ uint num_free = 0;
+ uint i;
+
+ for (i = 0; i < NUM_SVC_BUFS; i++)
+ if (!priv->svc_bufs[i].buf)
+ num_free++;
+
+ return num_free;
+}
+
+/**
+ * s10_unlock_bufs
+ * Given the returned buffer address, match that address to our buffer struct
+ * and unlock that buffer. This marks it as available to be refilled and sent
+ * (or freed).
+ * @priv: private data
+ * @kaddr: kernel address of buffer that was returned from service layer
+ */
+static void s10_unlock_bufs(struct s10_priv *priv, void *kaddr)
+{
+ uint i;
+
+ if (!kaddr)
+ return;
+
+ for (i = 0; i < NUM_SVC_BUFS; i++)
+ if (priv->svc_bufs[i].buf == kaddr) {
+ clear_bit_unlock(SVC_BUF_LOCK,
+ &priv->svc_bufs[i].lock);
+ return;
+ }
+
+ WARN(1, "Unknown buffer returned from service layer %p\n", kaddr);
+}
+
+/**
+ * s10_receive_callback
+ * Callback for service layer to use to provide client (this driver) messages
+ * received through the mailbox.
+ * @client: service layer client struct
+ * @data: message
+ */
+static void s10_receive_callback(struct intel_svc_client *client,
+ struct intel_svc_c_data *data)
+{
+ struct s10_priv *priv = client->priv;
+ u32 status;
+ int i;
+
+ WARN_ONCE(!data, "%s: intel_svc_rc_data = NULL", __func__);
+
+ status = data->status;
+
+ /*
+ * Here we set status bits as we receive them. Elsewhere, we always use
+ * test_and_clear_bit() to check status in priv->status
+ */
+ for (i = 0; i <= SVC_STATUS_RECONFIG_ERROR; i++)
+ if (status & (1 << i))
+ set_bit(i, &priv->status);
+
+ if (status & BIT(SVC_STATUS_RECONFIG_BUFFER_DONE)) {
+ s10_unlock_bufs(priv, data->kaddr1);
+ s10_unlock_bufs(priv, data->kaddr2);
+ s10_unlock_bufs(priv, data->kaddr3);
+ }
+
+ complete(&priv->status_return_completion);
+}
+
+/**
+ * s10_ops_write_init
+ * Prepare for FPGA reconfiguration by requesting partial reconfig and
+ * allocating buffers from the service layer.
+ * @mgr: fpga manager
+ * @info: fpga image info
+ * @buf: fpga image buffer
+ * @count: size of buf in bytes
+ */
+static int s10_ops_write_init(struct fpga_manager *mgr,
+ struct fpga_image_info *info,
+ const char *buf, size_t count)
+{
+ struct s10_priv *priv = mgr->priv;
+ struct device *dev = priv->client.dev;
+ struct intel_command_reconfig_payload payload;
+ char *kbuf;
+ uint i;
+ int ret;
+
+ payload.flags = 0;
+ if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
+ dev_info(dev, "Requesting partial reconfiguration.\n");
+ payload.flags |= BIT(COMMAND_RECONFIG_FLAG_PARTIAL);
+ } else {
+ dev_info(dev, "Requesting full reconfiguration.\n");
+ }
+
+ reinit_completion(&priv->status_return_completion);
+ ret = s10_svc_send_msg(priv, COMMAND_RECONFIG,
+ &payload, sizeof(payload));
+ if (ret < 0)
+ goto init_done;
+
+ ret = wait_for_completion_interruptible_timeout(
+ &priv->status_return_completion, S10_RECONFIG_TIMEOUT);
+ if (!ret) {
+ dev_err(dev, "timeout waiting for RECONFIG_REQUEST\n");
+ ret = -ETIMEDOUT;
+ goto init_done;
+ }
+ if (ret < 0) {
+ dev_err(dev, "error (%d) waiting for RECONFIG_REQUEST\n", ret);
+ goto init_done;
+ }
+
+ ret = 0;
+ if (!test_and_clear_bit(SVC_STATUS_RECONFIG_REQUEST_OK,
+ &priv->status)) {
+ ret = -ETIMEDOUT;
+ goto init_done;
+ }
+
+ /* Allocate buffers from the service layer's pool. */
+ for (i = 0; i < NUM_SVC_BUFS; i++) {
+ kbuf = intel_svc_allocate_memory(priv->chan, SVC_BUF_SIZE);
+ if (!kbuf) {
+ s10_free_buffers(mgr);
+ ret = -ENOMEM;
+ goto init_done;
+ }
+
+ priv->svc_bufs[i].buf = kbuf;
+ priv->svc_bufs[i].lock = 0;
+ }
+
+init_done:
+ intel_svc_done(priv->chan);
+ return ret;
+}
+
+/**
+ * s10_send_buf
+ * Send a buffer to the service layer queue.
+ * @mgr: fpga manager struct
+ * @buf: fpga image buffer
+ * @count: size of buf in bytes
+ * Returns # of bytes transferred or -ENOBUFS if the all the buffers are in use
+ * or if the service queue is full. Never returns 0.
+ */
+static int s10_send_buf(struct fpga_manager *mgr, const char *buf, size_t count)
+
+{
+ struct s10_priv *priv = mgr->priv;
+ struct device *dev = priv->client.dev;
+ void *svc_buf;
+ size_t xfer_sz;
+ int ret;
+ uint i;
+
+ /* get/lock a buffer that that's not being used */
+ for (i = 0; i < NUM_SVC_BUFS; i++)
+ if (!test_and_set_bit_lock(SVC_BUF_LOCK,
+ &priv->svc_bufs[i].lock))
+ break;
+
+ if (i == NUM_SVC_BUFS)
+ return -ENOBUFS;
+
+ xfer_sz = count < SVC_BUF_SIZE ? count : SVC_BUF_SIZE;
+
+ svc_buf = priv->svc_bufs[i].buf;
+ memcpy(svc_buf, buf, xfer_sz);
+ /* Returns -ENOBUFS If service queue is full. */
+ ret = s10_svc_send_msg(priv, COMMAND_RECONFIG_DATA_SUBMIT,
+ svc_buf, xfer_sz);
+ if (ret < 0) {
+ dev_err(dev,
+ "Error while sending data to service layer (%d)", ret);
+ return ret;
+ }
+
+ return xfer_sz;
+}
+
+/**
+ * s10_ops_write
+ *
+ * Send a FPGA image to privileged layers to write to the FPGA. When done
+ * sending, free all service layer buffers we allocated in write_init.
+ *
+ * @mgr: fpga manager
+ * @buf: fpga image buffer
+ * @count: size of buf in bytes
+ * Returns 0 for success or negative errno.
+ */
+static int s10_ops_write(struct fpga_manager *mgr, const char *buf,
+ size_t count)
+{
+ struct s10_priv *priv = mgr->priv;
+ struct device *dev = priv->client.dev;
+ long wait_status;
+ int sent = 0;
+ int ret = 0;
+
+ /*
+ * Loop waiting for buffers to be returned. When a buffer is returned,
+ * reuse it to send more data or free if if all data has been sent.
+ */
+ while (count > 0 || s10_free_buffer_count(mgr) != NUM_SVC_BUFS) {
+ reinit_completion(&priv->status_return_completion);
+
+ if (count > 0) {
+ sent = s10_send_buf(mgr, buf, count);
+ if (sent < 0)
+ continue;
+
+ count -= sent;
+ buf += sent;
+ } else {
+ if (s10_free_buffers(mgr))
+ return 0;
+
+ ret = s10_svc_send_msg(
+ priv, COMMAND_RECONFIG_DATA_CLAIM,
+ NULL, 0);
+ if (ret < 0)
+ break;
+ }
+
+ /*
+ * If callback hasn't already happened, wait for buffers to be
+ * returned from service layer
+ */
+ wait_status = 1; /* not timed out */
+ if (!priv->status)
+ wait_status = wait_for_completion_interruptible_timeout(
+ &priv->status_return_completion,
+ S10_BUFFER_TIMEOUT);
+
+ if (test_and_clear_bit(SVC_STATUS_RECONFIG_BUFFER_DONE,
+ &priv->status) ||
+ test_and_clear_bit(SVC_STATUS_RECONFIG_BUFFER_SUBMITTED,
+ &priv->status)) {
+ ret = 0;
+ continue;
+ }
+
+ if (test_and_clear_bit(SVC_STATUS_RECONFIG_ERROR,
+ &priv->status)) {
+ dev_err(dev, "ERROR - giving up - SVC_STATUS_RECONFIG_ERROR\n");
+ ret = -EFAULT;
+ break;
+ }
+
+ if (!wait_status) {
+ dev_err(dev, "timeout waiting for svc layer buffers\n");
+ ret = -ETIMEDOUT;
+ break;
+ }
+ if (wait_status < 0) {
+ ret = wait_status;
+ dev_err(dev,
+ "error (%d) waiting for svc layer buffers\n",
+ ret);
+ break;
+ }
+ }
+
+ if (!s10_free_buffers(mgr))
+ dev_err(dev, "%s not all buffers were freed\n", __func__);
+
+ return ret;
+}
+
+/**
+ * s10_ops_write_complete
+ * Wait for FPGA configuration to be done
+ * @mgr: fpga manager
+ * @info: fpga image info
+ * Returns 0 for success negative errno.
+ */
+static int s10_ops_write_complete(struct fpga_manager *mgr,
+ struct fpga_image_info *info)
+{
+ struct s10_priv *priv = mgr->priv;
+ struct device *dev = priv->client.dev;
+ unsigned long timeout;
+ int ret;
+
+ timeout = usecs_to_jiffies(info->config_complete_timeout_us);
+
+ do {
+ reinit_completion(&priv->status_return_completion);
+
+ ret = s10_svc_send_msg(priv, COMMAND_RECONFIG_STATUS, NULL, 0);
+ if (ret < 0)
+ break;
+
+ ret = wait_for_completion_interruptible_timeout(
+ &priv->status_return_completion, timeout);
+ if (!ret) {
+ dev_err(dev,
+ "timeout waiting for RECONFIG_COMPLETED\n");
+ ret = -ETIMEDOUT;
+ break;
+ }
+ if (ret < 0) {
+ dev_err(dev,
+ "error (%d) waiting for RECONFIG_COMPLETED\n",
+ ret);
+ break;
+ }
+ /* Not error or timeout, so ret is # of jiffies until timeout */
+ timeout = ret;
+ ret = 0;
+
+ if (test_and_clear_bit(SVC_STATUS_RECONFIG_COMPLETED,
+ &priv->status))
+ break;
+
+ if (test_and_clear_bit(SVC_STATUS_RECONFIG_ERROR,
+ &priv->status)) {
+ dev_err(dev, "ERROR - giving up - SVC_STATUS_RECONFIG_ERROR\n");
+ ret = -EFAULT;
+ break;
+ }
+ } while (1);
+
+ intel_svc_done(priv->chan);
+
+ return ret;
+}
+
+static enum fpga_mgr_states s10_ops_state(struct fpga_manager *mgr)
+{
+ return FPGA_MGR_STATE_UNKNOWN;
+}
+
+static const struct fpga_manager_ops s10_ops = {
+ .state = s10_ops_state,
+ .write_init = s10_ops_write_init,
+ .write = s10_ops_write,
+ .write_complete = s10_ops_write_complete,
+};
+
+static int s10_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct fpga_manager *mgr;
+ struct s10_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->client.dev = dev;
+ priv->client.receive_cb = s10_receive_callback;
+ priv->client.priv = priv;
+
+ priv->chan = request_svc_channel_byname(&priv->client,
+ SVC_CLIENT_FPGA);
+ if (IS_ERR(priv->chan)) {
+ dev_err(dev, "couldn't get service channel (%s)\n",
+ SVC_CLIENT_FPGA);
+ return PTR_ERR(priv->chan);
+ }
+
+ init_completion(&priv->status_return_completion);
+
+ mgr = fpga_mgr_create(dev, "Stratix10 SOC FPGA Manager",
+ &s10_ops, priv);
+ if (!mgr) {
+ free_svc_channel(priv->chan);
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, mgr);
+
+ ret = fpga_mgr_register(mgr);
+ if (ret) {
+ fpga_mgr_free(mgr);
+ free_svc_channel(priv->chan);
+ }
+
+ return ret;
+}
+
+static int s10_remove(struct platform_device *pdev)
+{
+ struct fpga_manager *mgr = platform_get_drvdata(pdev);
+ struct s10_priv *priv = mgr->priv;
+
+ fpga_mgr_unregister(mgr);
+ free_svc_channel(priv->chan);
+
+ return 0;
+}
+
+static const struct of_device_id s10_of_match[] = {
+ { .compatible = "intel,stratix10-soc-fpga-mgr", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, s10_of_match);
+
+static struct platform_driver s10_driver = {
+ .probe = s10_probe,
+ .remove = s10_remove,
+ .driver = {
+ .name = "Stratix10 SoC FPGA manager",
+ .of_match_table = of_match_ptr(s10_of_match),
+ },
+};
+
+module_platform_driver(s10_driver);
+
+MODULE_AUTHOR("Alan Tull <atull@kernel.org>");
+MODULE_DESCRIPTION("Intel Stratix 10 SOC FPGA Manager");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index a3049128a0a8..d8c6fc6572fc 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -245,6 +245,15 @@ config SENSORS_ADT7475
This driver can also be build as a module. If so, the module
will be called adt7475.
+config SENSORS_ALTERA_A10SR
+ bool "Altera Arria10 System Status"
+ depends on MFD_ALTERA_A10SR
+ help
+ If you say yes here you get support for the power ready status
+ for the Arria10's external power supplies on the Arria10 DevKit.
+ These values are read over the SPI bus from the Arria10 System
+ Resource chip.
+
config SENSORS_ASC7621
tristate "Andigilog aSC7621"
depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index e7d52a36e6c4..6aecca24fa29 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_SENSORS_ADT7411) += adt7411.o
obj-$(CONFIG_SENSORS_ADT7462) += adt7462.o
obj-$(CONFIG_SENSORS_ADT7470) += adt7470.o
obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o
+obj-$(CONFIG_SENSORS_ALTERA_A10SR) += altera-a10sr-hwmon.o
obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o
obj-$(CONFIG_SENSORS_ARM_SCMI) += scmi-hwmon.o
obj-$(CONFIG_SENSORS_ARM_SCPI) += scpi-hwmon.o
diff --git a/drivers/hwmon/altera-a10sr-hwmon.c b/drivers/hwmon/altera-a10sr-hwmon.c
new file mode 100644
index 000000000000..584cc48a9b1b
--- /dev/null
+++ b/drivers/hwmon/altera-a10sr-hwmon.c
@@ -0,0 +1,406 @@
+/*
+ * Copyright Intel Corporation (C) 2017-2018. All Rights Reserved
+ * Copyright Altera Corporation (C) 2014-2016. All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * HW Monitor driver for Altera Arria10 MAX5 System Resource Chip
+ * Adapted from DA9052
+ */
+
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/mfd/altera-a10sr.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#define ALTR_A10SR_1V0_BIT_POS ALTR_A10SR_PG1_1V0_SHIFT
+#define ALTR_A10SR_0V95_BIT_POS ALTR_A10SR_PG1_0V95_SHIFT
+#define ALTR_A10SR_0V9_BIT_POS ALTR_A10SR_PG1_0V9_SHIFT
+#define ALTR_A10SR_10V_BIT_POS ALTR_A10SR_PG1_10V_SHIFT
+#define ALTR_A10SR_5V0_BIT_POS ALTR_A10SR_PG1_5V0_SHIFT
+#define ALTR_A10SR_3V3_BIT_POS ALTR_A10SR_PG1_3V3_SHIFT
+#define ALTR_A10SR_2V5_BIT_POS ALTR_A10SR_PG1_2V5_SHIFT
+#define ALTR_A10SR_1V8_BIT_POS ALTR_A10SR_PG1_1V8_SHIFT
+#define ALTR_A10SR_OP_FLAG_BIT_POS ALTR_A10SR_PG1_OP_FLAG_SHIFT
+/* 2nd register needs an offset of 8 to get to 2nd register */
+#define ALTR_A10SR_FBC2MP_BIT_POS (8 + ALTR_A10SR_PG2_FBC2MP_SHIFT)
+#define ALTR_A10SR_FAC2MP_BIT_POS (8 + ALTR_A10SR_PG2_FAC2MP_SHIFT)
+#define ALTR_A10SR_FMCBVADJ_BIT_POS (8 + ALTR_A10SR_PG2_FMCBVADJ_SHIFT)
+#define ALTR_A10SR_FMCAVADJ_BIT_POS (8 + ALTR_A10SR_PG2_FMCAVADJ_SHIFT)
+#define ALTR_A10SR_HL_VDDQ_BIT_POS (8 + ALTR_A10SR_PG2_HL_VDDQ_SHIFT)
+#define ALTR_A10SR_HL_VDD_BIT_POS (8 + ALTR_A10SR_PG2_HL_VDD_SHIFT)
+#define ALTR_A10SR_HL_HPS_BIT_POS (8 + ALTR_A10SR_PG2_HL_HPS_SHIFT)
+#define ALTR_A10SR_HPS_BIT_POS (8 + ALTR_A10SR_PG2_HPS_SHIFT)
+/* 3rd register needs an offset of 16 to get to 3rd register */
+#define ALTR_A10SR_PCIE_WAKE_BIT_POS (16 + ALTR_A10SR_PG3_PCIE_WAKE_SHIFT)
+#define ALTR_A10SR_PCIE_PR_BIT_POS (16 + ALTR_A10SR_PG3_PCIE_PR_SHIFT)
+#define ALTR_A10SR_FMCB_PR_BIT_POS (16 + ALTR_A10SR_PG3_FMCB_PR_SHIFT)
+#define ALTR_A10SR_FMCA_PR_BIT_POS (16 + ALTR_A10SR_PG3_FMCA_PR_SHIFT)
+#define ALTR_A10SR_FILE_PR_BIT_POS (16 + ALTR_A10SR_PG3_FILE_PR_SHIFT)
+#define ALTR_A10SR_BF_PR_BIT_POS (16 + ALTR_A10SR_PG3_BF_PR_SHIFT)
+#define ALTR_A10SR_10V_FAIL_BIT_POS (16 + ALTR_A10SR_PG3_10V_FAIL_SHIFT)
+#define ALTR_A10SR_FAM2C_BIT_POS (16 + ALTR_A10SR_PG3_FAM2C_SHIFT)
+/* FMCA/B & PCIE Enables need an offset of 24 */
+#define ALTR_A10SR_FMCB_AUXEN_POS (24 + ALTR_A10SR_FMCB_AUXEN_SHIFT)
+#define ALTR_A10SR_FMCB_EN_POS (24 + ALTR_A10SR_FMCB_EN_SHIFT)
+#define ALTR_A10SR_FMCA_AUXEN_POS (24 + ALTR_A10SR_FMCA_AUXEN_SHIFT)
+#define ALTR_A10SR_FMCA_EN_POS (24 + ALTR_A10SR_FMCA_EN_SHIFT)
+#define ALTR_A10SR_PCIE_AUXEN_POS (24 + ALTR_A10SR_PCIE_AUXEN_SHIFT)
+#define ALTR_A10SR_PCIE_EN_POS (24 + ALTR_A10SR_PCIE_EN_SHIFT)
+/* HPS Resets need an offset of 32 */
+#define ALTR_A10SR_HPS_RST_UART_POS (32 + ALTR_A10SR_HPS_UARTA_RSTN_SHIFT)
+#define ALTR_A10SR_HPS_RST_WARM_POS (32 + ALTR_A10SR_HPS_WARM_RSTN_SHIFT)
+#define ALTR_A10SR_HPS_RST_WARM1_POS (32 + ALTR_A10SR_HPS_WARM_RST1N_SHIFT)
+#define ALTR_A10SR_HPS_RST_COLD_POS (32 + ALTR_A10SR_HPS_COLD_RSTN_SHIFT)
+#define ALTR_A10SR_HPS_RST_NPOR_POS (32 + ALTR_A10SR_HPS_NPOR_SHIFT)
+#define ALTR_A10SR_HPS_RST_NRST_POS (32 + ALTR_A10SR_HPS_NRST_SHIFT)
+#define ALTR_A10SR_HPS_RST_ENET_POS (32 + ALTR_A10SR_HPS_ENET_RSTN_SHIFT)
+#define ALTR_A10SR_HPS_RST_ENETINT_POS (32 + ALTR_A10SR_HPS_ENET_INTN_SHIFT)
+/* Peripheral Resets need an offset of 40 */
+#define ALTR_A10SR_PER_RST_USB_POS (40 + ALTR_A10SR_USB_RST_SHIFT)
+#define ALTR_A10SR_PER_RST_BQSPI_POS (40 + ALTR_A10SR_BQSPI_RST_N_SHIFT)
+#define ALTR_A10SR_PER_RST_FILE_POS (40 + ALTR_A10SR_FILE_RST_N_SHIFT)
+#define ALTR_A10SR_PER_RST_PCIE_POS (40 + ALTR_A10SR_PCIE_PERST_N_SHIFT)
+/* HWMON - Read Entire Register */
+#define ALTR_A10SR_ENTIRE_REG (88)
+#define ALTR_A10SR_ENTIRE_REG_MASK (0xFF)
+#define ALTR_A10SR_VERSION (0 + ALTR_A10SR_ENTIRE_REG)
+#define ALTR_A10SR_LED (1 + ALTR_A10SR_ENTIRE_REG)
+#define ALTR_A10SR_PB (2 + ALTR_A10SR_ENTIRE_REG)
+#define ALTR_A10SR_PBF (3 + ALTR_A10SR_ENTIRE_REG)
+#define ALTR_A10SR_PG1 (4 + ALTR_A10SR_ENTIRE_REG)
+#define ALTR_A10SR_PG2 (5 + ALTR_A10SR_ENTIRE_REG)
+#define ALTR_A10SR_PG3 (6 + ALTR_A10SR_ENTIRE_REG)
+#define ALTR_A10SR_FMCAB (7 + ALTR_A10SR_ENTIRE_REG)
+#define ALTR_A10SR_HPS_RST (8 + ALTR_A10SR_ENTIRE_REG)
+#define ALTR_A10SR_PER_RST (9 + ALTR_A10SR_ENTIRE_REG)
+#define ALTR_A10SR_SFPA (10 + ALTR_A10SR_ENTIRE_REG)
+#define ALTR_A10SR_SFPB (11 + ALTR_A10SR_ENTIRE_REG)
+#define ALTR_A10SR_I2C_MASTER (12 + ALTR_A10SR_ENTIRE_REG)
+#define ALTR_A10SR_WARM_RST (13 + ALTR_A10SR_ENTIRE_REG)
+#define ALTR_A10SR_WARM_RST_KEY (14 + ALTR_A10SR_ENTIRE_REG)
+#define ALTR_A10SR_PMBUS (15 + ALTR_A10SR_ENTIRE_REG)
+
+/**
+ * struct altr_a10sr_hwmon - Altera Max5 HWMON device private data structure
+ * @device: hwmon class.
+ * @regmap: the regmap from the parent device.
+ */
+struct altr_a10sr_hwmon {
+ struct regmap *regmap;
+};
+
+static ssize_t altr_a10sr_read_status(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct altr_a10sr_hwmon *hwmon = dev_get_drvdata(dev);
+ int val, ret, index = to_sensor_dev_attr(devattr)->index;
+ int mask = ALTR_A10SR_REG_BIT_MASK(index);
+ unsigned char reg = ALTR_A10SR_PWR_GOOD1_REG +
+ ALTR_A10SR_REG_OFFSET(index);
+
+ /* Check if this is an entire register read */
+ if (index >= ALTR_A10SR_ENTIRE_REG) {
+ reg = ((index - ALTR_A10SR_ENTIRE_REG) << 1);
+ mask = ALTR_A10SR_ENTIRE_REG_MASK;
+ }
+
+ ret = regmap_read(hwmon->regmap, reg, &val);
+ if (ret < 0)
+ return ret;
+
+ if (mask == ALTR_A10SR_ENTIRE_REG_MASK)
+ val = val & mask;
+ else
+ val = !!(val & mask);
+
+ return scnprintf(buf, 5, "%d\n", val);
+}
+
+static ssize_t set_enable(struct device *dev,
+ struct device_attribute *dev_attr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ struct altr_a10sr_hwmon *hwmon = dev_get_drvdata(dev);
+ int ret, index = to_sensor_dev_attr(dev_attr)->index;
+ int mask = ALTR_A10SR_REG_BIT_MASK(index);
+ unsigned char reg = (ALTR_A10SR_PWR_GOOD1_REG & WRITE_REG_MASK) +
+ ALTR_A10SR_REG_OFFSET(index);
+ int res = kstrtol(buf, 10, &val);
+
+ if (res < 0)
+ return res;
+
+ /* Check if this is an entire register write */
+ if (index >= ALTR_A10SR_ENTIRE_REG) {
+ reg = ((index - ALTR_A10SR_ENTIRE_REG) << 1);
+ mask = ALTR_A10SR_ENTIRE_REG_MASK;
+ }
+
+ ret = regmap_update_bits(hwmon->regmap, reg, mask, val);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+/* First Power Good Register Bits */
+static SENSOR_DEVICE_ATTR(1v0_alarm, S_IRUGO, altr_a10sr_read_status, NULL,
+ ALTR_A10SR_1V0_BIT_POS);
+static SENSOR_DEVICE_ATTR(0v95_alarm, S_IRUGO, altr_a10sr_read_status, NULL,
+ ALTR_A10SR_0V95_BIT_POS);
+static SENSOR_DEVICE_ATTR(0v9_alarm, S_IRUGO, altr_a10sr_read_status, NULL,
+ ALTR_A10SR_0V9_BIT_POS);
+static SENSOR_DEVICE_ATTR(5v0_alarm, S_IRUGO, altr_a10sr_read_status, NULL,
+ ALTR_A10SR_5V0_BIT_POS);
+static SENSOR_DEVICE_ATTR(3v3_alarm, S_IRUGO, altr_a10sr_read_status, NULL,
+ ALTR_A10SR_3V3_BIT_POS);
+static SENSOR_DEVICE_ATTR(2v5_alarm, S_IRUGO, altr_a10sr_read_status, NULL,
+ ALTR_A10SR_2V5_BIT_POS);
+static SENSOR_DEVICE_ATTR(1v8_alarm, S_IRUGO, altr_a10sr_read_status, NULL,
+ ALTR_A10SR_1V8_BIT_POS);
+static SENSOR_DEVICE_ATTR(opflag_alarm, S_IRUGO, altr_a10sr_read_status, NULL,
+ ALTR_A10SR_OP_FLAG_BIT_POS);
+/* Second Power Good Register Bits */
+static SENSOR_DEVICE_ATTR(fbc2mp_alarm, S_IRUGO, altr_a10sr_read_status, NULL,
+ ALTR_A10SR_FBC2MP_BIT_POS);
+static SENSOR_DEVICE_ATTR(fac2mp_alarm, S_IRUGO, altr_a10sr_read_status, NULL,
+ ALTR_A10SR_FAC2MP_BIT_POS);
+static SENSOR_DEVICE_ATTR(fmcbvadj_alarm, S_IRUGO, altr_a10sr_read_status, NULL,
+ ALTR_A10SR_FMCBVADJ_BIT_POS);
+static SENSOR_DEVICE_ATTR(fmcavadj_alarm, S_IRUGO, altr_a10sr_read_status, NULL,
+ ALTR_A10SR_FMCAVADJ_BIT_POS);
+static SENSOR_DEVICE_ATTR(hl_vddq_alarm, S_IRUGO, altr_a10sr_read_status, NULL,
+ ALTR_A10SR_HL_VDDQ_BIT_POS);
+static SENSOR_DEVICE_ATTR(hl_vdd_alarm, S_IRUGO, altr_a10sr_read_status, NULL,
+ ALTR_A10SR_HL_VDD_BIT_POS);
+static SENSOR_DEVICE_ATTR(hlhps_vdd_alarm, S_IRUGO, altr_a10sr_read_status,
+ NULL, ALTR_A10SR_HL_HPS_BIT_POS);
+static SENSOR_DEVICE_ATTR(hps_alarm, S_IRUGO, altr_a10sr_read_status, NULL,
+ ALTR_A10SR_HPS_BIT_POS);
+/* Third Power Good Register Bits */
+static SENSOR_DEVICE_ATTR(pcie_wake_input, S_IRUGO, altr_a10sr_read_status,
+ NULL, ALTR_A10SR_PCIE_WAKE_BIT_POS);
+static SENSOR_DEVICE_ATTR(pcie_pr_input, S_IRUGO, altr_a10sr_read_status, NULL,
+ ALTR_A10SR_PCIE_PR_BIT_POS);
+static SENSOR_DEVICE_ATTR(fmcb_pr_input, S_IRUGO, altr_a10sr_read_status, NULL,
+ ALTR_A10SR_FMCB_PR_BIT_POS);
+static SENSOR_DEVICE_ATTR(fmca_pr_input, S_IRUGO, altr_a10sr_read_status, NULL,
+ ALTR_A10SR_FMCA_PR_BIT_POS);
+static SENSOR_DEVICE_ATTR(file_pr_input, S_IRUGO, altr_a10sr_read_status, NULL,
+ ALTR_A10SR_FILE_PR_BIT_POS);
+static SENSOR_DEVICE_ATTR(bf_pr_input, S_IRUGO, altr_a10sr_read_status, NULL,
+ ALTR_A10SR_BF_PR_BIT_POS);
+static SENSOR_DEVICE_ATTR(10v_alarm, S_IRUGO, altr_a10sr_read_status,
+ NULL, ALTR_A10SR_10V_FAIL_BIT_POS);
+static SENSOR_DEVICE_ATTR(fam2c_alarm, S_IRUGO, altr_a10sr_read_status, NULL,
+ ALTR_A10SR_FAM2C_BIT_POS);
+/* Peripheral Enable bits */
+static SENSOR_DEVICE_ATTR(fmcb_aux_en, S_IRUGO | S_IWUSR,
+ altr_a10sr_read_status, set_enable,
+ ALTR_A10SR_FMCB_AUXEN_POS);
+static SENSOR_DEVICE_ATTR(fmcb_en, S_IRUGO | S_IWUSR,
+ altr_a10sr_read_status, set_enable,
+ ALTR_A10SR_FMCB_EN_POS);
+static SENSOR_DEVICE_ATTR(fmca_aux_en, S_IRUGO | S_IWUSR,
+ altr_a10sr_read_status, set_enable,
+ ALTR_A10SR_FMCA_AUXEN_POS);
+static SENSOR_DEVICE_ATTR(fmca_en, S_IRUGO | S_IWUSR,
+ altr_a10sr_read_status, set_enable,
+ ALTR_A10SR_FMCA_EN_POS);
+static SENSOR_DEVICE_ATTR(pcie_aux_en, S_IRUGO | S_IWUSR,
+ altr_a10sr_read_status, set_enable,
+ ALTR_A10SR_PCIE_AUXEN_POS);
+static SENSOR_DEVICE_ATTR(pcie_en, S_IRUGO | S_IWUSR,
+ altr_a10sr_read_status, set_enable,
+ ALTR_A10SR_PCIE_EN_POS);
+/* HPS Reset bits */
+static SENSOR_DEVICE_ATTR(hps_uart_rst, S_IRUGO,
+ altr_a10sr_read_status, set_enable,
+ ALTR_A10SR_HPS_RST_UART_POS);
+static SENSOR_DEVICE_ATTR(hps_warm_rst, S_IRUGO,
+ altr_a10sr_read_status, set_enable,
+ ALTR_A10SR_HPS_RST_WARM_POS);
+static SENSOR_DEVICE_ATTR(hps_warm1_rst, S_IRUGO,
+ altr_a10sr_read_status, set_enable,
+ ALTR_A10SR_HPS_RST_WARM1_POS);
+static SENSOR_DEVICE_ATTR(hps_cold_rst, S_IRUGO,
+ altr_a10sr_read_status, set_enable,
+ ALTR_A10SR_HPS_RST_COLD_POS);
+static SENSOR_DEVICE_ATTR(hps_npor, S_IRUGO,
+ altr_a10sr_read_status, set_enable,
+ ALTR_A10SR_HPS_RST_NPOR_POS);
+static SENSOR_DEVICE_ATTR(hps_nrst, S_IRUGO,
+ altr_a10sr_read_status, set_enable,
+ ALTR_A10SR_HPS_RST_NRST_POS);
+static SENSOR_DEVICE_ATTR(hps_enet_rst, S_IRUGO | S_IWUSR,
+ altr_a10sr_read_status, set_enable,
+ ALTR_A10SR_HPS_RST_ENET_POS);
+static SENSOR_DEVICE_ATTR(hps_enet_int, S_IRUGO | S_IWUSR,
+ altr_a10sr_read_status, set_enable,
+ ALTR_A10SR_HPS_RST_ENETINT_POS);
+/* Peripheral Reset bits */
+static SENSOR_DEVICE_ATTR(usb_reset, S_IRUGO | S_IWUSR, altr_a10sr_read_status,
+ set_enable, ALTR_A10SR_PER_RST_USB_POS);
+static SENSOR_DEVICE_ATTR(bqspi_resetn, S_IRUGO | S_IWUSR,
+ altr_a10sr_read_status, set_enable,
+ ALTR_A10SR_PER_RST_BQSPI_POS);
+static SENSOR_DEVICE_ATTR(file_resetn, S_IRUGO | S_IWUSR,
+ altr_a10sr_read_status, set_enable,
+ ALTR_A10SR_PER_RST_FILE_POS);
+static SENSOR_DEVICE_ATTR(pcie_perstn, S_IRUGO | S_IWUSR,
+ altr_a10sr_read_status, set_enable,
+ ALTR_A10SR_PER_RST_PCIE_POS);
+/* Entire Byte Read */
+static SENSOR_DEVICE_ATTR(max5_version, S_IRUGO, altr_a10sr_read_status,
+ NULL, ALTR_A10SR_VERSION);
+static SENSOR_DEVICE_ATTR(max5_led, S_IRUGO, altr_a10sr_read_status,
+ NULL, ALTR_A10SR_LED);
+static SENSOR_DEVICE_ATTR(max5_button, S_IRUGO, altr_a10sr_read_status,
+ NULL, ALTR_A10SR_PB);
+static SENSOR_DEVICE_ATTR(max5_button_irq, S_IRUGO | S_IWUSR,
+ altr_a10sr_read_status, set_enable, ALTR_A10SR_PBF);
+static SENSOR_DEVICE_ATTR(max5_pg1, S_IRUGO, altr_a10sr_read_status,
+ NULL, ALTR_A10SR_PG1);
+static SENSOR_DEVICE_ATTR(max5_pg2, S_IRUGO, altr_a10sr_read_status,
+ NULL, ALTR_A10SR_PG2);
+static SENSOR_DEVICE_ATTR(max5_pg3, S_IRUGO, altr_a10sr_read_status,
+ NULL, ALTR_A10SR_PG3);
+static SENSOR_DEVICE_ATTR(max5_fmcab, S_IRUGO, altr_a10sr_read_status,
+ NULL, ALTR_A10SR_FMCAB);
+static SENSOR_DEVICE_ATTR(max5_hps_resets, S_IRUGO | S_IWUSR,
+ altr_a10sr_read_status, set_enable,
+ ALTR_A10SR_HPS_RST);
+static SENSOR_DEVICE_ATTR(max5_per_resets, S_IRUGO | S_IWUSR,
+ altr_a10sr_read_status, set_enable,
+ ALTR_A10SR_PER_RST);
+static SENSOR_DEVICE_ATTR(max5_sfpa, S_IRUGO | S_IWUSR,
+ altr_a10sr_read_status, set_enable, ALTR_A10SR_SFPA);
+static SENSOR_DEVICE_ATTR(max5_sfpb, S_IRUGO | S_IWUSR,
+ altr_a10sr_read_status, set_enable, ALTR_A10SR_SFPB);
+static SENSOR_DEVICE_ATTR(max5_i2c_master, S_IRUGO | S_IWUSR,
+ altr_a10sr_read_status, set_enable,
+ ALTR_A10SR_I2C_MASTER);
+static SENSOR_DEVICE_ATTR(max5_pmbus, S_IRUGO | S_IWUSR,
+ altr_a10sr_read_status, set_enable,
+ ALTR_A10SR_PMBUS);
+
+static struct attribute *altr_a10sr_attrs[] = {
+ /* First Power Good Register */
+ &sensor_dev_attr_opflag_alarm.dev_attr.attr,
+ &sensor_dev_attr_1v8_alarm.dev_attr.attr,
+ &sensor_dev_attr_2v5_alarm.dev_attr.attr,
+ &sensor_dev_attr_1v0_alarm.dev_attr.attr,
+ &sensor_dev_attr_3v3_alarm.dev_attr.attr,
+ &sensor_dev_attr_5v0_alarm.dev_attr.attr,
+ &sensor_dev_attr_0v9_alarm.dev_attr.attr,
+ &sensor_dev_attr_0v95_alarm.dev_attr.attr,
+ /* Second Power Good Register */
+ &sensor_dev_attr_hps_alarm.dev_attr.attr,
+ &sensor_dev_attr_hlhps_vdd_alarm.dev_attr.attr,
+ &sensor_dev_attr_hl_vdd_alarm.dev_attr.attr,
+ &sensor_dev_attr_hl_vddq_alarm.dev_attr.attr,
+ &sensor_dev_attr_fmcavadj_alarm.dev_attr.attr,
+ &sensor_dev_attr_fmcbvadj_alarm.dev_attr.attr,
+ &sensor_dev_attr_fac2mp_alarm.dev_attr.attr,
+ &sensor_dev_attr_fbc2mp_alarm.dev_attr.attr,
+ /* Third Power Good Register */
+ &sensor_dev_attr_pcie_wake_input.dev_attr.attr,
+ &sensor_dev_attr_pcie_pr_input.dev_attr.attr,
+ &sensor_dev_attr_fmcb_pr_input.dev_attr.attr,
+ &sensor_dev_attr_fmca_pr_input.dev_attr.attr,
+ &sensor_dev_attr_file_pr_input.dev_attr.attr,
+ &sensor_dev_attr_bf_pr_input.dev_attr.attr,
+ &sensor_dev_attr_10v_alarm.dev_attr.attr,
+ &sensor_dev_attr_fam2c_alarm.dev_attr.attr,
+/* Peripheral Enable Register */
+ &sensor_dev_attr_fmcb_aux_en.dev_attr.attr,
+ &sensor_dev_attr_fmcb_en.dev_attr.attr,
+ &sensor_dev_attr_fmca_aux_en.dev_attr.attr,
+ &sensor_dev_attr_fmca_en.dev_attr.attr,
+ &sensor_dev_attr_pcie_aux_en.dev_attr.attr,
+ &sensor_dev_attr_pcie_en.dev_attr.attr,
+ /* HPS Reset bits */
+ &sensor_dev_attr_hps_uart_rst.dev_attr.attr,
+ &sensor_dev_attr_hps_warm_rst.dev_attr.attr,
+ &sensor_dev_attr_hps_warm1_rst.dev_attr.attr,
+ &sensor_dev_attr_hps_cold_rst.dev_attr.attr,
+ &sensor_dev_attr_hps_npor.dev_attr.attr,
+ &sensor_dev_attr_hps_nrst.dev_attr.attr,
+ &sensor_dev_attr_hps_enet_rst.dev_attr.attr,
+ &sensor_dev_attr_hps_enet_int.dev_attr.attr,
+ /* Peripheral Reset bits */
+ &sensor_dev_attr_usb_reset.dev_attr.attr,
+ &sensor_dev_attr_bqspi_resetn.dev_attr.attr,
+ &sensor_dev_attr_file_resetn.dev_attr.attr,
+ &sensor_dev_attr_pcie_perstn.dev_attr.attr,
+ /* Byte Value Register */
+ &sensor_dev_attr_max5_version.dev_attr.attr,
+ &sensor_dev_attr_max5_led.dev_attr.attr,
+ &sensor_dev_attr_max5_button.dev_attr.attr,
+ &sensor_dev_attr_max5_button_irq.dev_attr.attr,
+ &sensor_dev_attr_max5_pg1.dev_attr.attr,
+ &sensor_dev_attr_max5_pg2.dev_attr.attr,
+ &sensor_dev_attr_max5_pg3.dev_attr.attr,
+ &sensor_dev_attr_max5_fmcab.dev_attr.attr,
+ &sensor_dev_attr_max5_hps_resets.dev_attr.attr,
+ &sensor_dev_attr_max5_per_resets.dev_attr.attr,
+ &sensor_dev_attr_max5_sfpa.dev_attr.attr,
+ &sensor_dev_attr_max5_sfpb.dev_attr.attr,
+ &sensor_dev_attr_max5_i2c_master.dev_attr.attr,
+ &sensor_dev_attr_max5_pmbus.dev_attr.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(altr_a10sr);
+
+static int altr_a10sr_hwmon_probe(struct platform_device *pdev)
+{
+ struct altr_a10sr_hwmon *hwmon;
+ struct device *hwmon_dev;
+ struct altr_a10sr *a10sr = dev_get_drvdata(pdev->dev.parent);
+
+ hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon), GFP_KERNEL);
+ if (!hwmon)
+ return -ENOMEM;
+
+ hwmon->regmap = a10sr->regmap;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev,
+ "a10sr_hwmon", hwmon,
+ altr_a10sr_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct of_device_id altr_a10sr_hwmon_of_match[] = {
+ { .compatible = "altr,a10sr-hwmon" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, altr_a10sr_hwmon_of_match);
+
+static struct platform_driver altr_a10sr_hwmon_driver = {
+ .probe = altr_a10sr_hwmon_probe,
+ .driver = {
+ .name = "altr_a10sr_hwmon",
+ .of_match_table = of_match_ptr(altr_a10sr_hwmon_of_match),
+ },
+};
+
+module_platform_driver(altr_a10sr_hwmon_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Thor Thayer <tthayer@opensource.altera.com>");
+MODULE_DESCRIPTION("HW Monitor driver for Altera Arria10 System Resource Chip");
diff --git a/drivers/mfd/altera-a10sr.c b/drivers/mfd/altera-a10sr.c
index 96e7d2cb7b89..c57b5493ebcc 100644
--- a/drivers/mfd/altera-a10sr.c
+++ b/drivers/mfd/altera-a10sr.c
@@ -30,6 +30,10 @@
static const struct mfd_cell altr_a10sr_subdev_info[] = {
{
+ .name = "altr_a10sr_hwmon",
+ .of_compatible = "altr,a10sr-hwmon",
+ },
+ {
.name = "altr_a10sr_gpio",
.of_compatible = "altr,a10sr-gpio",
},
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 3726eacdf65d..4c7d95e7e465 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -152,6 +152,36 @@ config INTEL_MID_PTI
an Intel Atom (non-netbook) mobile device containing a MIPI
P1149.7 standard implementation.
+config INTEL_SERVICE
+ tristate "Intel Service Layer"
+ depends on ARCH_STRATIX10
+ default n
+ help
+ Intel service layer runs at privileged exception level, interfaces with
+ the service providers (FPGA manager is one of them) and manages secure
+ monitor call to communicate with secure monitor software at secure monitor
+ exception level.
+
+ Say Y here if you want Intel service layer support.
+
+config INTEL_RSU
+ tristate "Intel Remote System Update"
+ depends on INTEL_SERVICE
+ help
+ The Intel Remote System Update (RSU) driver exposes interfaces
+ accessed through the Intel Service Layer to user space via SysFS
+ device attribute nodes. The RSU interfaces report/control some of
+ the optional RSU features of the Stratix 10 SoC FPGA.
+
+ The RSU feature provides a way for customers to update the boot
+ configuration of a Stratix 10 SoC device with significantly reduced
+ risk of corrupting the bitstream storage and bricking the system.
+
+ Enable RSU support if you are using an Intel SoC FPGA with the RSU
+ feature enabled and you want Linux user space control.
+
+ Say Y here if you want Intel RSU support.
+
config SGI_IOC4
tristate "SGI IOC4 Base IO support"
depends on PCI
@@ -513,6 +543,17 @@ config MISC_RTSX
tristate
default MISC_RTSX_PCI || MISC_RTSX_USB
+config ALTERA_SYSID
+ tristate "Altera System ID"
+ help
+ This enables Altera System ID soft core driver.
+
+config ALTERA_ILC
+ tristate "Altera Interrupt Latency Counter driver"
+ help
+ This enables the Interrupt Latency Counter driver for the Altera
+ SOCFPGA platform.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index af22bbc3d00c..8df0ba395767 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -9,6 +9,8 @@ obj-$(CONFIG_AD525X_DPOT) += ad525x_dpot.o
obj-$(CONFIG_AD525X_DPOT_I2C) += ad525x_dpot-i2c.o
obj-$(CONFIG_AD525X_DPOT_SPI) += ad525x_dpot-spi.o
obj-$(CONFIG_INTEL_MID_PTI) += pti.o
+obj-$(CONFIG_INTEL_SERVICE) += intel-service.o
+obj-$(CONFIG_INTEL_RSU) += intel-rsu.o
obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o
obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o
obj-$(CONFIG_DUMMY_IRQ) += dummy-irq.o
@@ -42,7 +44,10 @@ obj-$(CONFIG_PCH_PHUB) += pch_phub.o
obj-y += ti-st/
obj-y += lis3lv02d/
obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
-obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/
+obj-$(CONFIG_ALTERA_STAPL) += altera-stapl/
+obj-$(CONFIG_ALTERA_HWMUTEX) += altera_hwmutex.o
+obj-$(CONFIG_ALTERA_ILC) += altera_ilc.o
+obj-$(CONFIG_ALTERA_SYSID) += altera_sysid.o
obj-$(CONFIG_INTEL_MEI) += mei/
obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/
obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o
diff --git a/drivers/misc/altera_hwmutex.c b/drivers/misc/altera_hwmutex.c
new file mode 100644
index 000000000000..5004fe92e94b
--- /dev/null
+++ b/drivers/misc/altera_hwmutex.c
@@ -0,0 +1,320 @@
+/*
+ * Copyright Altera Corporation (C) 2013. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/altera_hwmutex.h>
+
+#define DRV_NAME "altera_hwmutex"
+
+
+static DEFINE_SPINLOCK(list_lock); /* protect mutex_list */
+static LIST_HEAD(mutex_list);
+
+/* Mutex Registers */
+#define MUTEX_REG 0x0
+
+#define MUTEX_REG_VALUE_MASK 0xFFFF
+#define MUTEX_REG_OWNER_OFFSET 16
+#define MUTEX_REG_OWNER_MASK 0xFFFF
+#define MUTEX_GET_OWNER(reg) \
+ ((reg >> MUTEX_REG_OWNER_OFFSET) & MUTEX_REG_OWNER_MASK)
+
+/**
+ * altera_mutex_request - Retrieves a pointer to an acquired mutex device
+ * structure
+ * @mutex_np: The pointer to mutex device node
+ *
+ * Returns a pointer to the mutex device structure associated with the
+ * supplied device node, or NULL if no corresponding mutex device was
+ * found.
+ */
+struct altera_mutex *altera_mutex_request(struct device_node *mutex_np)
+{
+ struct altera_mutex *mutex;
+
+ spin_lock(&list_lock);
+ list_for_each_entry(mutex, &mutex_list, list) {
+ if (mutex_np == mutex->pdev->dev.of_node) {
+ if (!mutex->requested) {
+ mutex->requested = true;
+ spin_unlock(&list_lock);
+ return mutex;
+ } else {
+ pr_info("Mutex device is in use.\n");
+ spin_unlock(&list_lock);
+ return NULL;
+ }
+ }
+ }
+ spin_unlock(&list_lock);
+ pr_info("Mutex device not found!\n");
+ return NULL;
+}
+EXPORT_SYMBOL(altera_mutex_request);
+
+/**
+ * altera_mutex_free - Free the mutex
+ * @mutex: the mutex
+ *
+ * Return 0 if success. Otherwise, returns non-zero.
+ */
+int altera_mutex_free(struct altera_mutex *mutex)
+{
+ if (!mutex || !mutex->requested)
+ return -EINVAL;
+
+ spin_lock(&list_lock);
+ mutex->requested = false;
+ spin_unlock(&list_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(altera_mutex_free);
+
+static int __mutex_trylock(struct altera_mutex *mutex, u16 owner, u16 value)
+{
+ u32 read;
+ int ret = 0;
+ u32 data = (owner << MUTEX_REG_OWNER_OFFSET) | value;
+
+ mutex_lock(&mutex->lock);
+ __raw_writel(data, mutex->regs + MUTEX_REG);
+ read = __raw_readl(mutex->regs + MUTEX_REG);
+ if (read != data)
+ ret = -1;
+
+ mutex_unlock(&mutex->lock);
+ return ret;
+}
+
+/**
+ * altera_mutex_lock - Acquires a hardware mutex, wait until it can get it.
+ * @mutex: the mutex to be acquired
+ * @owner: owner ID
+ * @value: the new non-zero value to write to mutex
+ *
+ * Returns 0 if mutex was successfully locked. Otherwise, returns non-zero.
+ *
+ * The mutex must later on be released by the same owner that acquired it.
+ * This function is not ISR callable.
+ */
+int altera_mutex_lock(struct altera_mutex *mutex, u16 owner, u16 value)
+{
+ if (!mutex || !mutex->requested)
+ return -EINVAL;
+
+ while (__mutex_trylock(mutex, owner, value) != 0)
+ ;
+
+ return 0;
+}
+EXPORT_SYMBOL(altera_mutex_lock);
+
+/**
+ * altera_mutex_trylock - Tries once to lock the hardware mutex and returns
+ * immediately
+ * @mutex: the mutex to be acquired
+ * @owner: owner ID
+ * @value: the new non-zero value to write to mutex
+ *
+ * Returns 0 if mutex was successfully locked. Otherwise, returns non-zero.
+ *
+ * The mutex must later on be released by the same owner that acquired it.
+ * This function is not ISR callable.
+ */
+int altera_mutex_trylock(struct altera_mutex *mutex, u16 owner, u16 value)
+{
+ if (!mutex || !mutex->requested)
+ return -EINVAL;
+
+ return __mutex_trylock(mutex, owner, value);
+}
+EXPORT_SYMBOL(altera_mutex_trylock);
+
+/**
+ * altera_mutex_unlock - Unlock a mutex that has been locked by this owner
+ * previously that was locked on the
+ * altera_mutex_lock. Upon release, the value stored
+ * in the mutex is set to zero.
+ * @mutex: the mutex to be released
+ * @owner: Owner ID
+ *
+ * Returns 0 if mutex was successfully unlocked. Otherwise, returns
+ * non-zero.
+ *
+ * This function is not ISR callable.
+ */
+int altera_mutex_unlock(struct altera_mutex *mutex, u16 owner)
+{
+ u32 reg;
+
+ if (!mutex || !mutex->requested)
+ return -EINVAL;
+
+ mutex_lock(&mutex->lock);
+
+ __raw_writel(owner << MUTEX_REG_OWNER_OFFSET,
+ mutex->regs + MUTEX_REG);
+
+ reg = __raw_readl(mutex->regs + MUTEX_REG);
+ if (reg & MUTEX_REG_VALUE_MASK) {
+ /* Unlock failed */
+ dev_dbg(&mutex->pdev->dev,
+ "Unlock mutex failed, owner %d and expected owner %d\n",
+ owner, MUTEX_GET_OWNER(reg));
+ mutex_unlock(&mutex->lock);
+ return -EINVAL;
+ }
+
+ mutex_unlock(&mutex->lock);
+ return 0;
+}
+EXPORT_SYMBOL(altera_mutex_unlock);
+
+/**
+ * altera_mutex_owned - Determines if this owner owns the mutex
+ * @mutex: the mutex to be queried
+ * @owner: Owner ID
+ *
+ * Returns 1 if the owner owns the mutex. Otherwise, returns zero.
+ */
+int altera_mutex_owned(struct altera_mutex *mutex, u16 owner)
+{
+ u32 reg;
+ u16 actual_owner;
+ int ret = 0;
+
+ if (!mutex || !mutex->requested)
+ return ret;
+
+ mutex_lock(&mutex->lock);
+ reg = __raw_readl(mutex->regs + MUTEX_REG);
+ actual_owner = MUTEX_GET_OWNER(reg);
+ if (actual_owner == owner)
+ ret = 1;
+
+ mutex_unlock(&mutex->lock);
+ return ret;
+}
+EXPORT_SYMBOL(altera_mutex_owned);
+
+/**
+ * altera_mutex_is_locked - Determines if the mutex is locked
+ * @mutex: the mutex to be queried
+ *
+ * Returns 1 if the mutex is locked, 0 if unlocked.
+ */
+int altera_mutex_is_locked(struct altera_mutex *mutex)
+{
+ u32 reg;
+ int ret = 0;
+
+ if (!mutex || !mutex->requested)
+ return ret;
+
+ mutex_lock(&mutex->lock);
+ reg = __raw_readl(mutex->regs + MUTEX_REG);
+ reg &= MUTEX_REG_VALUE_MASK;
+ if (reg)
+ ret = 1;
+
+ mutex_unlock(&mutex->lock);
+ return ret;
+}
+EXPORT_SYMBOL(altera_mutex_is_locked);
+
+static int altera_mutex_probe(struct platform_device *pdev)
+{
+ struct altera_mutex *mutex;
+ struct resource *regs;
+
+ mutex = devm_kzalloc(&pdev->dev, sizeof(struct altera_mutex),
+ GFP_KERNEL);
+ if (!mutex)
+ return -ENOMEM;
+
+ mutex->pdev = pdev;
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!regs)
+ return -ENXIO;
+
+ mutex->regs = devm_ioremap_resource(&pdev->dev, regs);
+ if (IS_ERR(mutex->regs))
+ return PTR_ERR(mutex->regs);
+
+ mutex_init(&mutex->lock);
+
+ spin_lock(&list_lock);
+ list_add_tail(&mutex->list, &mutex_list);
+ spin_unlock(&list_lock);
+
+ platform_set_drvdata(pdev, mutex);
+
+ return 0;
+}
+
+static int altera_mutex_remove(struct platform_device *pdev)
+{
+ struct altera_mutex *mutex = platform_get_drvdata(pdev);
+
+ spin_lock(&list_lock);
+ if (mutex)
+ list_del(&mutex->list);
+ spin_unlock(&list_lock);
+
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static const struct of_device_id altera_mutex_match[] = {
+ { .compatible = "altr,hwmutex-1.0" },
+ { /* Sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, altera_mutex_match);
+
+static struct platform_driver altera_mutex_platform_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = altera_mutex_match,
+ },
+ .remove = altera_mutex_remove,
+};
+
+static int __init altera_mutex_init(void)
+{
+ return platform_driver_probe(&altera_mutex_platform_driver,
+ altera_mutex_probe);
+}
+
+static void __exit altera_mutex_exit(void)
+{
+ platform_driver_unregister(&altera_mutex_platform_driver);
+}
+
+module_init(altera_mutex_init);
+module_exit(altera_mutex_exit);
+
+MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Altera Hardware Mutex driver");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/misc/altera_ilc.c b/drivers/misc/altera_ilc.c
new file mode 100644
index 000000000000..adc78102c7d5
--- /dev/null
+++ b/drivers/misc/altera_ilc.c
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2014 Altera Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/kfifo.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/sysfs.h>
+
+#define DRV_NAME "altera_ilc"
+#define CTRL_REG 0x80
+#define FREQ_REG 0x84
+#define STP_REG 0x88
+#define VLD_REG 0x8C
+#define ILC_MAX_PORTS 32
+#define ILC_FIFO_DEFAULT 32
+#define ILC_ENABLE 0x01
+#define CHAR_SIZE 10
+#define POLL_INTERVAL 1
+#define GET_PORT_COUNT(_val) ((_val & 0x7C) >> 2)
+#define GET_VLD_BIT(_val, _offset) (((_val) >> _offset) & 0x1)
+
+struct altera_ilc {
+ struct platform_device *pdev;
+ void __iomem *regs;
+ unsigned int port_count;
+ unsigned int irq;
+ unsigned int channel_offset;
+ unsigned int interrupt_channels[ILC_MAX_PORTS];
+ struct kfifo kfifos[ILC_MAX_PORTS];
+ struct device_attribute dev_attr[ILC_MAX_PORTS];
+ struct delayed_work ilc_work;
+ char sysfs[ILC_MAX_PORTS][CHAR_SIZE];
+ u32 fifo_depth;
+};
+
+static int ilc_irq_lookup(struct altera_ilc *ilc, int irq)
+{
+ int i;
+ for (i = 0; i < ilc->port_count; i++) {
+ if (irq == platform_get_irq(ilc->pdev, i))
+ return i;
+ }
+ return -EPERM;
+}
+
+static ssize_t ilc_show_counter(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret, i, id, fifo_len;
+ unsigned int fifo_buf[ILC_MAX_PORTS];
+ char temp[10];
+ struct altera_ilc *ilc = dev_get_drvdata(dev);
+
+ fifo_len = 0;
+ ret = kstrtouint(attr->attr.name, 0, &id);
+
+ for (i = 0; i < ilc->port_count; i++) {
+ if (id == (ilc->interrupt_channels[i])) {
+ /*Check for kfifo length*/
+ fifo_len = kfifo_len(&ilc->kfifos[i])
+ /sizeof(unsigned int);
+ if (fifo_len <= 0) {
+ dev_info(&ilc->pdev->dev, "Fifo for interrupt %s is empty\n",
+ attr->attr.name);
+ return 0;
+ }
+ /*Read from kfifo*/
+ ret = kfifo_out(&ilc->kfifos[i], &fifo_buf,
+ kfifo_len(&ilc->kfifos[i]));
+ }
+ }
+
+ for (i = 0; i < fifo_len; i++) {
+ sprintf(temp, "%u\n", fifo_buf[i]);
+ strcat(buf, temp);
+ }
+
+ strcat(buf, "\0");
+
+ return strlen(buf);
+}
+
+static struct attribute *altera_ilc_attrs[ILC_MAX_PORTS];
+
+struct attribute_group altera_ilc_attr_group = {
+ .name = "ilc_data",
+ .attrs = altera_ilc_attrs,
+};
+
+static void ilc_work(struct work_struct *work)
+{
+ unsigned int ilc_value, ret, offset, stp_reg;
+ struct altera_ilc *ilc =
+ container_of(work, struct altera_ilc, ilc_work.work);
+
+ offset = ilc_irq_lookup(ilc, ilc->irq);
+ if (offset < 0) {
+ dev_err(&ilc->pdev->dev, "Unable to lookup irq number\n");
+ return;
+ }
+
+ if (GET_VLD_BIT(readl(ilc->regs + VLD_REG), offset)) {
+ /*Read counter register*/
+ ilc_value = readl(ilc->regs + (offset) * 4);
+
+ /*Putting value into kfifo*/
+ ret = kfifo_in((&ilc->kfifos[offset]),
+ (unsigned int *)&ilc_value, sizeof(ilc_value));
+
+ /*Clearing stop register*/
+ stp_reg = readl(ilc->regs + STP_REG);
+ writel((!(0x1 << offset))&stp_reg, ilc->regs + STP_REG);
+
+ return;
+ }
+
+ /*Start workqueue to poll data valid*/
+ schedule_delayed_work(&ilc->ilc_work, msecs_to_jiffies(POLL_INTERVAL));
+}
+
+static irqreturn_t ilc_interrupt_handler(int irq, void *p)
+{
+ unsigned int offset, stp_reg;
+
+ struct altera_ilc *ilc = (struct altera_ilc *)p;
+
+ /*Update ILC struct*/
+ ilc->irq = irq;
+
+ dev_dbg(&ilc->pdev->dev, "Interrupt %u triggered\n",
+ ilc->irq);
+
+ offset = ilc_irq_lookup(ilc, irq);
+ if (offset < 0) {
+ dev_err(&ilc->pdev->dev, "Unable to lookup irq number\n");
+ return IRQ_RETVAL(IRQ_NONE);
+ }
+
+ /*Setting stop register*/
+ stp_reg = readl(ilc->regs + STP_REG);
+ writel((0x1 << offset)|stp_reg, ilc->regs + STP_REG);
+
+ /*Start workqueue to poll data valid*/
+ schedule_delayed_work(&ilc->ilc_work, 0);
+
+ return IRQ_RETVAL(IRQ_NONE);
+}
+
+static int altera_ilc_probe(struct platform_device *pdev)
+{
+ struct altera_ilc *ilc;
+ struct resource *regs;
+ struct device_node *np = pdev->dev.of_node;
+ int ret, i;
+
+ ilc = devm_kzalloc(&pdev->dev, sizeof(struct altera_ilc),
+ GFP_KERNEL);
+ if (!ilc)
+ return -ENOMEM;
+
+ ilc->pdev = pdev;
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!regs)
+ return -ENXIO;
+
+ ilc->regs = devm_ioremap_resource(&pdev->dev, regs);
+ if (!ilc->regs)
+ return -EADDRNOTAVAIL;
+
+ ilc->port_count = GET_PORT_COUNT(readl(ilc->regs + CTRL_REG));
+ if (ilc->port_count <= 0) {
+ dev_warn(&pdev->dev, "No interrupt connected to ILC\n");
+ return -EPERM;
+ }
+
+ /*Check for fifo depth*/
+ ret = of_property_read_u32(np, "altr,sw-fifo-depth",
+ &(ilc->fifo_depth));
+ if (ret) {
+ dev_warn(&pdev->dev, "Fifo depth undefined\n");
+ dev_warn(&pdev->dev, "Setting fifo depth to default value (32)\n");
+ ilc->fifo_depth = ILC_FIFO_DEFAULT;
+ }
+
+ /*Initialize Kfifo*/
+ for (i = 0; i < ilc->port_count; i++) {
+ ret = kfifo_alloc(&ilc->kfifos[i], (ilc->fifo_depth *
+ sizeof(unsigned int)), GFP_KERNEL);
+ if (ret) {
+ dev_err(&pdev->dev, "Kfifo failed to initialize\n");
+ return ret;
+ }
+ }
+
+ /*Register each of the IRQs*/
+ for (i = 0; i < ilc->port_count; i++) {
+ ilc->interrupt_channels[i] = platform_get_irq(pdev, i);
+
+ ret = devm_request_irq(&pdev->dev, (ilc->interrupt_channels[i]),
+ ilc_interrupt_handler, IRQF_SHARED, "ilc_0",
+ (void *)(ilc));
+
+ if (ret < 0)
+ dev_warn(&pdev->dev, "Failed to register interrupt handler");
+ }
+
+ /*Setup sysfs interface*/
+ for (i = 0; (i < ilc->port_count); i++) {
+ sprintf(ilc->sysfs[i], "%d", (ilc->interrupt_channels[i]));
+ ilc->dev_attr[i].attr.name = ilc->sysfs[i];
+ ilc->dev_attr[i].attr.mode = S_IRUGO;
+ ilc->dev_attr[i].show = ilc_show_counter;
+ altera_ilc_attrs[i] = &ilc->dev_attr[i].attr;
+ altera_ilc_attrs[i+1] = NULL;
+ }
+ ret = sysfs_create_group(&pdev->dev.kobj, &altera_ilc_attr_group);
+
+ /*Initialize workqueue*/
+ INIT_DELAYED_WORK(&ilc->ilc_work, ilc_work);
+
+ /*Global enable ILC softIP*/
+ writel(ILC_ENABLE, ilc->regs + CTRL_REG);
+
+ platform_set_drvdata(pdev, ilc);
+
+ dev_info(&pdev->dev, "Driver successfully loaded\n");
+
+ return 0;
+}
+
+static int altera_ilc_remove(struct platform_device *pdev)
+{
+ int i;
+ struct altera_ilc *ilc = platform_get_drvdata(pdev);
+
+ /*Remove sysfs interface*/
+ sysfs_remove_group(&pdev->dev.kobj, &altera_ilc_attr_group);
+
+ /*Free up kfifo memory*/
+ for (i = 0; i < ilc->port_count; i++)
+ kfifo_free(&ilc->kfifos[i]);
+
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static const struct of_device_id altera_ilc_match[] = {
+ { .compatible = "altr,ilc-1.0" },
+ { /* Sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, altera_ilc_match);
+
+static struct platform_driver altera_ilc_platform_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(altera_ilc_match),
+ },
+ .remove = altera_ilc_remove,
+};
+
+static int __init altera_ilc_init(void)
+{
+ return platform_driver_probe(&altera_ilc_platform_driver,
+ altera_ilc_probe);
+}
+
+static void __exit altera_ilc_exit(void)
+{
+ platform_driver_unregister(&altera_ilc_platform_driver);
+}
+
+module_init(altera_ilc_init);
+module_exit(altera_ilc_exit);
+
+MODULE_AUTHOR("Chee Nouk Phoon <cnphoon@altera.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Altera Interrupt Latency Counter Driver");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/misc/altera_sysid.c b/drivers/misc/altera_sysid.c
new file mode 100644
index 000000000000..be35530c504b
--- /dev/null
+++ b/drivers/misc/altera_sysid.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright Altera Corporation (C) 2013.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Credit:
+ * Walter Goossens
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+#define DRV_NAME "altera_sysid"
+
+struct altera_sysid {
+ void __iomem *regs;
+};
+
+/* System ID Registers*/
+#define SYSID_REG_ID (0x0)
+#define SYSID_REG_TIMESTAMP (0x4)
+
+static ssize_t altera_sysid_show_id(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct altera_sysid *sysid = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", readl(sysid->regs + SYSID_REG_ID));
+}
+
+static ssize_t altera_sysid_show_timestamp(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned int reg;
+ struct tm timestamp;
+ struct altera_sysid *sysid = dev_get_drvdata(dev);
+
+ reg = readl(sysid->regs + SYSID_REG_TIMESTAMP);
+
+ time_to_tm(reg, 0, &timestamp);
+
+ return sprintf(buf, "%u (%u-%u-%u %u:%u:%u UTC)\n", reg,
+ (unsigned int)(timestamp.tm_year + 1900),
+ timestamp.tm_mon + 1, timestamp.tm_mday, timestamp.tm_hour,
+ timestamp.tm_min, timestamp.tm_sec);
+}
+
+static DEVICE_ATTR(id, S_IRUGO, altera_sysid_show_id, NULL);
+static DEVICE_ATTR(timestamp, S_IRUGO, altera_sysid_show_timestamp, NULL);
+
+static struct attribute *altera_sysid_attrs[] = {
+ &dev_attr_id.attr,
+ &dev_attr_timestamp.attr,
+ NULL,
+};
+
+struct attribute_group altera_sysid_attr_group = {
+ .name = "sysid",
+ .attrs = altera_sysid_attrs,
+};
+
+static int altera_sysid_probe(struct platform_device *pdev)
+{
+ struct altera_sysid *sysid;
+ struct resource *regs;
+
+ sysid = devm_kzalloc(&pdev->dev, sizeof(struct altera_sysid),
+ GFP_KERNEL);
+ if (!sysid)
+ return -ENOMEM;
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!regs)
+ return -ENXIO;
+
+ sysid->regs = devm_ioremap_resource(&pdev->dev, regs);
+ if (IS_ERR(sysid->regs))
+ return PTR_ERR(sysid->regs);
+
+ platform_set_drvdata(pdev, sysid);
+
+ return sysfs_create_group(&pdev->dev.kobj, &altera_sysid_attr_group);
+}
+
+static int altera_sysid_remove(struct platform_device *pdev)
+{
+ sysfs_remove_group(&pdev->dev.kobj, &altera_sysid_attr_group);
+
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static const struct of_device_id altera_sysid_match[] = {
+ { .compatible = "altr,sysid-1.0" },
+ { /* Sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, altera_sysid_match);
+
+static struct platform_driver altera_sysid_platform_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(altera_sysid_match),
+ },
+ .probe = altera_sysid_probe,
+ .remove = altera_sysid_remove,
+};
+
+static int __init altera_sysid_init(void)
+{
+ return platform_driver_register(&altera_sysid_platform_driver);
+}
+
+static void __exit altera_sysid_exit(void)
+{
+ platform_driver_unregister(&altera_sysid_platform_driver);
+}
+
+module_init(altera_sysid_init);
+module_exit(altera_sysid_exit);
+
+MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Altera System ID driver");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/misc/intel-rsu.c b/drivers/misc/intel-rsu.c
new file mode 100644
index 000000000000..94b85416f882
--- /dev/null
+++ b/drivers/misc/intel-rsu.c
@@ -0,0 +1,387 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Intel Corporation
+ */
+
+/*
+ * This driver exposes some optional features of the Intel Stratix 10 SoC FPGA.
+ * The SysFS interfaces exposed here are FPGA Remote System Update (RSU)
+ * related. They allow user space software to query the configuration system
+ * status and to request optional reboot behavior specific to Intel FPGAs.
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/completion.h>
+#include <linux/intel-service-client.h>
+#include <linux/kobject.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+
+#define MAX_U64_STR_LEN 22
+
+/*
+ * Private data structure
+ */
+struct intel_rsu_priv {
+ struct intel_svc_chan *chan;
+ struct intel_svc_client client;
+ struct completion svc_completion;
+ struct {
+ unsigned long current_image;
+ unsigned long fail_image;
+ unsigned int version;
+ unsigned int state;
+ unsigned int error_details;
+ unsigned int error_location;
+ } status;
+};
+
+/*
+ * status_svc_callback() - Callback from intel-service layer that returns SMC
+ * response with RSU status data. Parses up data and
+ * update driver private data structure.
+ * client - returned context from intel-service layer
+ * data - SMC response data
+ */
+static void status_svc_callback(struct intel_svc_client *client,
+ struct intel_svc_c_data *data)
+{
+ struct intel_rsu_priv *priv = client->priv;
+ struct arm_smccc_res *res = (struct arm_smccc_res *)data->kaddr1;
+
+ if (data->status == BIT(SVC_STATUS_RSU_OK)) {
+ priv->status.version =
+ (unsigned int)(res->a2 >> 32) & 0xFFFFFFFF;
+ priv->status.state = (unsigned int)res->a2 & 0xFFFFFFFF;
+ priv->status.fail_image = res->a1;
+ priv->status.current_image = res->a0;
+ priv->status.error_location =
+ (unsigned int)res->a3 & 0xFFFFFFFF;
+ priv->status.error_details =
+ (unsigned int)(res->a3 >> 32) & 0xFFFFFFFF;
+ } else {
+ dev_err(client->dev, "COMMAND_RSU_STATUS returned 0x%lX\n",
+ res->a0);
+ priv->status.version = 0;
+ priv->status.state = 0;
+ priv->status.fail_image = 0;
+ priv->status.current_image = 0;
+ priv->status.error_location = 0;
+ priv->status.error_details = 0;
+ }
+
+ complete(&priv->svc_completion);
+}
+
+/*
+ * get_status() - Start an intel-service layer transaction to perform the SMC
+ * that is necessary to get RSU status information. Wait for
+ * completion and timeout if needed.
+ * priv - driver private data
+ *
+ * Returns 0 on success
+ */
+static int get_status(struct intel_rsu_priv *priv)
+{
+ struct intel_svc_client_msg msg;
+ int ret;
+ unsigned long timeout;
+
+ reinit_completion(&priv->svc_completion);
+ priv->client.receive_cb = status_svc_callback;
+
+ msg.command = COMMAND_RSU_STATUS;
+ ret = intel_svc_send(priv->chan, &msg);
+ if (ret < 0)
+ goto status_done;
+
+ timeout = msecs_to_jiffies(SVC_RSU_REQUEST_TIMEOUT_MS);
+ ret =
+ wait_for_completion_interruptible_timeout(&priv->svc_completion,
+ timeout);
+ if (!ret) {
+ dev_err(priv->client.dev,
+ "timeout waiting for COMMAND_RSU_STATUS\n");
+ ret = -ETIMEDOUT;
+ goto status_done;
+ }
+ if (ret < 0) {
+ dev_err(priv->client.dev,
+ "error (%d) waiting for COMMAND_RSU_STATUS\n", ret);
+ goto status_done;
+ }
+
+ ret = 0;
+
+status_done:
+ intel_svc_done(priv->chan);
+ return ret;
+}
+
+/* current_image_show() - DEVICE_ATTR callback to show current_image status */
+static ssize_t current_image_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct intel_rsu_priv *priv = dev_get_drvdata(dev);
+
+ if (!priv)
+ return -ENODEV;
+
+ return scnprintf(buf, PAGE_SIZE, "%ld", priv->status.current_image);
+}
+
+/* fail_image_show() - DEVICE_ATTR callback to show fail_image status */
+static ssize_t fail_image_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct intel_rsu_priv *priv = dev_get_drvdata(dev);
+
+ if (!priv)
+ return -ENODEV;
+
+ return scnprintf(buf, PAGE_SIZE, "%ld", priv->status.fail_image);
+}
+
+/* version_show() - DEVICE_ATTR callback to show version status */
+static ssize_t version_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct intel_rsu_priv *priv = dev_get_drvdata(dev);
+
+ if (!priv)
+ return -ENODEV;
+
+ return scnprintf(buf, PAGE_SIZE, "%d", priv->status.version);
+}
+
+/* state_show() - DEVICE_ATTR callback to show state status */
+static ssize_t state_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct intel_rsu_priv *priv = dev_get_drvdata(dev);
+
+ if (!priv)
+ return -ENODEV;
+
+ return scnprintf(buf, PAGE_SIZE, "%d", priv->status.state);
+}
+
+/* error_location_show() - DEVICE_ATTR callback to show error_location status */
+static ssize_t error_location_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct intel_rsu_priv *priv = dev_get_drvdata(dev);
+
+ if (!priv)
+ return -ENODEV;
+
+ return scnprintf(buf, PAGE_SIZE, "%d", priv->status.error_location);
+}
+
+/* error_details_show() - DEVICE_ATTR callback to show error_details status */
+static ssize_t error_details_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct intel_rsu_priv *priv = dev_get_drvdata(dev);
+
+ if (!priv)
+ return -ENODEV;
+
+ return scnprintf(buf, PAGE_SIZE, "%d", priv->status.error_details);
+}
+
+/*
+ * update_svc_callback() - Callback from intel-service layer that returns SMC
+ * response from RSU update. Checks for success/fail.
+ * client - returned context from intel-service layer
+ * data - SMC repsonse data
+ */
+static void update_svc_callback(struct intel_svc_client *client,
+ struct intel_svc_c_data *data)
+{
+ struct intel_rsu_priv *priv = client->priv;
+
+ if (data->status != BIT(SVC_STATUS_RSU_OK))
+ dev_err(client->dev, "COMMAND_RSU_UPDATE returned %i\n",
+ data->status);
+
+ complete(&priv->svc_completion);
+}
+
+/*
+ * send_update() - Start an intel-service layer transaction to perform the SMC
+ * that is necessary to send an RSU update request. Wait for
+ * completion and timeout if needed.
+ * priv - driver private data
+ *
+ * Returns 0 on success
+ */
+static int send_update(struct intel_rsu_priv *priv,
+ unsigned long address)
+{
+ struct intel_svc_client_msg msg;
+ int ret;
+ unsigned long timeout;
+
+ reinit_completion(&priv->svc_completion);
+ priv->client.receive_cb = update_svc_callback;
+
+ msg.command = COMMAND_RSU_UPDATE;
+ msg.arg[0] = address;
+
+ ret = intel_svc_send(priv->chan, &msg);
+ if (ret < 0)
+ goto update_done;
+
+ timeout = msecs_to_jiffies(SVC_RSU_REQUEST_TIMEOUT_MS);
+ ret = wait_for_completion_interruptible_timeout(&priv->svc_completion,
+ timeout);
+ if (!ret) {
+ dev_err(priv->client.dev,
+ "timeout waiting for COMMAND_RSU_UPDATE\n");
+ ret = -ETIMEDOUT;
+ goto update_done;
+ }
+ if (ret < 0) {
+ dev_err(priv->client.dev,
+ "error (%d) waiting for COMMAND_RSU_UPDATE\n", ret);
+ goto update_done;
+ }
+
+ ret = 0;
+
+update_done:
+ intel_svc_done(priv->chan);
+ return ret;
+}
+
+/* reboot_image_store() - DEVICE_ATTR callback to store reboot_image request */
+static ssize_t reboot_image_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct intel_rsu_priv *priv = dev_get_drvdata(dev);
+ unsigned long address;
+ int ret;
+
+ if (priv == 0)
+ return -ENODEV;
+
+ /* Ensure the input buffer is null terminated and not too long */
+ if (strnlen(buf, MAX_U64_STR_LEN) == MAX_U64_STR_LEN)
+ return -EINVAL;
+
+ ret = kstrtoul(buf, 10, &address);
+ if (ret)
+ return ret;
+
+ send_update(priv, address);
+
+ return count;
+}
+
+/*
+ * Attribute structures
+ */
+
+static DEVICE_ATTR_RO(current_image);
+static DEVICE_ATTR_RO(fail_image);
+static DEVICE_ATTR_RO(state);
+static DEVICE_ATTR_RO(version);
+static DEVICE_ATTR_RO(error_location);
+static DEVICE_ATTR_RO(error_details);
+static DEVICE_ATTR_WO(reboot_image);
+
+static struct attribute *attrs[] = {
+ &dev_attr_current_image.attr,
+ &dev_attr_fail_image.attr,
+ &dev_attr_state.attr,
+ &dev_attr_version.attr,
+ &dev_attr_error_location.attr,
+ &dev_attr_error_details.attr,
+ &dev_attr_reboot_image.attr,
+ NULL
+};
+
+static struct attribute_group attr_group = {
+ .attrs = attrs
+};
+
+static int intel_rsu_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct device *dev = &pdev->dev;
+ struct intel_rsu_priv *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->client.dev = dev;
+ priv->client.receive_cb = update_svc_callback;
+ priv->client.priv = priv;
+
+ priv->status.current_image = 0;
+ priv->status.fail_image = 0;
+ priv->status.error_location = 0;
+ priv->status.error_details = 0;
+ priv->status.version = 0;
+ priv->status.state = 0;
+
+ priv->chan = request_svc_channel_byname(&priv->client, SVC_CLIENT_RSU);
+ if (IS_ERR(priv->chan)) {
+ dev_err(dev, "couldn't get service channel (%s)\n",
+ SVC_CLIENT_RSU);
+ return PTR_ERR(priv->chan);
+ }
+
+ init_completion(&priv->svc_completion);
+
+ platform_set_drvdata(pdev, priv);
+
+ ret = get_status(priv);
+ if (ret) {
+ dev_err(dev, "Error getting RSU status (%i)\n", ret);
+ free_svc_channel(priv->chan);
+ return ret;
+ }
+
+ ret = sysfs_create_group(&dev->kobj, &attr_group);
+ if (ret)
+ free_svc_channel(priv->chan);
+
+ return ret;
+}
+
+static int intel_rsu_remove(struct platform_device *pdev)
+{
+ struct intel_rsu_priv *priv = platform_get_drvdata(pdev);
+
+ free_svc_channel(priv->chan);
+
+ return 0;
+}
+
+static const struct of_device_id intel_rsu_of_match[] = {
+ {.compatible = "intel,stratix10-rsu",},
+ {},
+};
+MODULE_DEVICE_TABLE(of, intel_rsu_of_match);
+
+static struct platform_driver intel_rsu_driver = {
+ .probe = intel_rsu_probe,
+ .remove = intel_rsu_remove,
+ .driver = {
+ .name = "intel-rsu",
+ .of_match_table = intel_rsu_of_match,
+ },
+};
+
+module_platform_driver(intel_rsu_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel Remote System Update SysFS Driver");
+MODULE_AUTHOR("David Koltak <david.koltak@linux.intel.com>");
diff --git a/drivers/misc/intel-service.c b/drivers/misc/intel-service.c
new file mode 100644
index 000000000000..ea32db718ca3
--- /dev/null
+++ b/drivers/misc/intel-service.c
@@ -0,0 +1,1042 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2017-2018, Intel Corporation
+ */
+
+/*
+ * Intel Stratix10 SoC is composed of a 64 bit quad-core ARM Cortex A53 hard
+ * processor system (HPS) and Secure Device Manager (SDM). SDM is the
+ * hardware which does the FPGA configuration, QSPI, Crypto and warm reset.
+ *
+ * When the FPGA is configured from HPS, there needs to be a way for HPS to
+ * notify SDM the location and size of the configuration data. Then SDM will
+ * get the configuration data from that location and perform the FPGA
+ * configuration.
+ *
+ * To meet the whole system security needs and support virtual machine
+ * requesting communication with SDM, only the secure world of software (EL3,
+ * Exception Level 3) can interface with SDM. All software entities running
+ * on other exception levels must channel through the EL3 software whenever
+ * it needs service from SDM.
+ *
+ * Intel Stratix10 service layer driver is added to provide the service for
+ * FPGA configuration. Running at privileged exception level (EL1, Exception
+ * Level 1), Intel Stratix10 service layer driver interfaces with the service
+ * client at EL1 (Intel Stratix10 FPGA Manager) and manages secure monitor
+ * call (SMC) to communicate with secure monitor software at secure monitor
+ * exception level (EL3).
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/genalloc.h>
+#include <linux/intel-service-client.h>
+#include <linux/io.h>
+#include <linux/kfifo.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include "intel-smc.h"
+
+/* SVC_NUM_DATA_IN_FIFO - number of struct intel_svc_data in the FIFO */
+#define SVC_NUM_DATA_IN_FIFO 32
+/* SVC_NUM_CHANNEL - number of channel supported by service layer driver */
+#define SVC_NUM_CHANNEL 2
+/*
+ * FPGA_CONFIG_DATA_CLAIM_TIMEOUT_MS - claim back the submitted buffer(s)
+ * from the secure world for FPGA manager to reuse, or to free the buffer(s)
+ * when all bit-stream data had be send.
+ */
+#define FPGA_CONFIG_DATA_CLAIM_TIMEOUT_MS 200
+/*
+ * FPGA_CONFIG_STATUS_TIMEOUT_SEC - poll the FPGA configuration status,
+ * service layer will return error to FPGA manager when timeout occurs,
+ * timeout is set to 30 seconds (30 * 1000) at Intel Stratix10 SoC.
+ */
+#define FPGA_CONFIG_STATUS_TIMEOUT_SEC 30
+
+typedef void (svc_invoke_fn)(unsigned long, unsigned long, unsigned long,
+ unsigned long, unsigned long, unsigned long,
+ unsigned long, unsigned long,
+ struct arm_smccc_res *);
+static int svc_normal_to_secure_thread(void *data);
+struct intel_svc_chan;
+
+/**
+ * struct intel_svc_sh_memory - service shared memory structure
+ * @sync_complete: state for a completion
+ * @addr: physical address of shared memory block
+ * @size: size of shared memory block
+ * @invoke_fn: function to issue secure monitor or hypervisor call
+ *
+ * This struct is used to save physical address and size of shared memory
+ * block. The shared memory blocked is allocated by secure monitor software
+ * at secure world.
+ *
+ * Service layer driver uses the physical address and size to create a memory
+ * pool, then allocates data buffer from that memory pool for service client.
+ */
+struct intel_svc_sh_memory {
+ struct completion sync_complete;
+ unsigned long addr;
+ unsigned long size;
+ svc_invoke_fn *invoke_fn;
+};
+
+/**
+ * struct intel_svc_data_mem - service memory structure
+ * @vaddr: virtual address
+ * @paddr: physical address
+ * @size: size of memory
+ * @node: link list head node
+ *
+ * This struct is used in a list that keeps track of buffers which have
+ * been allocated or freed from the memory pool. Service layer driver also
+ * uses this struct to transfer physical address to virtual address.
+ */
+struct intel_svc_data_mem {
+ void *vaddr;
+ phys_addr_t paddr;
+ size_t size;
+ struct list_head node;
+};
+
+/**
+ * struct intel_svc_data - service data structure
+ * @chan: service channel
+ * @paddr: playload physical address
+ * @size: playload size
+ * @command: service command requested by client
+ * @arg[3]: args to be passed via registers and not physically mapped buffers
+ * This struct is used in service FIFO for inter-process communication.
+ */
+struct intel_svc_data {
+ struct intel_svc_chan *chan;
+ phys_addr_t paddr;
+ size_t size;
+ u32 command;
+ u64 arg[3];
+};
+
+/**
+ * struct intel_svc_controller - service controller
+ * @dev: device
+ * @chans: array of service channels
+ * $num_chans: number of channels in 'chans' array
+ * @num_active_client: number of active service client
+ * @node: list management
+ * @genpool: memory pool pointing to the memory region
+ * @task: pointer to the thread task which handles SMC or HVC call
+ * @svc_fifo: a queue for storing service message data
+ * @complete_status: state for completion
+ * @svc_fifo_lock: protect access to service message data queue
+ * @invoke_fn: function to issue secure monitor call or hypervisor call
+ *
+ * This struct is used to create communication channels for service clients, to
+ * handle secure monitor or hypervisor call.
+ */
+struct intel_svc_controller {
+ struct device *dev;
+ struct intel_svc_chan *chans;
+ int num_chans;
+ int num_active_client;
+ struct list_head node;
+ struct gen_pool *genpool;
+ struct task_struct *task;
+ struct kfifo svc_fifo;
+ struct completion complete_status;
+ spinlock_t svc_fifo_lock;
+ svc_invoke_fn *invoke_fn;
+};
+
+/**
+ * struct intel_svc_chan - service communication channel
+ * @ctrl: pointer to service controller which is the provider of this channel
+ * @scl: pointer to service client which owns the channel
+ * @name: service client name associated with the channel
+ * @lock: protect access to the channel
+ *
+ * This struct is used by service client to communicate with service layer, each
+ * service client has its own channel created by service controller.
+ */
+struct intel_svc_chan {
+ struct intel_svc_controller *ctrl;
+ struct intel_svc_client *scl;
+ char *name;
+ spinlock_t lock;
+};
+
+static LIST_HEAD(svc_ctrl);
+static LIST_HEAD(svc_data_mem);
+
+/**
+ * request_svc_channel_byname() - request a service channel
+ * @client: pointer to service client
+ * @name: service client name
+ *
+ * This function is used by service client to request a service channel.
+ *
+ * Return: a pointer to channel assigned to the client on success,
+ * or ERR_PTR() on error.
+ */
+struct intel_svc_chan *request_svc_channel_byname(
+ struct intel_svc_client *client, const char *name)
+{
+ struct device *dev = client->dev;
+ struct intel_svc_controller *controller;
+ struct intel_svc_chan *chan = NULL;
+ unsigned long flag;
+ int i;
+
+ /* if probe was called after client's, or error on probe */
+ if (list_empty(&svc_ctrl))
+ return ERR_PTR(-EPROBE_DEFER);
+
+ controller = list_first_entry(&svc_ctrl,
+ struct intel_svc_controller, node);
+ for (i = 0; i < SVC_NUM_CHANNEL; i++) {
+ if (!strcmp(controller->chans[i].name, name)) {
+ chan = &controller->chans[i];
+ break;
+ }
+ }
+
+ /* if there was no channel match */
+ if (i == SVC_NUM_CHANNEL) {
+ dev_err(dev, "%s: channel not allocated\n", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (chan->scl || !try_module_get(controller->dev->driver->owner)) {
+ dev_dbg(dev, "%s: svc not free\n", __func__);
+ return ERR_PTR(-EBUSY);
+ }
+
+ spin_lock_irqsave(&chan->lock, flag);
+ chan->scl = client;
+ chan->ctrl->num_active_client++;
+ spin_unlock_irqrestore(&chan->lock, flag);
+
+ return chan;
+}
+EXPORT_SYMBOL_GPL(request_svc_channel_byname);
+
+/**
+ * free_svc_channel() - free service channel
+ * @chan: service channel to be freed
+ *
+ * This function is used by service client to free a service channel.
+ */
+void free_svc_channel(struct intel_svc_chan *chan)
+{
+ unsigned long flag;
+
+ spin_lock_irqsave(&chan->lock, flag);
+ chan->scl = NULL;
+ chan->ctrl->num_active_client--;
+ module_put(chan->ctrl->dev->driver->owner);
+ spin_unlock_irqrestore(&chan->lock, flag);
+}
+EXPORT_SYMBOL_GPL(free_svc_channel);
+
+/**
+ * intel_svc_send() - send a message data to the remote
+ * @chan: service channel assigned to the client
+ * @msg: message data to be sent, in the format of "struct intel_svc_client_msg"
+ *
+ * This function is used by service client to send command or data to service
+ * layer driver.
+ *
+ * Return: non-negative value for successful submission to the data queue
+ * created by service layer driver, or negative value on error.
+ */
+int intel_svc_send(struct intel_svc_chan *chan, void *msg)
+{
+ struct intel_svc_client_msg *p_msg = (struct intel_svc_client_msg *)msg;
+ struct intel_svc_data_mem *p_mem;
+ struct intel_svc_data *p_data;
+ int ret = 0;
+ unsigned int cpu = 0;
+
+ p_data = kmalloc(sizeof(*p_data), GFP_KERNEL);
+ if (!p_data)
+ return -ENOMEM;
+
+ /* first client will create kernel thread */
+ if (!chan->ctrl->task) {
+ chan->ctrl->task =
+ kthread_create_on_node(svc_normal_to_secure_thread,
+ (void *)chan->ctrl,
+ cpu_to_node(cpu),
+ "svc_smc_hvc_thread");
+ if (IS_ERR(chan->ctrl->task)) {
+ dev_err(chan->ctrl->dev,
+ "fails to create svc_smc_hvc_thread\n");
+ kfree(p_data);
+ return -EINVAL;
+ }
+ kthread_bind(chan->ctrl->task, cpu);
+ wake_up_process(chan->ctrl->task);
+ }
+
+ pr_debug("%s: sent P-va=%p, P-com=%x, P-size=%u\n", __func__,
+ p_msg->payload, p_msg->command,
+ (unsigned int)p_msg->payload_length);
+
+ list_for_each_entry(p_mem, &svc_data_mem, node)
+ if (p_mem->vaddr == p_msg->payload) {
+ p_data->paddr = p_mem->paddr;
+ break;
+ }
+
+ p_data->command = p_msg->command;
+ p_data->arg[0] = p_msg->arg[0];
+ p_data->arg[1] = p_msg->arg[1];
+ p_data->arg[2] = p_msg->arg[2];
+ p_data->size = p_msg->payload_length;
+ p_data->chan = chan;
+ pr_debug("%s: put to FIFO pa=0x%016x, cmd=%x, size=%u\n", __func__,
+ (unsigned int)p_data->paddr, p_data->command,
+ (unsigned int)p_data->size);
+ ret = kfifo_in_spinlocked(&chan->ctrl->svc_fifo, p_data,
+ sizeof(*p_data),
+ &chan->ctrl->svc_fifo_lock);
+ wake_up_process(chan->ctrl->task);
+
+ kfree(p_data);
+
+ if (!ret)
+ return -ENOBUFS;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(intel_svc_send);
+
+/**
+ * intel_svc_done() - complete service request transactions
+ * @chan: service channel assigned to the client
+ *
+ * This function should be called when client has finished its request
+ * or there is an error in the request process. It allows the service layer
+ * to stop the running thread to have maximize savings in kernel resources.
+ */
+void intel_svc_done(struct intel_svc_chan *chan)
+{
+ /* stop thread when thread is running AND only one active client */
+ if (chan->ctrl->task && (chan->ctrl->num_active_client <= 1)) {
+ pr_debug("svc_smc_hvc_shm_thread is stopped\n");
+ kthread_stop(chan->ctrl->task);
+ chan->ctrl->task = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(intel_svc_done);
+
+/**
+ * intel_svc_allocate_memory() - allocate memory
+ * @chan: service channel assigned to the client
+ * @size: memory size requested by a specific service client
+ *
+ * Service layer allocates the requested number of bytes buffer from the
+ * memory pool, service client uses this function to get allocated buffers.
+ *
+ * Return: address of allocated memory on success, or ERR_PTR() on error.
+ */
+void *intel_svc_allocate_memory(struct intel_svc_chan *chan, size_t size)
+{
+ struct intel_svc_data_mem *pmem;
+ unsigned long va;
+ phys_addr_t pa;
+ struct gen_pool *genpool = chan->ctrl->genpool;
+ size_t s = roundup(size, 1 << genpool->min_alloc_order);
+
+ pmem = devm_kzalloc(chan->ctrl->dev, sizeof(*pmem), GFP_KERNEL);
+ if (!pmem)
+ return ERR_PTR(-ENOMEM);
+
+ va = gen_pool_alloc(genpool, s);
+ if (!va)
+ return ERR_PTR(-ENOMEM);
+
+ memset((void *)va, 0, s);
+ pa = gen_pool_virt_to_phys(genpool, va);
+
+ pmem->vaddr = (void *)va;
+ pmem->paddr = pa;
+ pmem->size = s;
+ list_add_tail(&pmem->node, &svc_data_mem);
+ pr_debug("%s: va=%p, pa=0x%016x\n", __func__,
+ pmem->vaddr, (unsigned int)pmem->paddr);
+
+ return (void *)va;
+}
+EXPORT_SYMBOL_GPL(intel_svc_allocate_memory);
+
+/**
+ * intel_svc_free_memory() - free allocated memory
+ * @chan: service channel assigned to the client
+ * @kaddr: memory to be freed
+ *
+ * This function is used by service client to free allocated buffers.
+ */
+void intel_svc_free_memory(struct intel_svc_chan *chan, void *kaddr)
+{
+ struct intel_svc_data_mem *pmem;
+ size_t size = 0;
+
+ list_for_each_entry(pmem, &svc_data_mem, node)
+ if (pmem->vaddr == kaddr) {
+ size = pmem->size;
+ break;
+ }
+
+ gen_pool_free(chan->ctrl->genpool, (unsigned long)kaddr, size);
+ pmem->vaddr = NULL;
+ list_del(&pmem->node);
+}
+EXPORT_SYMBOL_GPL(intel_svc_free_memory);
+
+/**
+ * svc_pa_to_va() - translate physical address to virtual address
+ * @addr: to be translated physical address
+ *
+ * Return: valid virtual address or NULL if the provided physical
+ * address doesn't exist.
+ */
+static void *svc_pa_to_va(unsigned long addr)
+{
+ struct intel_svc_data_mem *pmem;
+
+ pr_debug("claim back P-addr=0x%016x\n", (unsigned int)addr);
+ list_for_each_entry(pmem, &svc_data_mem, node)
+ if (pmem->paddr == addr)
+ return pmem->vaddr;
+
+ /* physical address is not found */
+ return NULL;
+}
+
+/**
+ * svc_thread_cmd_data_claim() - claim back buffer from the secure world
+ * @addr: pointer to service layer controller
+ * @p_data: pointer to service data structure
+ * @c_data: pointer to callback data structure to service client
+ *
+ * Claim back the submitted buffers from the secure world and pass buffer
+ * back to service client (FPGA manager, etc) for reuse.
+ */
+static void svc_thread_cmd_data_claim(struct intel_svc_controller *ctrl,
+ struct intel_svc_data *p_data,
+ struct intel_svc_c_data *c_data)
+{
+ struct arm_smccc_res res;
+ unsigned long timeout;
+
+ reinit_completion(&ctrl->complete_status);
+ timeout = msecs_to_jiffies(FPGA_CONFIG_DATA_CLAIM_TIMEOUT_MS);
+
+ pr_debug("%s: claim back the submitted buffer\n", __func__);
+ do {
+ ctrl->invoke_fn(INTEL_SIP_SMC_FPGA_CONFIG_COMPLETED_WRITE,
+ 0, 0, 0, 0, 0, 0, 0, &res);
+
+ if (res.a0 == INTEL_SIP_SMC_STATUS_OK) {
+ if (!res.a1) {
+ complete(&ctrl->complete_status);
+ break;
+ }
+ c_data->status = BIT(SVC_STATUS_RECONFIG_BUFFER_DONE);
+ c_data->kaddr1 = svc_pa_to_va(res.a1);
+ c_data->kaddr2 = (res.a2) ? svc_pa_to_va(res.a2) : NULL;
+ c_data->kaddr3 = (res.a3) ? svc_pa_to_va(res.a3) : NULL;
+ p_data->chan->scl->receive_cb(p_data->chan->scl,
+ c_data);
+ } else {
+ pr_debug("%s: secure world busy, polling again\n",
+ __func__);
+ }
+ } while (res.a0 == INTEL_SIP_SMC_STATUS_OK ||
+ res.a0 == INTEL_SIP_SMC_FPGA_CONFIG_STATUS_BUSY ||
+ wait_for_completion_timeout(&ctrl->complete_status, timeout));
+}
+
+/**
+ * svc_thread_cmd_config_status() - check configuration status
+ * @ctrl: pointer to service layer controller
+ * @p_data: pointer to service data structure
+ * @c_data: pointer to callback data structure to service client
+ *
+ * Check whether the secure firmware at secure world has finished the FPGA
+ * configuration, and then inform FPGA manager the configuration status.
+ */
+static void svc_thread_cmd_config_status(struct intel_svc_controller *ctrl,
+ struct intel_svc_data *p_data,
+ struct intel_svc_c_data *c_data)
+{
+ struct arm_smccc_res res;
+ int count_in_sec;
+
+ c_data->kaddr1 = NULL;
+ c_data->kaddr2 = NULL;
+ c_data->kaddr3 = NULL;
+ c_data->status = BIT(SVC_STATUS_RECONFIG_ERROR);
+
+ pr_debug("%s: polling config status\n", __func__);
+
+ count_in_sec = FPGA_CONFIG_STATUS_TIMEOUT_SEC;
+ while (count_in_sec) {
+ ctrl->invoke_fn(INTEL_SIP_SMC_FPGA_CONFIG_ISDONE,
+ 0, 0, 0, 0, 0, 0, 0, &res);
+ if ((res.a0 == INTEL_SIP_SMC_STATUS_OK) ||
+ (res.a0 == INTEL_SIP_SMC_FPGA_CONFIG_STATUS_ERROR))
+ break;
+
+ /*
+ * configuration is still in progress, wait one second then
+ * poll again
+ */
+ msleep(1000);
+ count_in_sec--;
+ };
+
+ if (res.a0 == INTEL_SIP_SMC_STATUS_OK && count_in_sec)
+ c_data->status = BIT(SVC_STATUS_RECONFIG_COMPLETED);
+
+ p_data->chan->scl->receive_cb(p_data->chan->scl, c_data);
+}
+
+/**
+ * svc_thread_recv_status_ok() - handle the successful status
+ * @p_data: pointer to service data structure
+ * @c_data: pointer to callback data structure to service client
+ * @res: result from SMC or HVC call
+ *
+ * Send back the correspond status to the service client (FPGA manager etc).
+ */
+static void svc_thread_recv_status_ok(struct intel_svc_data *p_data,
+ struct intel_svc_c_data *c_data,
+ struct arm_smccc_res res)
+{
+ c_data->kaddr1 = NULL;
+ c_data->kaddr2 = NULL;
+ c_data->kaddr3 = NULL;
+
+ switch (p_data->command) {
+ case COMMAND_RECONFIG:
+ c_data->status = BIT(SVC_STATUS_RECONFIG_REQUEST_OK);
+ break;
+ case COMMAND_RECONFIG_DATA_SUBMIT:
+ c_data->status = BIT(SVC_STATUS_RECONFIG_BUFFER_SUBMITTED);
+ break;
+ case COMMAND_NOOP:
+ c_data->status = BIT(SVC_STATUS_RECONFIG_BUFFER_SUBMITTED);
+ c_data->kaddr1 = svc_pa_to_va(res.a1);
+ break;
+ case COMMAND_RECONFIG_STATUS:
+ c_data->status = BIT(SVC_STATUS_RECONFIG_COMPLETED);
+ break;
+ default:
+ break;
+ }
+
+ pr_debug("%s: call receive_cb\n", __func__);
+ p_data->chan->scl->receive_cb(p_data->chan->scl, c_data);
+}
+
+/**
+ * svc_normal_to_secure_thread() - the function to run in the kthread
+ * @data: data pointer for kthread function
+ *
+ * Service layer driver creates intel_svc_smc_hvc_call kthread on CPU
+ * node 0, its function intel_svc_secure_call_thread is used to handle
+ * SMC or HVC calls between kernel driver and secure monitor software.
+ *
+ * Return: 0
+ */
+static int svc_normal_to_secure_thread(void *data)
+{
+ struct intel_svc_controller *ctrl = (struct intel_svc_controller *)data;
+ struct intel_svc_data *pdata;
+ struct intel_svc_c_data *cdata;
+ struct arm_smccc_res res;
+ unsigned long a0, a1, a2;
+ int ret_fifo = 0;
+
+ pdata = kmalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ cdata = kmalloc(sizeof(*cdata), GFP_KERNEL);
+ if (!cdata) {
+ kfree(pdata);
+ return -ENOMEM;
+ }
+
+ /* default set, to remove build warning */
+ a0 = INTEL_SIP_SMC_FPGA_CONFIG_LOOPBACK;
+ a1 = 0;
+ a2 = 0;
+
+ pr_debug("smc_hvc_shm_thread is running\n");
+
+ while (!kthread_should_stop()) {
+ ret_fifo = kfifo_out_spinlocked(&ctrl->svc_fifo,
+ pdata, sizeof(*pdata),
+ &ctrl->svc_fifo_lock);
+
+ if (!ret_fifo) {
+ schedule_timeout_interruptible(MAX_SCHEDULE_TIMEOUT);
+ continue;
+ }
+
+ pr_debug("get from FIFO pa=0x%016x, command=%u, size=%u\n",
+ (unsigned int)pdata->paddr, pdata->command,
+ (unsigned int)pdata->size);
+
+ switch (pdata->command) {
+ case COMMAND_RECONFIG_DATA_CLAIM:
+ svc_thread_cmd_data_claim(ctrl, pdata, cdata);
+ continue;
+ case COMMAND_RECONFIG:
+ a0 = INTEL_SIP_SMC_FPGA_CONFIG_START;
+ a1 = 0;
+ a2 = 0;
+ break;
+ case COMMAND_RECONFIG_DATA_SUBMIT:
+ a0 = INTEL_SIP_SMC_FPGA_CONFIG_WRITE;
+ a1 = (unsigned long)pdata->paddr;
+ a2 = (unsigned long)pdata->size;
+ break;
+ case COMMAND_RECONFIG_STATUS:
+ a0 = INTEL_SIP_SMC_FPGA_CONFIG_ISDONE;
+ a1 = 0;
+ a2 = 0;
+ break;
+ case COMMAND_RSU_STATUS:
+ a0 = INTEL_SIP_SMC_RSU_STATUS;
+ a1 = 0;
+ a2 = 0;
+ break;
+ case COMMAND_RSU_UPDATE:
+ a0 = INTEL_SIP_SMC_RSU_UPDATE;
+ a1 = pdata->arg[0];
+ a2 = 0;
+ break;
+ default:
+ /* it shouldn't happen */
+ break;
+ }
+ pr_debug("%s: before SMC call -- a0=0x%016x a1=0x%016x",
+ __func__, (unsigned int)a0, (unsigned int)a1);
+ pr_debug(" a2=0x%016x\n", (unsigned int)a2);
+
+ ctrl->invoke_fn(a0, a1, a2, 0, 0, 0, 0, 0, &res);
+
+ pr_debug("%s: after SMC call -- res.a0=0x%016x",
+ __func__, (unsigned int)res.a0);
+ pr_debug(" res.a1=0x%016x, res.a2=0x%016x",
+ (unsigned int)res.a1, (unsigned int)res.a2);
+ pr_debug(" res.a3=0x%016x\n", (unsigned int)res.a3);
+
+ if (pdata->command == COMMAND_RSU_STATUS) {
+ if (res.a0 == INTEL_SIP_SMC_RSU_ERROR)
+ cdata->status = 0;
+ else
+ cdata->status = BIT(SVC_STATUS_RSU_OK);
+
+ cdata->kaddr1 = &res;
+ cdata->kaddr2 = NULL;
+ cdata->kaddr3 = NULL;
+ pdata->chan->scl->receive_cb(pdata->chan->scl, cdata);
+ continue;
+ }
+
+ if (pdata->command == COMMAND_RSU_UPDATE) {
+ if (res.a0 == INTEL_SIP_SMC_STATUS_OK)
+ cdata->status = BIT(SVC_STATUS_RSU_OK);
+ else
+ cdata->status = 0;
+
+ cdata->kaddr1 = NULL;
+ cdata->kaddr2 = NULL;
+ cdata->kaddr3 = NULL;
+ pdata->chan->scl->receive_cb(pdata->chan->scl, cdata);
+ continue;
+ }
+
+ switch (res.a0) {
+ case INTEL_SIP_SMC_STATUS_OK:
+ svc_thread_recv_status_ok(pdata, cdata, res);
+ break;
+ case INTEL_SIP_SMC_FPGA_CONFIG_STATUS_BUSY:
+ switch (pdata->command) {
+ case COMMAND_RECONFIG_DATA_SUBMIT:
+ svc_thread_cmd_data_claim(ctrl,
+ pdata, cdata);
+ break;
+ case COMMAND_RECONFIG_STATUS:
+ svc_thread_cmd_config_status(ctrl,
+ pdata, cdata);
+ break;
+ default:
+ break;
+ }
+ break;
+ case INTEL_SIP_SMC_FPGA_CONFIG_STATUS_REJECTED:
+ pr_debug("%s: STATUS_REJECTED\n", __func__);
+ break;
+ case INTEL_SIP_SMC_FPGA_CONFIG_STATUS_ERROR:
+ pr_err("%s: STATUS_ERROR\n", __func__);
+ cdata->status = BIT(SVC_STATUS_RECONFIG_ERROR);
+ cdata->kaddr1 = NULL;
+ cdata->kaddr2 = NULL;
+ cdata->kaddr3 = NULL;
+ pdata->chan->scl->receive_cb(pdata->chan->scl, cdata);
+ break;
+ default:
+ break;
+ }
+ };
+
+ kfree(cdata);
+ kfree(pdata);
+
+ return 0;
+}
+
+/**
+ * svc_normal_to_secure_shm_thread() - the function to run in the kthread
+ * @data: data pointer for kthread function
+ *
+ * Service layer driver creates intel_svc_smc_hvc_shm kthread on CPU
+ * node 0, its function intel_svc_secure_shm_thread is used to query the
+ * physical address of memory block reserved by secure monitor software at
+ * secure world.
+ *
+ * Return: 0
+ */
+static int svc_normal_to_secure_shm_thread(void *data)
+{
+ struct intel_svc_sh_memory *sh_mem = (struct intel_svc_sh_memory *)data;
+ struct arm_smccc_res res;
+
+ /* SMC or HVC call to get shared memory info from secure world */
+ sh_mem->invoke_fn(INTEL_SIP_SMC_FPGA_CONFIG_GET_MEM,
+ 0, 0, 0, 0, 0, 0, 0, &res);
+ if (res.a0 == INTEL_SIP_SMC_STATUS_OK) {
+ sh_mem->addr = res.a1;
+ sh_mem->size = res.a2;
+ } else {
+ pr_err("%s: after SMC call -- res.a0=0x%016x", __func__,
+ (unsigned int)res.a0);
+ sh_mem->addr = 0;
+ sh_mem->size = 0;
+ }
+
+ complete(&sh_mem->sync_complete);
+ do_exit(0);
+}
+
+/**
+ * svc_get_sh_memory_param() - get memory block reserved by secure monitor SW
+ * @pdev: pointer to service layer device
+ * @param: pointer to service shared memory structure
+ *
+ * Return: zero for successfully getting the physical address of memory block
+ * reserved by secure monitor software, or negative value on error.
+ */
+static int svc_get_sh_memory_param(struct platform_device *pdev,
+ struct intel_svc_sh_memory *param)
+{
+ struct device *dev = &pdev->dev;
+ struct task_struct *sh_memory_task;
+ unsigned int cpu = 0;
+
+ init_completion(&param->sync_complete);
+
+ /* smc/hvc call happens on cpu 0 bound kthread */
+ sh_memory_task = kthread_create_on_node(svc_normal_to_secure_shm_thread,
+ (void *)param, cpu_to_node(cpu),
+ "svc_smc_hvc_shm_thread");
+ if (IS_ERR(sh_memory_task))
+ dev_err(dev, "fail to create intel_svc_smc_shm_thread\n");
+ kthread_bind(sh_memory_task, cpu);
+ wake_up_process(sh_memory_task);
+
+ if (!wait_for_completion_timeout(&param->sync_complete, 10 * HZ)) {
+ dev_err(dev,
+ "timeout to get sh-memory paras from secure world\n");
+ return -ETIMEDOUT;
+ }
+
+ if (!param->addr || !param->size) {
+ dev_err(dev,
+ "fails to get shared memory info from secure world\n");
+ return -ENOMEM;
+ }
+
+ dev_dbg(dev, "SM software provides paddr: 0x%016x, size: 0x%08x\n",
+ (unsigned int)param->addr,
+ (unsigned int)param->size);
+
+ return 0;
+}
+
+/**
+ * svc_create_memory_pool() - create a memory pool from reserved memory block
+ * @pdev: pointer to service layer device
+ * @param: pointer to service shared memory structure
+ *
+ * Return: pool allocated from reserved memory block or ERR_PTR() on error.
+ */
+static struct gen_pool *
+svc_create_memory_pool(struct platform_device *pdev,
+ struct intel_svc_sh_memory *param)
+{
+ struct device *dev = &pdev->dev;
+ struct gen_pool *genpool;
+ unsigned long vaddr;
+ phys_addr_t paddr;
+ size_t size;
+ phys_addr_t begin;
+ phys_addr_t end;
+ void *va;
+ size_t page_mask = PAGE_SIZE - 1;
+ int min_alloc_order = 3;
+ int ret;
+
+ begin = roundup(param->addr, PAGE_SIZE);
+ end = rounddown(param->addr + param->size, PAGE_SIZE);
+ paddr = begin;
+ size = end - begin;
+ va = memremap(paddr, size, MEMREMAP_WC);
+ if (!va) {
+ dev_err(dev, "fail to remap shared memory\n");
+ return ERR_PTR(-EINVAL);
+ }
+ vaddr = (unsigned long)va;
+ dev_dbg(dev,
+ "reserved memory vaddr: %p, paddr: 0x%16x size: 0x%8x\n",
+ va, (unsigned int)paddr, (unsigned int)size);
+ if ((vaddr & page_mask) || (paddr & page_mask) ||
+ (size & page_mask)) {
+ dev_err(dev, "page is not aligned\n");
+ return ERR_PTR(-EINVAL);
+ }
+ genpool = gen_pool_create(min_alloc_order, -1);
+ if (!genpool) {
+ dev_err(dev, "fail to create genpool\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ gen_pool_set_algo(genpool, gen_pool_best_fit, NULL);
+ ret = gen_pool_add_virt(genpool, vaddr, paddr, size, -1);
+ if (ret) {
+ dev_err(dev, "fail to add memory chunk to the pool\n");
+ gen_pool_destroy(genpool);
+ return ERR_PTR(ret);
+ }
+
+ return genpool;
+}
+
+/**
+ * svc_smccc_smc() - secure monitor call between normal and secure world
+ * @a0-a7: arguments passed in registers 0 to 7
+ * @res: result values from register 0 to 3
+ */
+static void svc_smccc_smc(unsigned long a0, unsigned long a1,
+ unsigned long a2, unsigned long a3,
+ unsigned long a4, unsigned long a5,
+ unsigned long a6, unsigned long a7,
+ struct arm_smccc_res *res)
+{
+ arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res);
+}
+
+/**
+ * svc_smccc_hvc() - hypervisor call between normal and secure world
+ * @a0-a7: arguments passed in registers 0 to 7
+ * @res: result values from register 0 to 3
+ */
+static void svc_smccc_hvc(unsigned long a0, unsigned long a1,
+ unsigned long a2, unsigned long a3,
+ unsigned long a4, unsigned long a5,
+ unsigned long a6, unsigned long a7,
+ struct arm_smccc_res *res)
+{
+ arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res);
+}
+
+/**
+ * get_invoke_func() - invoke SMC or HVC call
+ * @dev: pointer to device
+ *
+ * Return: function pointer to svc_smccc_smc or svc_smccc_hvc.
+ */
+static svc_invoke_fn *get_invoke_func(struct device *dev)
+{
+ const char *method;
+
+ if (of_property_read_string(dev->of_node, "method", &method)) {
+ dev_warn(dev, "missing \"method\" property\n");
+ return ERR_PTR(-ENXIO);
+ }
+
+ if (!strcmp(method, "smc"))
+ return svc_smccc_smc;
+ if (!strcmp(method, "hvc"))
+ return svc_smccc_hvc;
+
+ dev_warn(dev, "invalid \"method\" property: %s\n", method);
+
+ return ERR_PTR(-EINVAL);
+}
+
+static const struct of_device_id intel_svc_drv_match[] = {
+ {.compatible = "intel,stratix10-svc"},
+ {},
+};
+
+static int intel_svc_drv_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct intel_svc_controller *controller;
+ struct intel_svc_chan *chans;
+ struct gen_pool *genpool;
+ struct intel_svc_sh_memory *sh_memory;
+ svc_invoke_fn *invoke_fn;
+ size_t fifo_size;
+ int ret;
+
+ /* get SMC or HVC function */
+ invoke_fn = get_invoke_func(dev);
+ if (IS_ERR(invoke_fn))
+ return -EINVAL;
+
+ sh_memory = devm_kzalloc(dev, sizeof(*sh_memory), GFP_KERNEL);
+ if (!sh_memory)
+ return -ENOMEM;
+
+ sh_memory->invoke_fn = invoke_fn;
+ ret = svc_get_sh_memory_param(pdev, sh_memory);
+ if (ret)
+ return ret;
+
+ genpool = svc_create_memory_pool(pdev, sh_memory);
+ if (!genpool)
+ return -ENOMEM;
+
+ /* allocate service controller and supporting channel */
+ controller = devm_kzalloc(dev, sizeof(*controller), GFP_KERNEL);
+ if (!controller)
+ return -ENOMEM;
+
+ chans = devm_kmalloc_array(dev, SVC_NUM_CHANNEL,
+ sizeof(*chans), GFP_KERNEL | __GFP_ZERO);
+ if (!chans)
+ return -ENOMEM;
+
+ controller->dev = dev;
+ controller->num_chans = SVC_NUM_CHANNEL;
+ controller->num_active_client = 0;
+ controller->chans = chans;
+ controller->genpool = genpool;
+ controller->task = NULL;
+ controller->invoke_fn = invoke_fn;
+ init_completion(&controller->complete_status);
+
+ fifo_size = sizeof(struct intel_svc_data) * SVC_NUM_DATA_IN_FIFO;
+ ret = kfifo_alloc(&controller->svc_fifo, fifo_size, GFP_KERNEL);
+ if (ret) {
+ dev_err(dev, "fails to allocate FIFO\n");
+ return ret;
+ }
+ spin_lock_init(&controller->svc_fifo_lock);
+
+ chans[0].scl = NULL;
+ chans[0].ctrl = controller;
+ chans[0].name = "fpga";
+ spin_lock_init(&chans[0].lock);
+
+ chans[1].scl = NULL;
+ chans[1].ctrl = controller;
+ chans[1].name = "rsu";
+ spin_lock_init(&chans[1].lock);
+
+ list_add_tail(&controller->node, &svc_ctrl);
+ platform_set_drvdata(pdev, controller);
+
+ pr_info("Intel Service Layer Driver Initialized\n");
+
+ return ret;
+}
+
+static int intel_svc_drv_remove(struct platform_device *pdev)
+{
+ struct intel_svc_controller *ctrl = platform_get_drvdata(pdev);
+
+ kfifo_free(&ctrl->svc_fifo);
+ if (ctrl->task) {
+ kthread_stop(ctrl->task);
+ ctrl->task = NULL;
+ }
+ if (ctrl->genpool)
+ gen_pool_destroy(ctrl->genpool);
+ list_del(&ctrl->node);
+
+ return 0;
+}
+
+static struct platform_driver intel_svc_driver = {
+ .probe = intel_svc_drv_probe,
+ .remove = intel_svc_drv_remove,
+ .driver = {
+ .name = "intel-svc",
+ .of_match_table = intel_svc_drv_match,
+ },
+};
+
+static int __init intel_svc_init(void)
+{
+ struct device_node *fw_np;
+ struct device_node *np;
+ int ret;
+
+ fw_np = of_find_node_by_name(NULL, "firmware");
+ if (!fw_np)
+ return -ENODEV;
+
+ np = of_find_matching_node(fw_np, intel_svc_drv_match);
+ if (!np) {
+ of_node_put(fw_np);
+ return -ENODEV;
+ }
+
+ of_node_put(np);
+ ret = of_platform_populate(fw_np, intel_svc_drv_match, NULL, NULL);
+ of_node_put(fw_np);
+ if (ret)
+ return ret;
+
+ return platform_driver_register(&intel_svc_driver);
+}
+
+static void __exit intel_svc_exit(void)
+{
+ return platform_driver_unregister(&intel_svc_driver);
+}
+
+subsys_initcall(intel_svc_init);
+module_exit(intel_svc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel Stratix10 Service Layer Driver");
+MODULE_AUTHOR("Richard Gong <richard.gong@intel.com>");
+MODULE_ALIAS("platform:intel-svc");
diff --git a/drivers/misc/intel-smc.h b/drivers/misc/intel-smc.h
new file mode 100644
index 000000000000..1612e5d8dca1
--- /dev/null
+++ b/drivers/misc/intel-smc.h
@@ -0,0 +1,310 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2017-2018, Intel Corporation
+ */
+
+#ifndef __INTEL_SMC_H
+#define __INTEL_SMC_H
+
+#include <linux/arm-smccc.h>
+#include <linux/bitops.h>
+
+/*
+ * This file defines the Secure Monitor Call (SMC) message protocol used for
+ * service layer driver in normal world (EL1) to communicate with secure
+ * monitor software in Secure Monitor Exception Level 3 (EL3).
+ *
+ * This file is shared with secure firmware (FW) which is out of kernel tree.
+ *
+ * An ARM SMC instruction takes a function identifier and up to 6 64-bit
+ * register values as arguments, and can return up to 4 64-bit register
+ * value. The operation of the secure monitor is determined by the parameter
+ * values passed in through registers.
+
+ * EL1 and EL3 communicates pointer as physical address rather than the
+ * virtual address.
+ */
+
+/*
+ * Functions specified by ARM SMC Calling convention:
+ *
+ * FAST call executes atomic operations, returns when the requested operation
+ * has completed.
+ * STD call starts a operation which can be preempted by a non-secure
+ * interrupt. The call can return before the requested operation has
+ * completed.
+ *
+ * a0..a7 is used as register names in the descriptions below, on arm32
+ * that translates to r0..r7 and on arm64 to w0..w7.
+ */
+
+#define INTEL_SIP_SMC_STD_CALL_VAL(func_num) \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL, ARM_SMCCC_SMC_64, \
+ ARM_SMCCC_OWNER_SIP, (func_num))
+
+#define INTEL_SIP_SMC_FAST_CALL_VAL(func_num) \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64, \
+ ARM_SMCCC_OWNER_SIP, (func_num))
+
+/*
+ * Return values in INTEL_SIP_SMC_* call
+ *
+ * INTEL_SIP_SMC_RETURN_UNKNOWN_FUNCTION:
+ * Secure monitor software doesn't recognize the request.
+ *
+ * INTEL_SIP_SMC_STATUS_OK:
+ * FPGA configuration completed successfully,
+ * In case of FPGA configuration write operation, it means secure monitor
+ * software can accept the next chunk of FPGA configuration data.
+ *
+ * INTEL_SIP_SMC_FPGA_CONFIG_STATUS_BUSY:
+ * In case of FPGA configuration write operation, it means secure monitor
+ * software is still processing previous data & can't accept the next chunk
+ * of data. Service driver needs to issue
+ * INTEL_SIP_SMC_FPGA_CONFIG_COMPLETED_WRITE call to query the
+ * completed block(s).
+ *
+ * INTEL_SIP_SMC_FPGA_CONFIG_STATUS_ERROR:
+ * There is error during the FPGA configuration process.
+ *
+ * INTEL_SIP_SMC_REG_ERROR:
+ * There is error during a read or write operation of the protected
+ * registers.
+ */
+#define INTEL_SIP_SMC_RETURN_UNKNOWN_FUNCTION 0xFFFFFFFF
+#define INTEL_SIP_SMC_STATUS_OK 0x0
+#define INTEL_SIP_SMC_FPGA_CONFIG_STATUS_BUSY 0x1
+#define INTEL_SIP_SMC_FPGA_CONFIG_STATUS_REJECTED 0x2
+#define INTEL_SIP_SMC_FPGA_CONFIG_STATUS_ERROR 0x4
+#define INTEL_SIP_SMC_REG_ERROR 0x5
+#define INTEL_SIP_SMC_RSU_ERROR 0x7
+
+/*
+ * Request INTEL_SIP_SMC_FPGA_CONFIG_START
+ *
+ * Sync call used by service driver at EL1 to request the FPGA in EL3 to
+ * be prepare to receive a new configuration.
+ *
+ * Call register usage:
+ * a0: INTEL_SIP_SMC_FPGA_CONFIG_START.
+ * a1: flag for full or partial configuration
+ * 0 full reconfiguration.
+ * 1 partial reconfiguration.
+ * a2-7: not used.
+ *
+ * Return status:
+ * a0: INTEL_SIP_SMC_STATUS_OK, or INTEL_SIP_SMC_FPGA_CONFIG_STATUS_ERROR.
+ * a1-3: not used.
+ */
+#define INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_START 1
+#define INTEL_SIP_SMC_FPGA_CONFIG_START \
+ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_START)
+
+/*
+ * Request INTEL_SIP_SMC_FPGA_CONFIG_WRITE
+ *
+ * Async call used by service driver at EL1 to provide FPGA configuration data
+ * to secure world.
+ *
+ * Call register usage:
+ * a0: INTEL_SIP_SMC_FPGA_CONFIG_WRITE.
+ * a1: 64bit physical address of the configuration data memory block
+ * a2: Size of configuration data block.
+ * a3-7: not used.
+ *
+ * Return status:
+ * a0: INTEL_SIP_SMC_STATUS_OK, INTEL_SIP_SMC_FPGA_CONFIG_STATUS_BUSY or
+ * INTEL_SIP_SMC_FPGA_CONFIG_STATUS_ERROR.
+ * a1: 64bit physical address of 1st completed memory block if any completed
+ * block, otherwise zero value.
+ * a2: 64bit physical address of 2nd completed memory block if any completed
+ * block, otherwise zero value.
+ * a3: 64bit physical address of 3rd completed memory block if any completed
+ * block, otherwise zero value.
+ */
+#define INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_WRITE 2
+#define INTEL_SIP_SMC_FPGA_CONFIG_WRITE \
+ INTEL_SIP_SMC_STD_CALL_VAL(INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_WRITE)
+
+/*
+ * Request INTEL_SIP_SMC_FPGA_CONFIG_COMPLETED_WRITE
+ *
+ * Sync call used by service driver at EL1 to track the completed write
+ * transactions. This request is called after INTEL_SIP_SMC_FPGA_CONFIG_WRITE
+ * call returns INTEL_SIP_SMC_FPGA_CONFIG_STATUS_BUSY.
+ *
+ * Call register usage:
+ * a0: INTEL_SIP_SMC_FPGA_CONFIG_COMPLETED_WRITE.
+ * a1-7: not used.
+ *
+ * Return status:
+ * a0: INTEL_SIP_SMC_STATUS_OK, INTEL_SIP_SMC_FPGA_CONFIG_STATUS_BUSY or
+ * INTEL_SIP_SMC_FPGA_CONFIG_STATUS_ERROR.
+ * a1: 64bit physical address of 1st completed memory block.
+ * a2: 64bit physical address of 2nd completed memory block if
+ * any completed block, otherwise zero value.
+ * a3: 64bit physical address of 3rd completed memory block if
+ * any completed block, otherwise zero value.
+ */
+#define INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_COMPLETED_WRITE 3
+#define INTEL_SIP_SMC_FPGA_CONFIG_COMPLETED_WRITE \
+INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_COMPLETED_WRITE)
+
+/*
+ * Request INTEL_SIP_SMC_FPGA_CONFIG_ISDONE
+ *
+ * Sync call used by service driver at EL1 to inform secure world that all
+ * data are sent, to check whether or not the secure world had completed
+ * the FPGA configuration process.
+ *
+ * Call register usage:
+ * a0: INTEL_SIP_SMC_FPGA_CONFIG_ISDONE.
+ * a1-7: not used.
+ *
+ * Return status:
+ * a0: INTEL_SIP_SMC_STATUS_OK, INTEL_SIP_SMC_FPGA_CONFIG_STATUS_BUSY or
+ * INTEL_SIP_SMC_FPGA_CONFIG_STATUS_ERROR.
+ * a1-3: not used.
+ */
+#define INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_ISDONE 4
+#define INTEL_SIP_SMC_FPGA_CONFIG_ISDONE \
+ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_ISDONE)
+
+/*
+ * Request INTEL_SIP_SMC_FPGA_CONFIG_GET_MEM
+ *
+ * Sync call used by service driver at EL1 to query the physical address of
+ * memory block reserved by secure monitor software.
+ *
+ * Call register usage:
+ * a0:INTEL_SIP_SMC_FPGA_CONFIG_GET_MEM.
+ * a1-7: not used.
+ *
+ * Return status:
+ * a0: INTEL_SIP_SMC_STATUS_OK or INTEL_SIP_SMC_FPGA_CONFIG_STATUS_ERROR.
+ * a1: start of physical address of reserved memory block.
+ * a2: size of reserved memory block.
+ * a3: not used.
+ */
+#define INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_GET_MEM 5
+#define INTEL_SIP_SMC_FPGA_CONFIG_GET_MEM \
+ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_GET_MEM)
+
+/*
+ * Request INTEL_SIP_SMC_FPGA_CONFIG_LOOPBACK
+ *
+ * For SMC loop-back mode only, used for internal integration, debugging
+ * or troubleshooting.
+ *
+ * Call register usage:
+ * a0: INTEL_SIP_SMC_FPGA_CONFIG_LOOPBACK.
+ * a1-7: not used.
+ *
+ * Return status:
+ * a0: INTEL_SIP_SMC_STATUS_OK or INTEL_SIP_SMC_FPGA_CONFIG_STATUS_ERROR.
+ * a1-3: not used.
+ */
+#define INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_LOOPBACK 6
+#define INTEL_SIP_SMC_FPGA_CONFIG_LOOPBACK \
+ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_LOOPBACK)
+
+/*
+ * Request INTEL_SIP_SMC_REG_READ
+ *
+ * Read a protected register using SMCCC
+ *
+ * Call register usage:
+ * a0: INTEL_SIP_SMC_REG_READ.
+ * a1: register address.
+ * a2-7: not used.
+ *
+ * Return status:
+ * a0: INTEL_SIP_SMC_STATUS_OK or INTEL_SIP_SMC_REG_ERROR.
+ * a1: Value in the register
+ * a2-3: not used.
+ */
+#define INTEL_SIP_SMC_FUNCID_REG_READ 7
+#define INTEL_SIP_SMC_REG_READ \
+ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_REG_READ)
+
+/*
+ * Request INTEL_SIP_SMC_REG_WRITE
+ *
+ * Write a protected register using SMCCC
+ *
+ * Call register usage:
+ * a0: INTEL_SIP_SMC_REG_WRITE.
+ * a1: register address
+ * a2: value to program into register.
+ * a3-7: not used.
+ *
+ * Return status:
+ * a0: INTEL_SIP_SMC_STATUS_OK or INTEL_SIP_SMC_REG_ERROR.
+ * a1-3: not used.
+ */
+#define INTEL_SIP_SMC_FUNCID_REG_WRITE 8
+#define INTEL_SIP_SMC_REG_WRITE \
+ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_REG_WRITE)
+
+/*
+ * Request INTEL_SIP_SMC_FUNCID_REG_UPDATE
+ *
+ * Update one or more bits in a protected register using a
+ * read-modify-write operation.
+ *
+ * Call register usage:
+ * a0: INTEL_SIP_SMC_REG_UPDATE.
+ * a1: register address
+ * a2: Write Mask.
+ * a3: Value to write.
+ * a4-7: not used.
+ *
+ * Return status:
+ * a0: INTEL_SIP_SMC_STATUS_OK or INTEL_SIP_SMC_REG_ERROR.
+ * a1-3: Not used.
+ */
+#define INTEL_SIP_SMC_FUNCID_REG_UPDATE 9
+#define INTEL_SIP_SMC_REG_UPDATE \
+ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_REG_UPDATE)
+
+/*
+ * Request INTEL_SIP_SMC_RSU_STATUS
+ *
+ * Sync call used by service driver at EL1 to query the RSU status
+ *
+ * Call register usage:
+ * a0 INTEL_SIP_SMC_RSU_STATUS
+ * a1-7 not used
+ *
+ * Return status
+ * a0: Current Image
+ * a1: Last Failing Image
+ * a2: Version | State
+ * a3: Error details | Error location
+ *
+ * Or
+ *
+ * a0: INTEL_SIP_SMC_RSU_ERROR
+ */
+#define INTEL_SIP_SMC_FUNCID_RSU_STATUS 11
+#define INTEL_SIP_SMC_RSU_STATUS \
+ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_RSU_STATUS)
+
+/*
+ * Request INTEL_SIP_SMC_RSU_UPDATE
+ *
+ * Sync call used by service driver at EL1 to tell you next reboot is RSU_UPDATE
+ *
+ * Call register usage:
+ * a0 INTEL_SIP_SMC_RSU_UPDATE
+ * a1 64bit physical address of the configuration data memory in flash
+ * a2-7 not used
+ *
+ * Return status
+ * a0 INTEL_SIP_SMC_STATUS_OK
+ */
+#define INTEL_SIP_SMC_FUNCID_RSU_UPDATE 12
+#define INTEL_SIP_SMC_RSU_UPDATE \
+ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_RSU_UPDATE)
+
+#endif
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index d9c368c44194..cf3038e2b839 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -1091,10 +1091,12 @@ static const struct flash_info spi_nor_ids[] = {
{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ "mx25u25635f", INFO(0xc22539, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_4B_OPCODES) },
{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
+ { "mx25u51245g", INFO(0xc2253a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
{ "mx66u51235f", INFO(0xc2253a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
{ "mx66l1g45g", INFO(0xc2201b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
+ { "mx66u2g45g", INFO(0xc2253c, 0, 64 * 1024, 4096, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
/* Micron */
{ "n25q016a", INFO(0x20bb15, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_QUAD_READ) },
diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig
index edf20361ea5f..a14085b1ad32 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
@@ -110,7 +110,7 @@ config DWMAC_ROCKCHIP
config DWMAC_SOCFPGA
tristate "SOCFPGA dwmac support"
- default ARCH_SOCFPGA
+ default (ARCH_SOCFPGA || ARCH_STRATIX10)
depends on OF && (ARCH_SOCFPGA || ARCH_STRATIX10 || COMPILE_TEST)
select MFD_SYSCON
help
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
index 5b3b06a0a3bf..e6f573535991 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
@@ -15,6 +15,9 @@
* Adopted from dwmac-sti.c
*/
+#if defined CONFIG_HAVE_ARM_SMCCC && defined CONFIG_ARCH_STRATIX10
+#include <linux/arm-smccc.h>
+#endif
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/of_address.h>
@@ -52,6 +55,9 @@ struct socfpga_dwmac {
int interface;
u32 reg_offset;
u32 reg_shift;
+#if defined CONFIG_HAVE_ARM_SMCCC && defined CONFIG_ARCH_STRATIX10
+ u32 sysmgr_reg;
+#endif
struct device *dev;
struct regmap *sys_mgr_base_addr;
struct reset_control *stmmac_rst;
@@ -61,6 +67,122 @@ struct socfpga_dwmac {
struct tse_pcs pcs;
};
+#if defined CONFIG_HAVE_ARM_SMCCC && defined CONFIG_ARCH_STRATIX10
+/* Functions specified by ARM SMC Calling convention:
+ *
+ * FAST call executes atomic operations, returns when the requested operation
+ * has completed.
+ * STD call starts a operation which can be preempted by a non-secure
+ * interrupt. The call can return before the requested operation has completed.
+ * a0..a7 is used as register names in the descriptions below, on arm32 that
+ * translates to r0..r7 and on arm64 to w0..w7.
+ */
+
+#define INTEL_SIP_SMC_STD_CALL_VAL(func_num) \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL, ARM_SMCCC_SMC_64, \
+ ARM_SMCCC_OWNER_SIP, (func_num))
+
+#define INTEL_SIP_SMC_FAST_CALL_VAL(func_num) \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64, \
+ ARM_SMCCC_OWNER_SIP, (func_num))
+
+#define INTEL_SIP_SMC_RETURN_UNKNOWN_FUNCTION 0xFFFFFFFF
+#define INTEL_SIP_SMC_STATUS_OK 0x0
+#define INTEL_SIP_SMC_REG_ERROR 0x5
+
+/* Request INTEL_SIP_SMC_REG_READ
+ *
+ * Read a protected register using SMCCC
+ *
+ * Call register usage:
+ * a0: INTEL_SIP_SMC_REG_READ.
+ * a1: register address.
+ * a2-7: not used.
+ *
+ * Return status:
+ * a0: INTEL_SIP_SMC_STATUS_OK, INTEL_SIP_SMC_REG_ERROR, or
+ * INTEL_SIP_SMC_RETURN_UNKNOWN_FUNCTION
+ * a1: Value in the register
+ * a2-3: not used.
+ */
+#define INTEL_SIP_SMC_FUNCID_REG_READ 7
+#define INTEL_SIP_SMC_REG_READ \
+ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_REG_READ)
+
+/* Request INTEL_SIP_SMC_REG_WRITE
+ *
+ * Write a protected register using SMCCC
+ *
+ * Call register usage:
+ * a0: INTEL_SIP_SMC_REG_WRITE.
+ * a1: register address
+ * a2: value to program into register.
+ * a3-7: not used.
+ *
+ * Return status:
+ * a0: INTEL_SIP_SMC_STATUS_OK, INTEL_SIP_SMC_REG_ERROR, or
+ * INTEL_SIP_SMC_RETURN_UNKNOWN_FUNCTION
+ * a1-3: not used.
+ */
+#define INTEL_SIP_SMC_FUNCID_REG_WRITE 8
+#define INTEL_SIP_SMC_REG_WRITE \
+ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_REG_WRITE)
+
+/**************** Stratix 10 EMAC Memory Controller Functions ************/
+
+/* s10_protected_reg_write
+ * Write to a protected SMC register.
+ * @context: Not used
+ * @reg: Address of register
+ * @value: Value to write
+ * Return: INTEL_SIP_SMC_STATUS_OK (0) on success
+ * INTEL_SIP_SMC_REG_ERROR on error
+ * INTEL_SIP_SMC_RETURN_UNKNOWN_FUNCTION if not supported
+ */
+static int s10_protected_reg_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct arm_smccc_res result;
+
+ arm_smccc_smc(INTEL_SIP_SMC_REG_WRITE, reg, val, 0, 0,
+ 0, 0, 0, &result);
+
+ return (int)result.a0;
+}
+
+/* s10_protected_reg_read
+ * Read the status of a protected SMC register
+ * @context: Not used
+ * @reg: Address of register
+ * @value: Value read.
+ * Return: INTEL_SIP_SMC_STATUS_OK (0) on success
+ * INTEL_SIP_SMC_REG_ERROR on error
+ * INTEL_SIP_SMC_RETURN_UNKNOWN_FUNCTION if not supported
+ */
+static int s10_protected_reg_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct arm_smccc_res result;
+
+ arm_smccc_smc(INTEL_SIP_SMC_REG_READ, reg, 0, 0, 0,
+ 0, 0, 0, &result);
+
+ *val = (unsigned int)result.a1;
+
+ return (int)result.a0;
+}
+
+static const struct regmap_config s10_emac_regmap_cfg = {
+ .name = "s10_emac",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .max_register = 0xffffffff,
+ .reg_read = s10_protected_reg_read,
+ .reg_write = s10_protected_reg_write,
+ .use_single_rw = true,
+};
+#endif
+
static void socfpga_dwmac_fix_mac_speed(void *priv, unsigned int speed)
{
struct socfpga_dwmac *dwmac = (struct socfpga_dwmac *)priv;
@@ -105,20 +227,43 @@ static int socfpga_dwmac_parse_data(struct socfpga_dwmac *dwmac, struct device *
struct device_node *np = dev->of_node;
struct regmap *sys_mgr_base_addr;
u32 reg_offset, reg_shift;
+#if defined CONFIG_HAVE_ARM_SMCCC && defined CONFIG_ARCH_STRATIX10
+ u32 sysmgr_reg = 0;
+#endif
int ret, index;
struct device_node *np_splitter = NULL;
struct device_node *np_sgmii_adapter = NULL;
+#if defined CONFIG_HAVE_ARM_SMCCC && defined CONFIG_ARCH_STRATIX10
+ struct device_node *np_sysmgr = NULL;
+#endif
struct resource res_splitter;
struct resource res_tse_pcs;
struct resource res_sgmii_adapter;
dwmac->interface = of_get_phy_mode(np);
+#if defined CONFIG_HAVE_ARM_SMCCC && defined CONFIG_ARCH_STRATIX10
+ sys_mgr_base_addr = devm_regmap_init(dev, NULL, (void *)dwmac,
+ &s10_emac_regmap_cfg);
+ if (IS_ERR(sys_mgr_base_addr))
+ return PTR_ERR(sys_mgr_base_addr);
+
+ np_sysmgr = of_parse_phandle(np, "altr,sysmgr-syscon", 0);
+ if (np_sysmgr) {
+ ret = of_property_read_u32_index(np_sysmgr, "reg", 0,
+ &sysmgr_reg);
+ if (ret) {
+ dev_info(dev, "Could not read sysmgr register address\n");
+ return -EINVAL;
+ }
+ }
+#else
sys_mgr_base_addr = syscon_regmap_lookup_by_phandle(np, "altr,sysmgr-syscon");
if (IS_ERR(sys_mgr_base_addr)) {
dev_info(dev, "No sysmgr-syscon node found\n");
return PTR_ERR(sys_mgr_base_addr);
}
+#endif
ret = of_property_read_u32_index(np, "altr,sysmgr-syscon", 1, &reg_offset);
if (ret) {
@@ -222,6 +367,9 @@ static int socfpga_dwmac_parse_data(struct socfpga_dwmac *dwmac, struct device *
dwmac->reg_offset = reg_offset;
dwmac->reg_shift = reg_shift;
dwmac->sys_mgr_base_addr = sys_mgr_base_addr;
+#if defined CONFIG_HAVE_ARM_SMCCC && defined CONFIG_ARCH_STRATIX10
+ dwmac->sysmgr_reg = sysmgr_reg;
+#endif
dwmac->dev = dev;
of_node_put(np_sgmii_adapter);
@@ -238,6 +386,9 @@ static int socfpga_dwmac_set_phy_mode(struct socfpga_dwmac *dwmac)
int phymode = dwmac->interface;
u32 reg_offset = dwmac->reg_offset;
u32 reg_shift = dwmac->reg_shift;
+#if defined CONFIG_HAVE_ARM_SMCCC && defined CONFIG_ARCH_STRATIX10
+ u32 sysmgr_reg = dwmac->sysmgr_reg;
+#endif
u32 ctrl, val, module;
switch (phymode) {
@@ -266,7 +417,11 @@ static int socfpga_dwmac_set_phy_mode(struct socfpga_dwmac *dwmac)
reset_control_assert(dwmac->stmmac_ocp_rst);
reset_control_assert(dwmac->stmmac_rst);
+#if defined CONFIG_HAVE_ARM_SMCCC && defined CONFIG_ARCH_STRATIX10
+ regmap_read(sys_mgr_base_addr, sysmgr_reg + reg_offset, &ctrl);
+#else
regmap_read(sys_mgr_base_addr, reg_offset, &ctrl);
+#endif
ctrl &= ~(SYSMGR_EMACGRP_CTRL_PHYSEL_MASK << reg_shift);
ctrl |= val << reg_shift;
@@ -284,7 +439,11 @@ static int socfpga_dwmac_set_phy_mode(struct socfpga_dwmac *dwmac)
ctrl &= ~(SYSMGR_EMACGRP_CTRL_PTP_REF_CLK_MASK << (reg_shift / 2));
}
+#if defined CONFIG_HAVE_ARM_SMCCC && defined CONFIG_ARCH_STRATIX10
+ regmap_write(sys_mgr_base_addr, sysmgr_reg + reg_offset, ctrl);
+#else
regmap_write(sys_mgr_base_addr, reg_offset, ctrl);
+#endif
/* Deassert reset for the phy configuration to be sampled by
* the enet controller, and operation to start in requested mode
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
index 7516ca210855..acba286fc0b7 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
@@ -34,7 +34,7 @@ int dwmac_dma_reset(void __iomem *ioaddr)
err = readl_poll_timeout(ioaddr + DMA_BUS_MODE, value,
!(value & DMA_BUS_MODE_SFT_RESET),
- 10000, 100000);
+ 0, 10000);
if (err)
return -EBUSY;
diff --git a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
index afed0f0f4027..c0c75c111abb 100644
--- a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
+++ b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
@@ -59,7 +59,7 @@ static int jumbo_frm(void *p, struct sk_buff *skb, int csum)
desc->des3 = cpu_to_le32(des2 + BUF_SIZE_4KiB);
stmmac_prepare_tx_desc(priv, desc, 1, bmax, csum,
- STMMAC_RING_MODE, 0, false, skb->len);
+ STMMAC_RING_MODE, 1, false, skb->len);
tx_q->tx_skbuff[entry] = NULL;
entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE);
@@ -91,7 +91,7 @@ static int jumbo_frm(void *p, struct sk_buff *skb, int csum)
tx_q->tx_skbuff_dma[entry].is_jumbo = true;
desc->des3 = cpu_to_le32(des2 + BUF_SIZE_4KiB);
stmmac_prepare_tx_desc(priv, desc, 1, nopaged_len, csum,
- STMMAC_RING_MODE, 0, true, skb->len);
+ STMMAC_RING_MODE, 1, true, skb->len);
}
tx_q->cur_tx = entry;
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 5dd303212e28..27b4376b0bb2 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -4460,6 +4460,7 @@ int stmmac_suspend(struct device *dev)
mutex_lock(&priv->lock);
+ netif_carrier_off(ndev);
netif_device_detach(ndev);
stmmac_stop_all_queues(priv);
@@ -4477,7 +4478,8 @@ int stmmac_suspend(struct device *dev)
pinctrl_pm_select_sleep_state(priv->device);
/* Disable clock in case of PWM is off */
clk_disable(priv->plat->pclk);
- clk_disable(priv->plat->stmmac_clk);
+ if (!of_machine_is_compatible("altr,socfpga-stratix10"))
+ clk_disable(priv->plat->stmmac_clk);
}
mutex_unlock(&priv->lock);
@@ -4528,6 +4530,9 @@ int stmmac_resume(struct device *dev)
if (!netif_running(ndev))
return 0;
+ if (ndev->phydev)
+ phy_start(ndev->phydev);
+
/* Power Down bit, into the PM register, is cleared
* automatically as soon as a magic packet or a Wake-up frame
* is received. Anyway, it's better to manually clear
@@ -4567,9 +4572,6 @@ int stmmac_resume(struct device *dev)
mutex_unlock(&priv->lock);
- if (ndev->phydev)
- phy_start(ndev->phydev);
-
return 0;
}
EXPORT_SYMBOL_GPL(stmmac_resume);
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index ad3fcad4d75b..db9b467fd039 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -100,6 +100,14 @@ config OF_OVERLAY
While this option is selected automatically when needed, you can
enable it manually to improve device tree unit test coverage.
+config OF_CONFIGFS
+ bool "Device Tree Overlay ConfigFS interface"
+ select CONFIGFS_FS
+ select OF_FLATTREE
+ depends on OF_OVERLAY
+ help
+ Enable a simple user-space driven DT overlay interface.
+
config OF_NUMA
bool
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index 663a4af0cccd..b00a95adf519 100644
--- 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
diff --git a/drivers/of/configfs.c b/drivers/of/configfs.c
new file mode 100644
index 000000000000..7a6cae074381
--- /dev/null
+++ b/drivers/of/configfs.c
@@ -0,0 +1,284 @@
+/*
+ * 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/sizes.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 "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, const void *blob,
+ size_t size)
+{
+ int err;
+
+ err = of_overlay_fdt_apply(blob, size, &overlay->ov_id);
+ if (err < 0)
+ pr_err("%s: Failed to create overlay (err=%d)\n", __func__,
+ 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)
+{
+ return sprintf(page, "%s\n", to_cfs_overlay_item(item)->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, overlay->fw->data, overlay->fw->size);
+ 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)
+{
+ return sprintf(page, "%s\n", to_cfs_overlay_item(item)->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,
+};
+
+static 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;
+}
+
+static 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, overlay->dtbo_size);
+ 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,
+};
+
+static 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);
diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
index e0a04bfc873e..8db904b73e4c 100644
--- a/drivers/tty/Kconfig
+++ b/drivers/tty/Kconfig
@@ -151,6 +151,25 @@ config LEGACY_PTY_COUNT
When not in use, each legacy PTY occupies 12 bytes on 32-bit
architectures and 24 bytes on 64-bit architectures.
+config BFIN_JTAG_COMM
+ tristate "Blackfin JTAG Communication"
+ depends on BLACKFIN
+ help
+ Add support for emulating a TTY device over the Blackfin JTAG.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bfin_jtag_comm.
+
+config BFIN_JTAG_COMM_CONSOLE
+ bool "Console on Blackfin JTAG"
+ depends on BFIN_JTAG_COMM=y
+
+config NEWHAVEN_LCD
+ tristate "NEWHAVEN LCD"
+ depends on I2C
+ help
+ Add support for a TTY device on a Newhaven I2C LCD device.
+
config SERIAL_NONSTANDARD
bool "Non-standard serial port support"
depends on HAS_IOMEM
diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile
index c72cafdf32b4..c48a9cac79d1 100644
--- a/drivers/tty/Makefile
+++ b/drivers/tty/Makefile
@@ -33,5 +33,6 @@ obj-$(CONFIG_PPC_EPAPR_HV_BYTECHAN) += ehv_bytechan.o
obj-$(CONFIG_GOLDFISH_TTY) += goldfish.o
obj-$(CONFIG_MIPS_EJTAG_FDC_TTY) += mips_ejtag_fdc.o
obj-$(CONFIG_VCC) += vcc.o
+obj-$(CONFIG_NEWHAVEN_LCD) += newhaven_lcd.o
obj-y += ipwireless/
diff --git a/drivers/tty/newhaven_lcd.c b/drivers/tty/newhaven_lcd.c
new file mode 100644
index 000000000000..9671fda15489
--- /dev/null
+++ b/drivers/tty/newhaven_lcd.c
@@ -0,0 +1,636 @@
+/*
+ * TTY on a LCD connected to I2C
+ * Supports Newhaven NHD-0216K3Z-NSW-BBW
+ *
+ * Copyright (C) 2013 Altera Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#define DRV_NAME "lcd-comm"
+#define DEV_NAME "ttyLCD"
+
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/delay.h>
+
+#define LCD_COMMAND 0xfe
+#define LCD_DISPLAY_ON 0x41
+#define LCD_DISPLAY_OFF 0x42
+#define LCD_SET_CURSOR 0x45
+#define LCD_BACKSPACE 0x4e
+#define LCD_CLEAR_SCREEN 0x51
+#define LCD_BRIGHTNESS 0x53
+#define LCD_CUSTOM_CHAR 0x54
+#define LCD_BYTES_PER_FONT 8
+#define LCD_BYTES_PER_FONT_CMD (LCD_BYTES_PER_FONT + 3)
+
+#define LCD_BRIGHTNESS_MIN 1
+#define LCD_BRIGHTNESS_MAX 8
+
+#define ASCII_BS 0x08
+#define ASCII_LF 0x0a
+#define ASCII_CR 0x0d
+#define ASCII_ESC 0x1b
+#define ASCII_SPACE 0x20
+#define ASCII_BACKSLASH 0x5c
+#define ASCII_TILDE 0x7e
+
+/* The NewHaven display has 8 custom characters that are user-loadable init
+ its cg ram. */
+#define CUSTOM_BACKSLASH 0x00
+#define CUSTOM_TILDE 0x01
+
+struct custom_font {
+ const char font_cmd[LCD_BYTES_PER_FONT_CMD];
+};
+
+/* Array of commands to send to set up custom fonts. */
+static struct custom_font custom_fonts[] = {
+ { { LCD_COMMAND, LCD_CUSTOM_CHAR, CUSTOM_BACKSLASH, 0x00, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00, 0x00, }, },
+ { { LCD_COMMAND, LCD_CUSTOM_CHAR, CUSTOM_TILDE, 0x00, 0x00, 0x00, 0x08, 0x15, 0x02, 0x00, 0x00, }, },
+};
+
+struct lcd {
+ struct device *dev;
+ struct i2c_client *client;
+ struct tty_driver *lcd_tty_driver;
+ struct tty_port port;
+ unsigned int width;
+ unsigned int height;
+ unsigned int brightness;
+ char *buffer;
+ unsigned int top_line;
+ unsigned int cursor_line;
+ unsigned int cursor_col;
+};
+
+#define MAX_LCDS 1
+static struct lcd lcd_data_static[MAX_LCDS];
+
+static int lcd_cmd_no_params(struct lcd *lcd_data, u8 cmd)
+{
+ int count;
+ u8 buf[2] = {LCD_COMMAND, cmd};
+
+ count = i2c_master_send(lcd_data->client, buf, sizeof(buf));
+ if (count != sizeof(buf)) {
+ pr_err("%s: i2c_master_send returns %d\n", __func__, count);
+ return -1;
+ }
+ msleep(1);
+ return 0;
+}
+
+static int lcd_cmd_one_param(struct lcd *lcd_data, u8 cmd, u8 param)
+{
+ int count;
+ u8 buf[3] = {LCD_COMMAND, cmd, param};
+
+ count = i2c_master_send(lcd_data->client, buf, sizeof(buf));
+ if (count != sizeof(buf)) {
+ pr_err("%s: i2c_master_send returns %d\n", __func__, count);
+ return -1;
+ }
+ msleep(1);
+ return 0;
+}
+
+static int lcd_cmd_backlight_brightness(struct lcd *lcd_data, u8 brightness)
+{
+ return lcd_cmd_one_param(lcd_data, LCD_BRIGHTNESS, brightness);
+}
+
+static int lcd_cmd_display_on(struct lcd *lcd_data)
+{
+ return lcd_cmd_no_params(lcd_data, LCD_DISPLAY_ON);
+}
+
+static int lcd_cmd_display_off(struct lcd *lcd_data)
+{
+ return lcd_cmd_no_params(lcd_data, LCD_DISPLAY_OFF);
+}
+
+static int lcd_cmd_clear_screen(struct lcd *lcd_data)
+{
+ return lcd_cmd_no_params(lcd_data, LCD_CLEAR_SCREEN);
+}
+
+static int lcd_cmd_backspace(struct lcd *lcd_data)
+{
+ return lcd_cmd_no_params(lcd_data, LCD_BACKSPACE);
+}
+
+/* Note that this has to happen early on or the LCD module will not
+ process the command */
+static int lcd_load_custom_fonts(struct lcd *lcd_data)
+{
+ int count, i;
+
+ for (i = 0; i < ARRAY_SIZE(custom_fonts); i++) {
+ count = i2c_master_send(lcd_data->client,
+ (const char *)&custom_fonts[i].font_cmd,
+ LCD_BYTES_PER_FONT_CMD);
+ if (count != LCD_BYTES_PER_FONT_CMD) {
+ pr_err("%s: i2c_master_send returns %d\n", __func__, count);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static char lcd_translate_printable_char(char val)
+{
+ if (val == ASCII_BACKSLASH)
+ return CUSTOM_BACKSLASH;
+ else if (val == ASCII_TILDE)
+ return CUSTOM_TILDE;
+
+ return val;
+}
+
+/* From NHD-0216K3Z-NSW-BBY Display Module datasheet. */
+#define LCD_CURSOR_LINE_MULTIPLIER 0x40
+
+static int lcd_cmd_set_cursor(struct lcd *lcd_data, u8 line, u8 col)
+{
+ u8 cursor;
+
+ BUG_ON((line >= lcd_data->height) || (col >= lcd_data->width));
+
+ cursor = col + (LCD_CURSOR_LINE_MULTIPLIER * line);
+ return lcd_cmd_one_param(lcd_data, LCD_SET_CURSOR, cursor);
+}
+
+/*
+ * Map a line on the lcd display to a line on the buffer.
+ * Note that the top line on the display (line 0) may not be line 0 on the
+ * buffer due to scrolling.
+ */
+static unsigned int lcd_line_to_buf_line(struct lcd *lcd_data,
+ unsigned int line)
+{
+ unsigned int buf_line;
+
+ buf_line = line + lcd_data->top_line;
+
+ if (buf_line >= lcd_data->height)
+ buf_line -= lcd_data->height;
+
+ return buf_line;
+}
+
+/* Returns a pointer to the line, column position in the lcd buffer */
+static char *lcd_buf_pointer(struct lcd *lcd_data, unsigned int line,
+ unsigned int col)
+{
+ unsigned int buf_line;
+ char *buf;
+
+ if ((lcd_data->cursor_line >= lcd_data->height) ||
+ (lcd_data->cursor_col >= lcd_data->width))
+ return lcd_data->buffer;
+
+ buf_line = lcd_line_to_buf_line(lcd_data, line);
+
+ buf = lcd_data->buffer + (buf_line * lcd_data->width) + col;
+
+ return buf;
+}
+
+static void lcd_clear_buffer_line(struct lcd *lcd_data, int line)
+{
+ char *buf;
+
+ BUG_ON(line >= lcd_data->height);
+
+ buf = lcd_buf_pointer(lcd_data, line, 0);
+ memset(buf, ASCII_SPACE, lcd_data->width);
+}
+
+static void lcd_clear_buffer(struct lcd *lcd_data)
+{
+ memset(lcd_data->buffer, ASCII_SPACE,
+ lcd_data->width * lcd_data->height);
+ lcd_data->cursor_line = 0;
+ lcd_data->cursor_col = 0;
+ lcd_data->top_line = 0;
+}
+
+static void lcd_reprint_one_line(struct lcd *lcd_data, u8 line)
+{
+ char *buf = lcd_buf_pointer(lcd_data, line, 0);
+
+ lcd_cmd_set_cursor(lcd_data, line, 0);
+ i2c_master_send(lcd_data->client, buf, lcd_data->width);
+}
+
+static void lcd_print_top_n_lines(struct lcd *lcd_data, u8 lines)
+{
+ unsigned int disp_line = 0;
+
+ while (disp_line < lines)
+ lcd_reprint_one_line(lcd_data, disp_line++);
+}
+
+static void lcd_add_char_at_cursor(struct lcd *lcd_data, char val)
+{
+ char *buf;
+
+ buf = lcd_buf_pointer(lcd_data, lcd_data->cursor_line,
+ lcd_data->cursor_col);
+
+ *buf = val;
+
+ if (lcd_data->cursor_col < (lcd_data->width - 1))
+ lcd_data->cursor_col++;
+}
+
+static void lcd_crlf(struct lcd *lcd_data)
+{
+ if (lcd_data->cursor_line < (lcd_data->height - 1)) {
+ /* Next line is blank, carriage return to beginning of line. */
+ lcd_data->cursor_line++;
+ if (lcd_data->cursor_line >= lcd_data->height)
+ lcd_data->cursor_line = 0;
+
+ } else {
+ /* Display is full. Scroll up one line. */
+ lcd_data->top_line++;
+ if (lcd_data->top_line >= lcd_data->height)
+ lcd_data->top_line = 0;
+
+ lcd_cmd_clear_screen(lcd_data);
+ lcd_clear_buffer_line(lcd_data, lcd_data->cursor_line);
+ lcd_print_top_n_lines(lcd_data, lcd_data->height);
+ }
+
+ lcd_cmd_set_cursor(lcd_data, lcd_data->height - 1, 0);
+ lcd_data->cursor_col = 0;
+}
+
+static void lcd_backspace(struct lcd *lcd_data)
+{
+ if (lcd_data->cursor_col > 0) {
+ lcd_cmd_backspace(lcd_data);
+ lcd_data->cursor_col--;
+ }
+}
+
+static int lcd_write(struct tty_struct *tty, const unsigned char *buf,
+ int count)
+{
+ struct lcd *lcd_data = tty->driver_data;
+ int buf_i = 0, left;
+ char val;
+
+#ifdef DEBUG
+ char *dbgbuf = kzalloc(count + 1, GFP_KERNEL);
+ strncpy(dbgbuf, buf, count);
+ pr_debug("\n%s: count=%d buf[0]=%02x --->%s<---\n", __func__, count,
+ buf[0], dbgbuf);
+#endif /* DEBUG */
+
+ if (count == 0) {
+#ifdef DEBUG
+ kfree(dbgbuf);
+#endif /* DEBUG */
+ return 0;
+ }
+
+ while (buf_i < count) {
+ left = count - buf_i;
+
+ /* process displayable chars */
+ if ((0x20 <= buf[buf_i]) && (buf[buf_i] <= 0x7f)) {
+ while ((buf_i < count) &&
+ ((0x20 <= buf[buf_i]) && (buf[buf_i] <= 0x7f))) {
+ val = lcd_translate_printable_char(buf[buf_i]);
+ lcd_add_char_at_cursor(lcd_data, val);
+ buf_i++;
+ }
+
+ /* flush the line out to the display when we get to eol */
+ lcd_reprint_one_line(lcd_data, lcd_data->cursor_line);
+
+ /*
+ * ECMA-48 CSI sequences (from console_codes man page)
+ *
+ * ESC [ 2 J : erase whole display.
+ * ESC [ 2 K : erase whole line.
+ */
+ } else if (buf[buf_i] == ASCII_ESC) {
+ if ((left >= 4) &&
+ (buf[buf_i + 1] == '[') &&
+ (buf[buf_i + 2] == '2') &&
+ (buf[buf_i + 3] == 'J')) {
+ pr_debug("ESC [2J = clear screan\n");
+ lcd_clear_buffer(lcd_data);
+ lcd_cmd_clear_screen(lcd_data);
+ buf_i += 4;
+
+ } else if ((left >= 4) &&
+ (buf[buf_i + 1] == '[') &&
+ (buf[buf_i + 2] == '2') &&
+ (buf[buf_i + 3] == 'K')) {
+ pr_debug("ESC [2K = clear line\n");
+ lcd_clear_buffer_line(lcd_data, lcd_data->cursor_line);
+ lcd_reprint_one_line(lcd_data, lcd_data->cursor_line);
+ lcd_cmd_set_cursor(lcd_data, lcd_data->cursor_line, 0);
+ lcd_data->cursor_col = 0;
+ buf_i += 4;
+
+ } else {
+ pr_debug("Unsupported escape sequence\n");
+ buf_i++;
+ }
+
+ } else if ((left >= 2) &&
+ (buf[buf_i] == ASCII_CR) && (buf[buf_i + 1] == ASCII_LF)) {
+ pr_debug("ASCII_CR/LF\n");
+ lcd_crlf(lcd_data);
+ buf_i += 2;
+
+ } else if ((left >= 1) && (buf[buf_i] == ASCII_CR)) {
+ pr_debug("ASCII_CR\n");
+ lcd_crlf(lcd_data);
+ buf_i++;
+
+ } else if ((left >= 1) && (buf[buf_i] == ASCII_LF)) {
+ pr_debug("ASCII_LF\n");
+ lcd_crlf(lcd_data);
+ buf_i++;
+
+ } else if ((left >= 1) && (buf[buf_i] == ASCII_BS)) {
+ pr_debug("ASCII_BS\n");
+ lcd_backspace(lcd_data);
+ buf_i++;
+
+ } else {
+ pr_debug("%s - Unsupported command 0x%02x\n", __func__, buf[buf_i]);
+ buf_i++;
+ }
+ }
+
+#ifdef DEBUG
+ kfree(dbgbuf);
+#endif /* DEBUG */
+ return count;
+}
+
+static ssize_t brightness_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct lcd *lcd_data = dev_get_drvdata(dev);
+
+ return scnprintf(buf, 2, "%d\n", lcd_data->brightness);
+}
+
+static ssize_t brightness_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct lcd *lcd_data = dev_get_drvdata(dev);
+ int ret, brightness;
+
+ ret = sscanf(buf, "%d", &brightness);
+ if (ret != 1)
+ return -EINVAL;
+
+ if ((brightness < LCD_BRIGHTNESS_MIN) ||
+ (brightness > LCD_BRIGHTNESS_MAX)) {
+ dev_err(lcd_data->dev, "out of range (%d to %d)\n",
+ LCD_BRIGHTNESS_MIN, LCD_BRIGHTNESS_MAX);
+ return -EINVAL;
+ }
+
+ lcd_data->brightness = brightness;
+ lcd_cmd_backlight_brightness(lcd_data, brightness);
+
+ return count;
+}
+static DEVICE_ATTR(brightness, S_IRUGO | S_IWUSR, brightness_show, brightness_store);
+
+static struct attribute *lcd_attrs[] = {
+ &dev_attr_brightness.attr,
+ NULL,
+};
+
+static struct attribute_group lcd_attr_group = {
+ .attrs = lcd_attrs,
+};
+
+static int lcd_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+ struct lcd *lcd_data;
+
+ lcd_data = &lcd_data_static[tty->index];
+ if (lcd_data == NULL)
+ return -ENODEV;
+
+ tty->driver_data = lcd_data;
+
+ return tty_port_install(&lcd_data->port, driver, tty);
+}
+
+static int lcd_open(struct tty_struct *tty, struct file *filp)
+{
+ struct lcd *lcd_data = tty->driver_data;
+ unsigned long flags;
+
+ tty->driver_data = lcd_data;
+ spin_lock_irqsave(&lcd_data->port.lock, flags);
+ lcd_data->port.count++;
+ spin_unlock_irqrestore(&lcd_data->port.lock, flags);
+ tty_port_tty_set(&lcd_data->port, tty);
+
+ return 0;
+}
+
+static void lcd_close(struct tty_struct *tty, struct file *filp)
+{
+ struct lcd *lcd_data = tty->driver_data;
+ unsigned long flags;
+ bool last;
+
+ spin_lock_irqsave(&lcd_data->port.lock, flags);
+ --lcd_data->port.count;
+ last = (lcd_data->port.count == 0);
+ spin_unlock_irqrestore(&lcd_data->port.lock, flags);
+ if (last)
+ tty_port_tty_set(&lcd_data->port, NULL);
+}
+
+static int lcd_write_room(struct tty_struct *tty)
+{
+ struct lcd *lcd_data = tty->driver_data;
+
+ return lcd_data->height * lcd_data->width;
+}
+
+static const struct tty_operations lcd_ops = {
+ .install = lcd_install,
+ .open = lcd_open,
+ .close = lcd_close,
+ .write = lcd_write,
+ .write_room = lcd_write_room,
+};
+
+static int lcd_probe(struct i2c_client *client,
+ const struct i2c_device_id *i2c_id)
+{
+ struct device_node *np = client->dev.of_node;
+ struct lcd *lcd_data;
+ struct tty_driver *lcd_tty_driver;
+ unsigned int width = 0, height = 0, i, brightness = 0;
+ char *buffer;
+ int ret = -ENOMEM;
+
+ of_property_read_u32(np, "height", &height);
+ of_property_read_u32(np, "width", &width);
+ if ((width == 0) || (height == 0)) {
+ dev_err(&client->dev,
+ "Need to specify lcd width/height in device tree\n");
+ ret = -EINVAL;
+ goto err_devtree;
+ }
+
+ of_property_read_u32(np, "brightness", &brightness);
+ if ((brightness < LCD_BRIGHTNESS_MIN) ||
+ (brightness > LCD_BRIGHTNESS_MAX)) {
+ dev_info(&client->dev,
+ "lcd brighness not set or out of range, defaulting to maximum\n");
+ brightness = LCD_BRIGHTNESS_MAX;
+ }
+
+ for (i = 0 ; i < MAX_LCDS ; i++)
+ if (lcd_data_static[i].client == NULL)
+ break;
+ if (i >= MAX_LCDS) {
+ ret = -ENODEV;
+ dev_warn(&client->dev,
+ "More than %d I2C LCD displays found. Giving up.\n",
+ MAX_LCDS);
+ goto err_devtree;
+ }
+ lcd_data = &lcd_data_static[i];
+
+ buffer = kzalloc(height * width, GFP_KERNEL);
+ if (!buffer)
+ goto err_devtree;
+
+ i2c_set_clientdata(client, lcd_data);
+
+ lcd_data->client = client;
+ lcd_data->dev = &client->dev;
+ lcd_data->height = height;
+ lcd_data->width = width;
+ lcd_data->buffer = buffer;
+ lcd_data->brightness = brightness;
+
+ dev_set_drvdata(&client->dev, lcd_data);
+ tty_port_init(&lcd_data->port);
+ lcd_tty_driver = alloc_tty_driver(MAX_LCDS);
+ if (!lcd_tty_driver)
+ goto err_driver;
+
+ lcd_tty_driver->driver_name = DRV_NAME;
+ lcd_tty_driver->name = DEV_NAME;
+ lcd_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ lcd_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+ lcd_tty_driver->init_termios = tty_std_termios;
+ tty_set_operations(lcd_tty_driver, &lcd_ops);
+
+ ret = tty_register_driver(lcd_tty_driver);
+ if (ret)
+ goto err_register;
+
+ lcd_data->lcd_tty_driver = lcd_tty_driver;
+
+ lcd_clear_buffer(lcd_data);
+ lcd_load_custom_fonts(lcd_data);
+ lcd_cmd_display_on(lcd_data);
+ lcd_cmd_backlight_brightness(lcd_data, brightness);
+ lcd_cmd_clear_screen(lcd_data);
+
+ ret = sysfs_create_group(&lcd_data->dev->kobj, &lcd_attr_group);
+ if (ret) {
+ dev_err(lcd_data->dev, "Can't create sysfs attrs for lcd\n");
+ return ret;
+ }
+
+ dev_info(&client->dev, "LCD driver initialized\n");
+
+ return 0;
+
+err_register:
+ put_tty_driver(lcd_data->lcd_tty_driver);
+err_driver:
+ kfree(buffer);
+err_devtree:
+ return ret;
+}
+
+static int __exit lcd_remove(struct i2c_client *client)
+{
+ struct lcd *lcd_data = i2c_get_clientdata(client);
+
+ lcd_cmd_display_off(lcd_data);
+
+ sysfs_remove_group(&lcd_data->dev->kobj, &lcd_attr_group);
+ tty_unregister_driver(lcd_data->lcd_tty_driver);
+ put_tty_driver(lcd_data->lcd_tty_driver);
+ kfree(lcd_data->buffer);
+
+ return 0;
+}
+
+static const struct of_device_id lcd_of_match[] = {
+ { .compatible = "newhaven,nhd-0216k3z-nsw-bbw", },
+ {},
+};
+
+static const struct i2c_device_id lcd_id[] = {
+ { DRV_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lcd_id);
+
+static struct i2c_driver lcd_i2c_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = lcd_of_match,
+ },
+ .probe = lcd_probe,
+ .remove = lcd_remove,
+ .id_table = lcd_id,
+};
+
+static int __init lcd_init(void)
+{
+ return i2c_add_driver(&lcd_i2c_driver);
+}
+subsys_initcall(lcd_init);
+
+static void __exit lcd_exit(void)
+{
+ i2c_del_driver(&lcd_i2c_driver);
+}
+module_exit(lcd_exit);
+
+MODULE_DESCRIPTION("LCD 2x16");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 83d3d271ca15..5bd01054efe6 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -35,6 +35,15 @@ config HDMI
bool
endif # HAS_IOMEM
+config FB_ALTERA_VIP
+ tristate "Altera VIP Frame Reader framebuffer support"
+ depends on FB
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ help
+ This driver supports the Altera Video and Image Processing(VIP)
+ Frame Reader
if VT
source "drivers/video/console/Kconfig"
diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
index 591a13a59787..d480b7aed00f 100644
--- a/drivers/video/fbdev/Kconfig
+++ b/drivers/video/fbdev/Kconfig
@@ -236,6 +236,16 @@ config FB_TILEBLITTING
comment "Frame buffer hardware drivers"
depends on FB
+config FB_ALTERA_VIP
+ tristate "Altera VIP Frame Reader framebuffer support"
+ depends on FB
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ help
+ This driver supports the Altera Video and Image Processing(VIP)
+ Frame Reader
+
config FB_GRVGA
tristate "Aeroflex Gaisler framebuffer support"
depends on FB && SPARC
diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile
index 13c900320c2c..49ca14827e2c 100644
--- a/drivers/video/fbdev/Makefile
+++ b/drivers/video/fbdev/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_FB_MACMODES) += macmodes.o
obj-$(CONFIG_FB_WMT_GE_ROPS) += wmt_ge_rops.o
# Hardware specific drivers go first
+obj-$(CONFIG_FB_ALTERA_VIP) += altvipfb.o
obj-$(CONFIG_FB_AMIGA) += amifb.o c2p_planar.o
obj-$(CONFIG_FB_ARC) += arcfb.o
obj-$(CONFIG_FB_CLPS711X) += clps711x-fb.o
diff --git a/drivers/video/fbdev/altvipfb.c b/drivers/video/fbdev/altvipfb.c
new file mode 100644
index 000000000000..b247858ba43a
--- /dev/null
+++ b/drivers/video/fbdev/altvipfb.c
@@ -0,0 +1,303 @@
+/*
+ * altvipfb.c -- Altera Video and Image Processing(VIP) Frame Reader driver
+ *
+ * This is based on a driver made by Thomas Chou <thomas@wytron.com.tw> and
+ * Walter Goossens <waltergoossens@home.nl> This driver supports the Altera VIP
+ * Frame Reader component. More info on the hardware can be found in
+ * the Altera Video and Image Processing Suite User Guide at this address
+ * http://www.altera.com/literature/ug/ug_vip.pdf.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/dma-mapping.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#define PALETTE_SIZE 256
+#define DRIVER_NAME "altvipfb"
+
+/* control registers */
+#define ALTVIPFB_CONTROL 0
+#define ALTVIPFB_FRAME_SELECT 12
+#define ALTVIPFB_FRAME0_BASE_ADDRESS 16
+#define ALTVIPFB_FRAME0_NUM_WORDS 20
+#define ALTVIPFB_FRAME0_SAMPLES 24
+#define ALTVIPFB_FRAME0_WIDTH 32
+#define ALTVIPFB_FRAME0_HEIGHT 36
+#define ALTVIPFB_FRAME0_INTERLACED 40
+
+struct altvipfb_type;
+
+struct altvipfb_dev {
+ struct platform_device *pdev;
+ struct fb_info info;
+ struct resource *reg_res;
+ void __iomem *base;
+ int mem_word_width;
+ u32 pseudo_palette[PALETTE_SIZE];
+};
+
+static int altvipfb_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp, struct fb_info *info)
+{
+ /*
+ * Set a single color register. The values supplied have a 32 bit
+ * magnitude.
+ * Return != 0 for invalid regno.
+ */
+
+ if (regno > 255)
+ return 1;
+
+ red >>= 8;
+ green >>= 8;
+ blue >>= 8;
+
+ if (regno < 255) {
+ ((u32 *)info->pseudo_palette)[regno] =
+ ((red & 255) << 16) | ((green & 255) << 8) | (blue & 255);
+ }
+
+ return 0;
+}
+
+static struct fb_ops altvipfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_setcolreg = altvipfb_setcolreg,
+};
+
+static int altvipfb_of_setup(struct altvipfb_dev *fbdev)
+{
+ struct device_node *np = fbdev->pdev->dev.of_node;
+ int ret;
+ u32 bits_per_color;
+
+ ret = of_property_read_u32(np, "max-width", &fbdev->info.var.xres);
+ if (ret) {
+ dev_err(&fbdev->pdev->dev,
+ "Missing required parameter 'max-width'");
+ return ret;
+ }
+ fbdev->info.var.xres_virtual = fbdev->info.var.xres,
+
+ ret = of_property_read_u32(np, "max-height", &fbdev->info.var.yres);
+ if (ret) {
+ dev_err(&fbdev->pdev->dev,
+ "Missing required parameter 'max-height'");
+ return ret;
+ }
+ fbdev->info.var.yres_virtual = fbdev->info.var.yres;
+
+ ret = of_property_read_u32(np, "bits-per-color", &bits_per_color);
+ if (ret) {
+ dev_err(&fbdev->pdev->dev,
+ "Missing required parameter 'bits-per-color'");
+ return ret;
+ }
+ if (bits_per_color != 8) {
+ dev_err(&fbdev->pdev->dev,
+ "bits-per-color is set to %i. Curently only 8 is supported.",
+ bits_per_color);
+ return -ENODEV;
+ }
+ fbdev->info.var.bits_per_pixel = 32;
+
+ ret = of_property_read_u32(np, "mem-word-width",
+ &fbdev->mem_word_width);
+ if (ret) {
+ dev_err(&fbdev->pdev->dev,
+ "Missing required parameter 'mem-word-width'");
+ return ret;
+ }
+ if (!(fbdev->mem_word_width >= 32 && fbdev->mem_word_width % 32 == 0)) {
+ dev_err(&fbdev->pdev->dev,
+ "mem-word-width is set to %i. must be >= 32 and multiple of 32.",
+ fbdev->mem_word_width);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void altvipfb_start_hw(struct altvipfb_dev *fbdev)
+{
+ writel(fbdev->info.fix.smem_start, fbdev->base +
+ ALTVIPFB_FRAME0_BASE_ADDRESS);
+ writel(fbdev->info.var.xres * fbdev->info.var.yres /
+ (fbdev->mem_word_width/32),
+ fbdev->base + ALTVIPFB_FRAME0_NUM_WORDS);
+ writel(fbdev->info.var.xres * fbdev->info.var.yres,
+ fbdev->base + ALTVIPFB_FRAME0_SAMPLES);
+ writel(fbdev->info.var.xres, fbdev->base + ALTVIPFB_FRAME0_WIDTH);
+ writel(fbdev->info.var.yres, fbdev->base + ALTVIPFB_FRAME0_HEIGHT);
+ writel(3, fbdev->base + ALTVIPFB_FRAME0_INTERLACED);
+ writel(0, fbdev->base + ALTVIPFB_FRAME_SELECT);
+
+ /* Finally set the control register to 1 to start streaming */
+ writel(1, fbdev->base + ALTVIPFB_CONTROL);
+}
+
+static void altvipfb_disable_hw(struct altvipfb_dev *fbdev)
+{
+ /* set the control register to 0 to stop streaming */
+ writel(0, fbdev->base + ALTVIPFB_CONTROL);
+}
+
+
+static int altvipfb_setup_fb_info(struct altvipfb_dev *fbdev)
+{
+ struct fb_info *info = &fbdev->info;
+ int ret;
+
+ strcpy(info->fix.id, DRIVER_NAME);
+ info->fix.type = FB_TYPE_PACKED_PIXELS;
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ info->fix.accel = FB_ACCEL_NONE;
+
+ info->fbops = &altvipfb_ops;
+ info->var.activate = FB_ACTIVATE_NOW;
+ info->var.height = -1;
+ info->var.width = -1;
+ info->var.vmode = FB_VMODE_NONINTERLACED;
+
+ ret = altvipfb_of_setup(fbdev);
+ if (ret)
+ return ret;
+
+ /* settings for 32bit pixels */
+ info->var.red.offset = 16;
+ info->var.red.length = 8;
+ info->var.red.msb_right = 0;
+ info->var.green.offset = 8;
+ info->var.green.length = 8;
+ info->var.green.msb_right = 0;
+ info->var.blue.offset = 0;
+ info->var.blue.length = 8;
+ info->var.blue.msb_right = 0;
+
+ info->fix.line_length = (info->var.xres *
+ (info->var.bits_per_pixel >> 3));
+ info->fix.smem_len = info->fix.line_length * info->var.yres;
+
+ info->pseudo_palette = fbdev->pseudo_palette;
+ info->flags = FBINFO_FLAG_DEFAULT;
+
+ return 0;
+}
+
+static int altvipfb_probe(struct platform_device *pdev)
+{
+ int retval;
+ void *fbmem_virt;
+ struct altvipfb_dev *fbdev;
+
+ fbdev = devm_kzalloc(&pdev->dev, sizeof(*fbdev), GFP_KERNEL);
+ if (!fbdev)
+ return -ENOMEM;
+
+ fbdev->pdev = pdev;
+ fbdev->reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!fbdev->reg_res)
+ return -ENODEV;
+
+ retval = altvipfb_setup_fb_info(fbdev);
+
+ fbmem_virt = dma_alloc_coherent(NULL,
+ fbdev->info.fix.smem_len,
+ (void *)&(fbdev->info.fix.smem_start),
+ GFP_KERNEL);
+ if (!fbmem_virt) {
+ dev_err(&pdev->dev,
+ "altvipfb: unable to allocate %d Bytes fb memory\n",
+ fbdev->info.fix.smem_len);
+ return retval;
+ }
+
+ fbdev->info.screen_base = fbmem_virt;
+
+ retval = fb_alloc_cmap(&fbdev->info.cmap, PALETTE_SIZE, 0);
+ if (retval < 0)
+ goto err_dma_free;
+
+ platform_set_drvdata(pdev, fbdev);
+
+ fbdev->base = devm_ioremap_resource(&pdev->dev, fbdev->reg_res);
+ if (IS_ERR(fbdev->base)) {
+ dev_err(&pdev->dev, "devm_ioremap_resource failed\n");
+ retval = PTR_ERR(fbdev->base);
+ goto err_dealloc_cmap;
+ }
+
+ altvipfb_start_hw(fbdev);
+
+ retval = register_framebuffer(&fbdev->info);
+ if (retval < 0)
+ goto err_dealloc_cmap;
+
+ dev_info(&pdev->dev, "fb%d: %s frame buffer device at 0x%x+0x%x\n",
+ fbdev->info.node, fbdev->info.fix.id,
+ (unsigned)fbdev->info.fix.smem_start,
+ fbdev->info.fix.smem_len);
+
+ return 0;
+
+err_dealloc_cmap:
+ fb_dealloc_cmap(&fbdev->info.cmap);
+err_dma_free:
+ dma_free_coherent(NULL, fbdev->info.fix.smem_len, fbmem_virt,
+ fbdev->info.fix.smem_start);
+ return retval;
+}
+
+static int altvipfb_remove(struct platform_device *dev)
+{
+ struct altvipfb_dev *fbdev = platform_get_drvdata(dev);
+
+ if (fbdev) {
+ unregister_framebuffer(&fbdev->info);
+ fb_dealloc_cmap(&fbdev->info.cmap);
+ dma_free_coherent(NULL, fbdev->info.fix.smem_len,
+ fbdev->info.screen_base,
+ fbdev->info.fix.smem_start);
+ altvipfb_disable_hw(fbdev);
+ }
+ return 0;
+}
+
+
+static struct of_device_id altvipfb_match[] = {
+ { .compatible = "altr,vip-frame-reader-1.0" },
+ { .compatible = "altr,vip-frame-reader-9.1" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, altvipfb_match);
+
+static struct platform_driver altvipfb_driver = {
+ .probe = altvipfb_probe,
+ .remove = altvipfb_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRIVER_NAME,
+ .of_match_table = altvipfb_match,
+ },
+};
+module_platform_driver(altvipfb_driver);
+
+MODULE_DESCRIPTION("Altera VIP Frame Reader framebuffer driver");
+MODULE_AUTHOR("Chris Rauer <crauer@altera.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/altera_hwmutex.h b/include/linux/altera_hwmutex.h
new file mode 100644
index 000000000000..166502b379f6
--- /dev/null
+++ b/include/linux/altera_hwmutex.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright Altera Corporation (C) 2013. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _ALTERA_MUTEX_H
+#define _ALTERA_MUTEX_H
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+
+struct altera_mutex {
+ struct list_head list;
+ struct platform_device *pdev;
+ struct mutex lock;
+ void __iomem *regs;
+ bool requested;
+};
+
+extern struct altera_mutex *altera_mutex_request(struct device_node *mutex_np);
+extern int altera_mutex_free(struct altera_mutex *mutex);
+
+extern int altera_mutex_lock(struct altera_mutex *mutex, u16 owner, u16 value);
+
+extern int altera_mutex_trylock(struct altera_mutex *mutex, u16 owner,
+ u16 value);
+extern int altera_mutex_unlock(struct altera_mutex *mutex, u16 owner);
+extern int altera_mutex_owned(struct altera_mutex *mutex, u16 owner);
+extern int altera_mutex_is_locked(struct altera_mutex *mutex);
+
+#endif /* _ALTERA_MUTEX_H */
diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
index eec7c2478b0d..244f5f5301d0 100644
--- a/include/linux/fpga/fpga-mgr.h
+++ b/include/linux/fpga/fpga-mgr.h
@@ -140,6 +140,9 @@ struct fpga_manager {
enum fpga_mgr_states state;
const struct fpga_manager_ops *mops;
void *priv;
+#if IS_ENABLED(CONFIG_FPGA_MGR_DEBUG_FS)
+ void *debugfs;
+#endif
};
#define to_fpga_manager(d) container_of(d, struct fpga_manager, dev)
diff --git a/include/linux/intel-service-client.h b/include/linux/intel-service-client.h
new file mode 100644
index 000000000000..88f0d9f298a6
--- /dev/null
+++ b/include/linux/intel-service-client.h
@@ -0,0 +1,198 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2017-2018, Intel Corporation
+ */
+
+#ifndef __INTEL_SERVICE_CLIENT_H
+#define __INTEL_SERVICE_CLIENT_H
+
+/*
+ * Service layer driver supports client names
+ * @fpga: for FPGA configuration
+ * @rsu: for remote status update
+ */
+#define SVC_CLIENT_FPGA "fpga"
+#define SVC_CLIENT_RSU "rsu"
+
+/*
+ * Status of the sent command, in bit number
+ * @SVC_COMMAND_STATUS_RECONFIG_REQUEST_OK:
+ * Secure firmware accepts the request of FPGA reconfiguration.
+ * @SVC_STATUS_RECONFIG_BUFFER_SUBMITTED:
+ * Service client successfully submits FPGA configuration
+ * data buffer to secure firmware.
+ * @SVC_COMMAND_STATUS_RECONFIG_BUFFER_DONE:
+ * Secure firmware completes data process, ready to accept the
+ * next WRITE transaction.
+ * @SVC_COMMAND_STATUS_RECONFIG_COMPLETED:
+ * Secure firmware completes FPGA configuration successfully, FPGA should
+ * be in user mode.
+ * @SVC_COMMAND_STATUS_RECONFIG_BUSY:
+ * FPGA configuration is still in process.
+ * @SVC_COMMAND_STATUS_RECONFIG_ERROR:
+ * Error encountered during FPGA configuration.
+ */
+#define SVC_STATUS_RECONFIG_REQUEST_OK 0
+#define SVC_STATUS_RECONFIG_BUFFER_SUBMITTED 1
+#define SVC_STATUS_RECONFIG_BUFFER_DONE 2
+#define SVC_STATUS_RECONFIG_COMPLETED 3
+#define SVC_STATUS_RECONFIG_BUSY 4
+#define SVC_STATUS_RECONFIG_ERROR 5
+#define SVC_STATUS_RSU_OK 6
+
+/*
+ * Flag bit for COMMAND_RECONFIG
+ * @COMMAND_RECONFIG_FLAG_PARTIAL
+ * Set to FPGA configuration type (full or partial), the default
+ * is full reconfig.
+ */
+#define COMMAND_RECONFIG_FLAG_PARTIAL 0
+
+/* Timeout settings for FPGA manager driver */
+#define SVC_RECONFIG_REQUEST_TIMEOUT_MS 100
+#define SVC_RECONFIG_BUFFER_TIMEOUT_MS 240
+
+/* Timeout settings for RSU driver */
+#define SVC_RSU_REQUEST_TIMEOUT_MS 300
+
+struct intel_svc_chan;
+
+/**
+ * enum intel_svc_command_code - supporting service commands
+ * @COMMAND_NOOP: do 'dummy' request for integration/debug/trouble-shootings
+ * @COMMAND_RECONFIG: ask for FPGA configuration preparation, return status
+ * is SVC_STATUS_RECONFIG_REQUEST_OK
+ * @COMMAND_RECONFIG_DATA_SUBMIT: submit buffer(s) of bit-stream data for the
+ * FPGA configuration, return status is SVC_STATUS_RECONFIG_BUFFER_SUBMITTED,
+ * or SVC_STATUS_RECONFIG_ERROR
+ * @COMMAND_RECONFIG_DATA_CLAIM: check the status of the configuration, return
+ * status is SVC_STATUS_RECONFIG_COMPLETED, or SVC_STATUS_RECONFIG_BUSY, or
+ * SVC_STATUS_RECONFIG_ERROR
+ * @COMMAND_RECONFIG_STATUS: check the status of the configuration, return
+ * status is SVC_STATUS_RECONFIG_COMPLETED, or SVC_STATUS_RECONFIG_BUSY, or
+ * SVC_STATUS_RECONFIG_ERROR
+ * @COMMAND_RSU_STATUS: request remote system update boot log
+ * status is SVC_STATUS_RSU_ERROR or log data
+ * @COMMAND_RSU_UPDATE: set the offset of the bitstream to boot after reboot
+ * status is SVC_STATUS_RSU_OK or SVC_STATUS_RSU_ERROR
+ */
+enum intel_svc_command_code {
+ COMMAND_NOOP = 0,
+ COMMAND_RECONFIG,
+ COMMAND_RECONFIG_DATA_SUBMIT,
+ COMMAND_RECONFIG_DATA_CLAIM,
+ COMMAND_RECONFIG_STATUS,
+ COMMAND_RSU_STATUS,
+ COMMAND_RSU_UPDATE
+};
+
+/**
+ * struct intel_svc_client_msg - message sent by client to service
+ * @command: service command
+ * @payload: starting address of data need be processed
+ * @payload_length: data size in bytes
+ * @arg: args to be passed via registers and not physically mapped buffers
+ */
+struct intel_svc_client_msg {
+ void *payload;
+ size_t payload_length;
+ enum intel_svc_command_code command;
+ u64 arg[3];
+};
+
+/**
+ * struct intel_command_reconfig_payload - reconfig payload
+ * @flags: flag bit for the type of FPGA configuration
+ */
+struct intel_command_reconfig_payload {
+ u32 flags;
+};
+
+/**
+ * struct intel_svc_c_data - callback data structure from service layer
+ * @status: the status of sent command
+ * @kaddr1-3: used when status is SVC_COMMAND_STATUS_RECONFIG_BUFFER_DONE
+ *
+ * kaddr1 - address of 1st completed data block.
+ * kaddr2 - address of 2nd completed data block.
+ * kaddr3 - address of 3rd completed data block.
+ */
+struct intel_svc_c_data {
+ u32 status;
+ void *kaddr1;
+ void *kaddr2;
+ void *kaddr3;
+};
+
+/**
+ * struct intel_svc_client - service client structure
+ * @dev: the client device
+ * @receive_callback: callback to provide service client the received data
+ * @priv: client private data
+ */
+struct intel_svc_client {
+ struct device *dev;
+ void (*receive_cb)(struct intel_svc_client *client,
+ struct intel_svc_c_data *data);
+ void *priv;
+};
+
+/**
+ * request_svc_channel_byname() - request service channel
+ * @client: identity of the client requesting the channel
+ * @name: supporting client name defined above
+ *
+ * Return: a pointer to channel assigned to the client on success,
+ * or ERR_PTR() on error.
+ */
+struct intel_svc_chan
+*request_svc_channel_byname(struct intel_svc_client *client,
+ const char *name);
+
+/**
+ * free_svc_channel() - free service channel.
+ * @chan: service channel to be freed
+ */
+void free_svc_channel(struct intel_svc_chan *chan);
+
+/**
+ * intel_svc_allocate_memory() - allocate the momory
+ * @chan: service channel assigned to the client
+ * @size: number of bytes client requests
+ *
+ * Service layer allocates the requested number of bytes from the memory
+ * pool for the client.
+ *
+ * Return: the starting address of allocated memory on success, or
+ * ERR_PTR() on error.
+ */
+void *intel_svc_allocate_memory(struct intel_svc_chan *chan, size_t size);
+
+/**
+ * intel_svc_free_memory() - free allocated memory
+ * @chan: service channel assigned to the client
+ * @kaddr: starting address of memory to be free back to pool
+ */
+void intel_svc_free_memory(struct intel_svc_chan *chan, void *kaddr);
+
+/**
+ * intel_svc_send() - send a message to the remote
+ * @chan: service channel assigned to the client
+ * @msg: message data to be sent, in the format of struct intel_svc_client_msg
+ *
+ * Return: positive value for successful submission to the data queue created
+ * by service layer driver, or -ENOBUFS if the data queue FIFO is full.
+ */
+int intel_svc_send(struct intel_svc_chan *chan, void *msg);
+
+/**
+ * intel_svc_done() - complete service request
+ * @chan: service channel assigned to the client
+ *
+ * This function is used by service client to inform service layer that
+ * client's service requests are completed, or there is an error in the
+ * request process.
+ */
+void intel_svc_done(struct intel_svc_chan *chan);
+#endif
+
diff --git a/include/linux/mfd/altera-a10sr.h b/include/linux/mfd/altera-a10sr.h
index 45a5e6e7db54..d177744be95f 100644
--- a/include/linux/mfd/altera-a10sr.h
+++ b/include/linux/mfd/altera-a10sr.h
@@ -60,13 +60,71 @@
#define ALTR_A10SR_IN_VALID_RANGE_HI 15
#define ALTR_A10SR_PWR_GOOD1_REG 0x08 /* Power Good1 Read */
+/* Power Good #1 Register Bit Definitions */
+#define ALTR_A10SR_PG1_OP_FLAG_SHIFT 7 /* Power On Complete */
+#define ALTR_A10SR_PG1_1V8_SHIFT 6 /* 1.8V Power Good */
+#define ALTR_A10SR_PG1_2V5_SHIFT 5 /* 2.5V Power Good */
+#define ALTR_A10SR_PG1_3V3_SHIFT 4 /* 3.3V Power Good */
+#define ALTR_A10SR_PG1_5V0_SHIFT 3 /* 5.0V Power Good */
+#define ALTR_A10SR_PG1_0V9_SHIFT 2 /* 0.9V Power Good */
+#define ALTR_A10SR_PG1_0V95_SHIFT 1 /* 0.95V Power Good */
+#define ALTR_A10SR_PG1_1V0_SHIFT 0 /* 1.0V Power Good */
+
#define ALTR_A10SR_PWR_GOOD2_REG 0x0A /* Power Good2 Read */
+/* Power Good #2 Register Bit Definitions */
+#define ALTR_A10SR_PG2_HPS_SHIFT 7 /* HPS Power Good */
+#define ALTR_A10SR_PG2_HL_HPS_SHIFT 6 /* HILOHPS_VDD Power Good */
+#define ALTR_A10SR_PG2_HL_VDD_SHIFT 5 /* HILO VDD Power Good */
+#define ALTR_A10SR_PG2_HL_VDDQ_SHIFT 4 /* HILO VDDQ Power Good */
+#define ALTR_A10SR_PG2_FMCAVADJ_SHIFT 3 /* FMCA VADJ Power Good */
+#define ALTR_A10SR_PG2_FMCBVADJ_SHIFT 2 /* FMCB VADJ Power Good */
+#define ALTR_A10SR_PG2_FAC2MP_SHIFT 1 /* FAC2MP Power Good */
+#define ALTR_A10SR_PG2_FBC2MP_SHIFT 0 /* FBC2MP Power Good */
+
#define ALTR_A10SR_PWR_GOOD3_REG 0x0C /* Power Good3 Read */
+/* Power Good #3 Register Bit Definitions */
+#define ALTR_A10SR_PG3_FAM2C_SHIFT 7 /* FAM2C Power Good */
+#define ALTR_A10SR_PG3_10V_FAIL_SHIFT 6 /* 10V Fail n */
+#define ALTR_A10SR_PG3_BF_PR_SHIFT 5 /* BF Present n */
+#define ALTR_A10SR_PG3_FILE_PR_SHIFT 4 /* File Present n */
+#define ALTR_A10SR_PG3_FMCA_PR_SHIFT 3 /* FMCA Present n */
+#define ALTR_A10SR_PG3_FMCB_PR_SHIFT 2 /* FMCB Present n */
+#define ALTR_A10SR_PG3_PCIE_PR_SHIFT 1 /* PCIE Present n */
+#define ALTR_A10SR_PG3_PCIE_WAKE_SHIFT 0 /* PCIe Wake N */
+
#define ALTR_A10SR_FMCAB_REG 0x0E /* FMCA/B & PCIe Pwr Enable */
+/* FMCA/B & PCIe Power Bit Definitions */
+#define ALTR_A10SR_PCIE_EN_SHIFT 7 /* PCIe Pwr Enable */
+#define ALTR_A10SR_PCIE_AUXEN_SHIFT 6 /* PCIe Aux Pwr Enable */
+#define ALTR_A10SR_FMCA_EN_SHIFT 5 /* FMCA Pwr Enable */
+#define ALTR_A10SR_FMCA_AUXEN_SHIFT 4 /* FMCA Aux Pwr Enable */
+#define ALTR_A10SR_FMCB_EN_SHIFT 3 /* FMCB Pwr Enable */
+#define ALTR_A10SR_FMCB_AUXEN_SHIFT 2 /* FMCB Aux Pwr Enable */
+
#define ALTR_A10SR_HPS_RST_REG 0x10 /* HPS Reset */
+#define ALTR_A10SR_HPS_UARTA_RSTN_SHIFT 7 /* UARTA Reset n */
+#define ALTR_A10SR_HPS_WARM_RSTN_SHIFT 6 /* WARM Reset n */
+#define ALTR_A10SR_HPS_WARM_RST1N_SHIFT 5 /* WARM Reset1 n */
+#define ALTR_A10SR_HPS_COLD_RSTN_SHIFT 4 /* COLD Reset n */
+#define ALTR_A10SR_HPS_NPOR_SHIFT 3 /* N Power On Reset */
+#define ALTR_A10SR_HPS_NRST_SHIFT 2 /* N Reset */
+#define ALTR_A10SR_HPS_ENET_RSTN_SHIFT 1 /* Ethernet Reset n */
+#define ALTR_A10SR_HPS_ENET_INTN_SHIFT 0 /* Ethernet IRQ n */
+
#define ALTR_A10SR_USB_QSPI_REG 0x12 /* USB, BQSPI, FILE Reset */
+#define ALTR_A10SR_USB_RST_SHIFT 7 /* USB Reset */
+#define ALTR_A10SR_BQSPI_RST_N_SHIFT 6 /* BQSPI Reset n */
+#define ALTR_A10SR_FILE_RST_N_SHIFT 5 /* FILE Reset n */
+#define ALTR_A10SR_PCIE_PERST_N_SHIFT 4 /* PCIe PE Reset n */
+
#define ALTR_A10SR_SFPA_REG 0x14 /* SFPA Control Reg */
#define ALTR_A10SR_SFPB_REG 0x16 /* SFPB Control Reg */
+/* SFPA Bit Definitions */
+#define ALTR_A10SR_SFP_TXDIS_SHIFT 7 /* SFPA TX Disable */
+#define ALTR_A10SR_SFP_RATESEL10 0x60 /* SFPA_Rate Select [1:0] */
+#define ALTR_A10SR_SFP_LOS_SHIFT 4 /* SFPA LOS */
+#define ALTR_A10SR_SFP_FAULT_SHIFT 3 /* SFPA Fault */
+
#define ALTR_A10SR_I2C_M_REG 0x18 /* I2C Master Select */
#define ALTR_A10SR_WARM_RST_REG 0x1A /* HPS Warm Reset */
#define ALTR_A10SR_WR_KEY_REG 0x1C /* HPS Warm Reset Key */