aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/arm/arm,scpi.txt34
-rw-r--r--Documentation/devicetree/bindings/bus/nvidia,tegra210-aconnect.txt45
-rw-r--r--Documentation/devicetree/bindings/clock/clps711x-clock.txt4
-rw-r--r--Documentation/devicetree/bindings/display/cirrus,clps711x-fb.txt4
-rw-r--r--Documentation/devicetree/bindings/input/clps711x-keypad.txt4
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/cirrus,clps711x-intc.txt4
-rw-r--r--Documentation/devicetree/bindings/media/s5p-mfc.txt39
-rw-r--r--Documentation/devicetree/bindings/memory-controllers/atmel,ebi.txt136
-rw-r--r--Documentation/devicetree/bindings/power/renesas,rcar-sysc.txt1
-rw-r--r--Documentation/devicetree/bindings/pwm/cirrus,clps711x-pwm.txt5
-rw-r--r--Documentation/devicetree/bindings/serial/cirrus,clps711x-uart.txt4
-rw-r--r--Documentation/devicetree/bindings/soc/qcom/qcom,smp2p.txt4
-rw-r--r--Documentation/devicetree/bindings/soc/qcom/qcom,smsm.txt4
-rw-r--r--Documentation/devicetree/bindings/timer/cirrus,clps711x-timer.txt6
-rw-r--r--MAINTAINERS2
-rw-r--r--arch/arm/boot/dts/exynos-mfc-reserved-memory.dtsi29
-rw-r--r--arch/arm/boot/dts/exynos4210-origen.dts4
-rw-r--r--arch/arm/boot/dts/exynos4210-smdkv310.dts4
-rw-r--r--arch/arm/boot/dts/exynos4412-odroid-common.dtsi6
-rw-r--r--arch/arm/boot/dts/exynos4412-origen.dts4
-rw-r--r--arch/arm/boot/dts/exynos4412-smdk4412.dts4
-rw-r--r--arch/arm/boot/dts/exynos5250-arndale.dts4
-rw-r--r--arch/arm/boot/dts/exynos5250-smdk5250.dts4
-rw-r--r--arch/arm/boot/dts/exynos5250-spring.dts4
-rw-r--r--arch/arm/boot/dts/exynos5420-arndale-octa.dts4
-rw-r--r--arch/arm/boot/dts/exynos5420-peach-pit.dts4
-rw-r--r--arch/arm/boot/dts/exynos5420-smdk5420.dts4
-rw-r--r--arch/arm/boot/dts/exynos5422-odroidxu3-common.dtsi4
-rw-r--r--arch/arm/boot/dts/exynos5800-peach-pi.dts4
-rw-r--r--arch/arm/mach-exynos/Kconfig1
-rw-r--r--arch/arm/mach-exynos/Makefile3
-rw-r--r--arch/arm/mach-exynos/exynos.c19
-rw-r--r--arch/arm/mach-exynos/mfc.h16
-rw-r--r--arch/arm/mach-exynos/s5p-dev-mfc.c93
-rw-r--r--arch/arm/mach-shmobile/pm-r8a7779.c6
-rw-r--r--arch/arm/mach-shmobile/pm-rcar-gen2.c6
-rw-r--r--drivers/bus/Kconfig13
-rw-r--r--drivers/bus/Makefile1
-rw-r--r--drivers/bus/tegra-aconnect.c112
-rw-r--r--drivers/clk/clk-clps711x.c2
-rw-r--r--drivers/clocksource/clps711x-timer.c2
-rw-r--r--drivers/cpufreq/s5pv210-cpufreq.c68
-rw-r--r--drivers/firmware/Kconfig13
-rw-r--r--drivers/firmware/Makefile1
-rw-r--r--drivers/firmware/arm_scpi.c42
-rw-r--r--drivers/firmware/qcom_scm-32.c327
-rw-r--r--drivers/firmware/qcom_scm-64.c307
-rw-r--r--drivers/firmware/qcom_scm.c345
-rw-r--r--drivers/firmware/qcom_scm.h47
-rw-r--r--drivers/firmware/scpi_pm_domain.c163
-rw-r--r--drivers/input/keyboard/clps711x-keypad.c4
-rw-r--r--drivers/irqchip/irq-clps711x.c2
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-core.c2
-rw-r--r--drivers/media/platform/exynos4-is/fimc-core.c2
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is.c2
-rw-r--r--drivers/media/platform/exynos4-is/fimc-lite.c2
-rw-r--r--drivers/media/platform/s5p-g2d/g2d.c2
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-core.c2
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc.c198
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h79
-rw-r--r--drivers/media/platform/s5p-tv/mixer_video.c2
-rw-r--r--drivers/media/v4l2-core/videobuf2-dma-contig.c53
-rw-r--r--drivers/memory/Kconfig11
-rw-r--r--drivers/memory/Makefile1
-rw-r--r--drivers/memory/atmel-ebi.c766
-rw-r--r--drivers/memory/atmel-sdramc.c11
-rw-r--r--drivers/memory/omap-gpmc.c136
-rw-r--r--drivers/memory/samsung/exynos-srom.c39
-rw-r--r--drivers/memory/tegra/mc.c10
-rw-r--r--drivers/memory/tegra/tegra124-emc.c8
-rw-r--r--drivers/of/of_reserved_mem.c85
-rw-r--r--drivers/power/reset/Kconfig10
-rw-r--r--drivers/power/reset/Makefile1
-rw-r--r--drivers/power/reset/brcm-kona-reset.c73
-rw-r--r--drivers/pwm/pwm-clps711x.c2
-rw-r--r--drivers/soc/Kconfig1
-rw-r--r--drivers/soc/Makefile1
-rw-r--r--drivers/soc/bcm/Kconfig15
-rw-r--r--drivers/soc/bcm/Makefile1
-rw-r--r--drivers/soc/bcm/brcmstb/Makefile (renamed from drivers/soc/brcmstb/Makefile)0
-rw-r--r--drivers/soc/bcm/brcmstb/biuctrl.c (renamed from drivers/soc/brcmstb/biuctrl.c)1
-rw-r--r--drivers/soc/bcm/brcmstb/common.c (renamed from drivers/soc/brcmstb/common.c)0
-rw-r--r--drivers/soc/brcmstb/Kconfig10
-rw-r--r--drivers/soc/qcom/smem_state.c12
-rw-r--r--drivers/soc/qcom/smp2p.c7
-rw-r--r--drivers/soc/qcom/smsm.c2
-rw-r--r--drivers/soc/qcom/wcnss_ctrl.c125
-rw-r--r--drivers/soc/renesas/Makefile2
-rw-r--r--drivers/soc/renesas/r8a7792-sysc.c34
-rw-r--r--drivers/soc/renesas/r8a7796-sysc.c48
-rw-r--r--drivers/soc/renesas/rcar-sysc.c45
-rw-r--r--drivers/soc/renesas/rcar-sysc.h2
-rw-r--r--drivers/soc/samsung/Kconfig4
-rw-r--r--drivers/soc/samsung/Makefile1
-rw-r--r--drivers/soc/samsung/pm_domains.c (renamed from arch/arm/mach-exynos/pm_domains.c)34
-rw-r--r--drivers/soc/tegra/pmc.c149
-rw-r--r--drivers/tty/serial/clps711x.c2
-rw-r--r--drivers/video/fbdev/clps711x-fb.c4
-rw-r--r--include/dt-bindings/power/r8a7796-sysc.h36
-rw-r--r--include/linux/of_reserved_mem.h25
-rw-r--r--include/linux/qcom_scm.h8
-rw-r--r--include/linux/scpi_protocol.h2
-rw-r--r--include/linux/soc/qcom/wcnss_ctrl.h8
-rw-r--r--include/linux/soc/renesas/rcar-sysc.h2
-rw-r--r--include/media/videobuf2-dma-contig.h2
-rw-r--r--include/soc/tegra/cpuidle.h2
106 files changed, 3336 insertions, 718 deletions
diff --git a/Documentation/devicetree/bindings/arm/arm,scpi.txt b/Documentation/devicetree/bindings/arm/arm,scpi.txt
index 313dabdc14f9..faa4b44572e3 100644
--- a/Documentation/devicetree/bindings/arm/arm,scpi.txt
+++ b/Documentation/devicetree/bindings/arm/arm,scpi.txt
@@ -87,10 +87,33 @@ Required properties:
implementation for the IDs to use. For Juno
R0 and Juno R1 refer to [3].
+Power domain bindings for the power domains based on SCPI Message Protocol
+------------------------------------------------------------
+
+This binding uses the generic power domain binding[4].
+
+PM domain providers
+===================
+
+Required properties:
+ - #power-domain-cells : Should be 1. Contains the device or the power
+ domain ID value used by SCPI commands.
+ - num-domains: Total number of power domains provided by SCPI. This is
+ needed as the SCPI message protocol lacks a mechanism to
+ query this information at runtime.
+
+PM domain consumers
+===================
+
+Required properties:
+ - power-domains : A phandle and PM domain specifier as defined by bindings of
+ the power controller specified by phandle.
+
[0] http://infocenter.arm.com/help/topic/com.arm.doc.dui0922b/index.html
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
[2] Documentation/devicetree/bindings/thermal/thermal.txt
[3] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0922b/apas03s22.html
+[4] Documentation/devicetree/bindings/power/power_domain.txt
Example:
@@ -144,6 +167,12 @@ scpi_protocol: scpi@2e000000 {
compatible = "arm,scpi-sensors";
#thermal-sensor-cells = <1>;
};
+
+ scpi_devpd: scpi-power-domains {
+ compatible = "arm,scpi-power-domains";
+ num-domains = <2>;
+ #power-domain-cells = <1>;
+ };
};
cpu@0 {
@@ -156,6 +185,7 @@ hdlcd@7ff60000 {
...
reg = <0 0x7ff60000 0 0x1000>;
clocks = <&scpi_clk 4>;
+ power-domains = <&scpi_devpd 1>;
};
thermal-zones {
@@ -186,3 +216,7 @@ The thermal-sensors property in the soc_thermal node uses the
temperature sensor provided by SCP firmware to setup a thermal
zone. The ID "3" is the sensor identifier for the temperature sensor
as used by the firmware.
+
+The num-domains property in scpi-power-domains domain specifies that
+SCPI provides 2 power domains. The hdlcd node uses the power domain with
+domain ID 1.
diff --git a/Documentation/devicetree/bindings/bus/nvidia,tegra210-aconnect.txt b/Documentation/devicetree/bindings/bus/nvidia,tegra210-aconnect.txt
new file mode 100644
index 000000000000..7ff13be1750b
--- /dev/null
+++ b/Documentation/devicetree/bindings/bus/nvidia,tegra210-aconnect.txt
@@ -0,0 +1,45 @@
+NVIDIA Tegra ACONNECT Bus
+
+The Tegra ACONNECT bus is an AXI switch which is used to connnect various
+components inside the Audio Processing Engine (APE). All CPU accesses to
+the APE subsystem go through the ACONNECT via an APB to AXI wrapper.
+
+Required properties:
+- compatible: Must be "nvidia,tegra210-aconnect".
+- clocks: Must contain the entries for the APE clock (TEGRA210_CLK_APE),
+ and APE interface clock (TEGRA210_CLK_APB2APE).
+- clock-names: Must contain the names "ape" and "apb2ape" for the corresponding
+ 'clocks' entries.
+- power-domains: Must contain a phandle that points to the audio powergate
+ (namely 'aud') for Tegra210.
+- #address-cells: The number of cells used to represent physical base addresses
+ in the aconnect address space. Should be 1.
+- #size-cells: The number of cells used to represent the size of an address
+ range in the aconnect address space. Should be 1.
+- ranges: Mapping of the aconnect address space to the CPU address space.
+
+All devices accessed via the ACONNNECT are described by child-nodes.
+
+Example:
+
+ aconnect@702c0000 {
+ compatible = "nvidia,tegra210-aconnect";
+ clocks = <&tegra_car TEGRA210_CLK_APE>,
+ <&tegra_car TEGRA210_CLK_APB2APE>;
+ clock-names = "ape", "apb2ape";
+ power-domains = <&pd_audio>;
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x702c0000 0x0 0x702c0000 0x00040000>;
+
+ status = "disabled";
+
+ child1 {
+ ...
+ };
+
+ child2 {
+ ...
+ };
+ };
diff --git a/Documentation/devicetree/bindings/clock/clps711x-clock.txt b/Documentation/devicetree/bindings/clock/clps711x-clock.txt
index ce5a7476f05d..f1bd53f79d91 100644
--- a/Documentation/devicetree/bindings/clock/clps711x-clock.txt
+++ b/Documentation/devicetree/bindings/clock/clps711x-clock.txt
@@ -1,7 +1,7 @@
* Clock bindings for the Cirrus Logic CLPS711X CPUs
Required properties:
-- compatible : Shall contain "cirrus,clps711x-clk".
+- compatible : Shall contain "cirrus,ep7209-clk".
- reg : Address of the internal register set.
- startup-frequency: Factory set CPU startup frequency in HZ.
- #clock-cells : Should be <1>.
@@ -13,7 +13,7 @@ for the full list of CLPS711X clock IDs.
Example:
clks: clks@80000000 {
#clock-cells = <1>;
- compatible = "cirrus,ep7312-clk", "cirrus,clps711x-clk";
+ compatible = "cirrus,ep7312-clk", "cirrus,ep7209-clk";
reg = <0x80000000 0xc000>;
startup-frequency = <73728000>;
};
diff --git a/Documentation/devicetree/bindings/display/cirrus,clps711x-fb.txt b/Documentation/devicetree/bindings/display/cirrus,clps711x-fb.txt
index d685be898d0c..e9c65746e2f1 100644
--- a/Documentation/devicetree/bindings/display/cirrus,clps711x-fb.txt
+++ b/Documentation/devicetree/bindings/display/cirrus,clps711x-fb.txt
@@ -1,7 +1,7 @@
* Currus Logic CLPS711X Framebuffer
Required properties:
-- compatible: Shall contain "cirrus,clps711x-fb".
+- compatible: Shall contain "cirrus,ep7209-fb".
- reg : Physical base address and length of the controller's registers +
location and size of the framebuffer memory.
- clocks : phandle + clock specifier pair of the FB reference clock.
@@ -18,7 +18,7 @@ Optional properties:
Example:
fb: fb@800002c0 {
- compatible = "cirrus,ep7312-fb", "cirrus,clps711x-fb";
+ compatible = "cirrus,ep7312-fb", "cirrus,ep7209-fb";
reg = <0x800002c0 0xd44>, <0x60000000 0xc000>;
clocks = <&clks 2>;
lcd-supply = <&reg5v0>;
diff --git a/Documentation/devicetree/bindings/input/clps711x-keypad.txt b/Documentation/devicetree/bindings/input/clps711x-keypad.txt
index e68d2bbc6c07..3eed8819d05d 100644
--- a/Documentation/devicetree/bindings/input/clps711x-keypad.txt
+++ b/Documentation/devicetree/bindings/input/clps711x-keypad.txt
@@ -1,7 +1,7 @@
* Cirrus Logic CLPS711X matrix keypad device tree bindings
Required Properties:
-- compatible: Shall contain "cirrus,clps711x-keypad".
+- compatible: Shall contain "cirrus,ep7209-keypad".
- row-gpios: List of GPIOs used as row lines.
- poll-interval: Poll interval time in milliseconds.
- linux,keymap: The definition can be found at
@@ -12,7 +12,7 @@ Optional Properties:
Example:
keypad {
- compatible = "cirrus,ep7312-keypad", "cirrus,clps711x-keypad";
+ compatible = "cirrus,ep7312-keypad", "cirrus,ep7209-keypad";
autorepeat;
poll-interval = <120>;
row-gpios = <&porta 0 0>,
diff --git a/Documentation/devicetree/bindings/interrupt-controller/cirrus,clps711x-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/cirrus,clps711x-intc.txt
index 759339c34e4f..969b4582ec60 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/cirrus,clps711x-intc.txt
+++ b/Documentation/devicetree/bindings/interrupt-controller/cirrus,clps711x-intc.txt
@@ -2,7 +2,7 @@ Cirrus Logic CLPS711X Interrupt Controller
Required properties:
-- compatible: Should be "cirrus,clps711x-intc".
+- compatible: Should be "cirrus,ep7209-intc".
- reg: Specifies base physical address of the registers set.
- interrupt-controller: Identifies the node as an interrupt controller.
- #interrupt-cells: Specifies the number of cells needed to encode an
@@ -34,7 +34,7 @@ ID Name Description
Example:
intc: interrupt-controller {
- compatible = "cirrus,clps711x-intc";
+ compatible = "cirrus,ep7312-intc", "cirrus,ep7209-intc";
reg = <0x80000000 0x4000>;
interrupt-controller;
#interrupt-cells = <1>;
diff --git a/Documentation/devicetree/bindings/media/s5p-mfc.txt b/Documentation/devicetree/bindings/media/s5p-mfc.txt
index 2d5787eac91a..92c94f5ecbf1 100644
--- a/Documentation/devicetree/bindings/media/s5p-mfc.txt
+++ b/Documentation/devicetree/bindings/media/s5p-mfc.txt
@@ -21,15 +21,18 @@ Required properties:
- clock-names : from common clock binding: must contain "mfc",
corresponding to entry in the clocks property.
- - samsung,mfc-r : Base address of the first memory bank used by MFC
- for DMA contiguous memory allocation and its size.
-
- - samsung,mfc-l : Base address of the second memory bank used by MFC
- for DMA contiguous memory allocation and its size.
-
Optional properties:
- power-domains : power-domain property defined with a phandle
to respective power domain.
+ - memory-region : from reserved memory binding: phandles to two reserved
+ memory regions, first is for "left" mfc memory bus interfaces,
+ second if for the "right" mfc memory bus, used when no SYSMMU
+ support is available
+
+Obsolete properties:
+ - samsung,mfc-r, samsung,mfc-l : support removed, please use memory-region
+ property instead
+
Example:
SoC specific DT entry:
@@ -43,9 +46,29 @@ mfc: codec@13400000 {
clock-names = "mfc";
};
+Reserved memory specific DT entry for given board (see reserved memory binding
+for more information):
+
+reserved-memory {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ mfc_left: region@51000000 {
+ compatible = "shared-dma-pool";
+ no-map;
+ reg = <0x51000000 0x800000>;
+ };
+
+ mfc_right: region@43000000 {
+ compatible = "shared-dma-pool";
+ no-map;
+ reg = <0x43000000 0x800000>;
+ };
+};
+
Board specific DT entry:
codec@13400000 {
- samsung,mfc-r = <0x43000000 0x800000>;
- samsung,mfc-l = <0x51000000 0x800000>;
+ memory-region = <&mfc_left>, <&mfc_right>;
};
diff --git a/Documentation/devicetree/bindings/memory-controllers/atmel,ebi.txt b/Documentation/devicetree/bindings/memory-controllers/atmel,ebi.txt
new file mode 100644
index 000000000000..9bb5f57e2066
--- /dev/null
+++ b/Documentation/devicetree/bindings/memory-controllers/atmel,ebi.txt
@@ -0,0 +1,136 @@
+* Device tree bindings for Atmel EBI
+
+The External Bus Interface (EBI) controller is a bus where you can connect
+asynchronous (NAND, NOR, SRAM, ....) and synchronous memories (SDR/DDR SDRAMs).
+The EBI provides a glue-less interface to asynchronous memories through the SMC
+(Static Memory Controller).
+
+Required properties:
+
+- compatible: "atmel,at91sam9260-ebi"
+ "atmel,at91sam9261-ebi"
+ "atmel,at91sam9263-ebi0"
+ "atmel,at91sam9263-ebi1"
+ "atmel,at91sam9rl-ebi"
+ "atmel,at91sam9g45-ebi"
+ "atmel,at91sam9x5-ebi"
+ "atmel,sama5d3-ebi"
+
+- reg: Contains offset/length value for EBI memory mapping.
+ This property might contain several entries if the EBI
+ memory range is not contiguous
+
+- #address-cells: Must be 2.
+ The first cell encodes the CS.
+ The second cell encode the offset into the CS memory
+ range.
+
+- #size-cells: Must be set to 1.
+
+- ranges: Encodes CS to memory region association.
+
+- clocks: Clock feeding the EBI controller.
+ See clock-bindings.txt
+
+Children device nodes are representing device connected to the EBI bus.
+
+Required device node properties:
+
+- reg: Contains the chip-select id, the offset and the length
+ of the memory region requested by the device.
+
+EBI bus configuration will be defined directly in the device subnode.
+
+Optional EBI/SMC properties:
+
+- atmel,smc-bus-width: width of the asynchronous device's data bus
+ 8, 16 or 32.
+ Default to 8 when undefined.
+
+- atmel,smc-byte-access-type "write" or "select" (see Atmel datasheet).
+ Default to "select" when undefined.
+
+- atmel,smc-read-mode "nrd" or "ncs".
+ Default to "ncs" when undefined.
+
+- atmel,smc-write-mode "nwe" or "ncs".
+ Default to "ncs" when undefined.
+
+- atmel,smc-exnw-mode "disabled", "frozen" or "ready".
+ Default to "disabled" when undefined.
+
+- atmel,smc-page-mode enable page mode if present. The provided value
+ defines the page size (supported values: 4, 8,
+ 16 and 32).
+
+- atmel,smc-tdf-mode: "normal" or "optimized". When set to
+ "optimized" the data float time is optimized
+ depending on the next device being accessed
+ (next device setup time is subtracted to the
+ current device data float time).
+ Default to "normal" when undefined.
+
+If at least one atmel,smc- property is defined the following SMC timing
+properties become mandatory. In the other hand, if none of the atmel,smc-
+properties are specified, we assume that the EBI bus configuration will be
+handled by the sub-device driver, and none of those properties should be
+defined.
+
+All the timings are expressed in nanoseconds (see Atmel datasheet for a full
+description).
+
+- atmel,smc-ncs-rd-setup-ns
+- atmel,smc-nrd-setup-ns
+- atmel,smc-ncs-wr-setup-ns
+- atmel,smc-nwe-setup-ns
+- atmel,smc-ncs-rd-pulse-ns
+- atmel,smc-nrd-pulse-ns
+- atmel,smc-ncs-wr-pulse-ns
+- atmel,smc-nwe-pulse-ns
+- atmel,smc-nwe-cycle-ns
+- atmel,smc-nrd-cycle-ns
+- atmel,smc-tdf-ns
+
+Example:
+
+ ebi: ebi@10000000 {
+ compatible = "atmel,sama5d3-ebi";
+ #address-cells = <2>;
+ #size-cells = <1>;
+ atmel,smc = <&hsmc>;
+ atmel,matrix = <&matrix>;
+ reg = <0x10000000 0x10000000
+ 0x40000000 0x30000000>;
+ ranges = <0x0 0x0 0x10000000 0x10000000
+ 0x1 0x0 0x40000000 0x10000000
+ 0x2 0x0 0x50000000 0x10000000
+ 0x3 0x0 0x60000000 0x10000000>;
+ clocks = <&mck>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_ebi_addr>;
+
+ nor: flash@0,0 {
+ compatible = "cfi-flash";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = <0x0 0x0 0x1000000>;
+ bank-width = <2>;
+
+ atmel,smc-read-mode = "nrd";
+ atmel,smc-write-mode = "nwe";
+ atmel,smc-bus-width = <16>;
+ atmel,smc-ncs-rd-setup-ns = <0>;
+ atmel,smc-ncs-wr-setup-ns = <0>;
+ atmel,smc-nwe-setup-ns = <8>;
+ atmel,smc-nrd-setup-ns = <16>;
+ atmel,smc-ncs-rd-pulse-ns = <84>;
+ atmel,smc-ncs-wr-pulse-ns = <84>;
+ atmel,smc-nrd-pulse-ns = <76>;
+ atmel,smc-nwe-pulse-ns = <76>;
+ atmel,smc-nrd-cycle-ns = <107>;
+ atmel,smc-nwe-cycle-ns = <84>;
+ atmel,smc-tdf-ns = <16>;
+ };
+ };
+
diff --git a/Documentation/devicetree/bindings/power/renesas,rcar-sysc.txt b/Documentation/devicetree/bindings/power/renesas,rcar-sysc.txt
index b74e4d4785ab..0725fb37a973 100644
--- a/Documentation/devicetree/bindings/power/renesas,rcar-sysc.txt
+++ b/Documentation/devicetree/bindings/power/renesas,rcar-sysc.txt
@@ -14,6 +14,7 @@ Required properties:
- "renesas,r8a7793-sysc" (R-Car M2-N)
- "renesas,r8a7794-sysc" (R-Car E2)
- "renesas,r8a7795-sysc" (R-Car H3)
+ - "renesas,r8a7796-sysc" (R-Car M3-W)
- reg: Address start and address range for the device.
- #power-domain-cells: Must be 1.
diff --git a/Documentation/devicetree/bindings/pwm/cirrus,clps711x-pwm.txt b/Documentation/devicetree/bindings/pwm/cirrus,clps711x-pwm.txt
index a183db48f910..c0b2028238d6 100644
--- a/Documentation/devicetree/bindings/pwm/cirrus,clps711x-pwm.txt
+++ b/Documentation/devicetree/bindings/pwm/cirrus,clps711x-pwm.txt
@@ -1,15 +1,14 @@
* Cirris Logic CLPS711X PWM controller
Required properties:
-- compatible: Shall contain "cirrus,clps711x-pwm".
+- compatible: Shall contain "cirrus,ep7209-pwm".
- reg: Physical base address and length of the controller's registers.
- clocks: phandle + clock specifier pair of the PWM reference clock.
- #pwm-cells: Should be 1. The cell specifies the index of the channel.
Example:
pwm: pwm@80000400 {
- compatible = "cirrus,ep7312-pwm",
- "cirrus,clps711x-pwm";
+ compatible = "cirrus,ep7312-pwm", "cirrus,ep7209-pwm";
reg = <0x80000400 0x4>;
clocks = <&clks 8>;
#pwm-cells = <1>;
diff --git a/Documentation/devicetree/bindings/serial/cirrus,clps711x-uart.txt b/Documentation/devicetree/bindings/serial/cirrus,clps711x-uart.txt
index caaeb2583579..07013fa60a48 100644
--- a/Documentation/devicetree/bindings/serial/cirrus,clps711x-uart.txt
+++ b/Documentation/devicetree/bindings/serial/cirrus,clps711x-uart.txt
@@ -1,7 +1,7 @@
* Cirrus Logic CLPS711X Universal Asynchronous Receiver/Transmitter (UART)
Required properties:
-- compatible: Should be "cirrus,clps711x-uart".
+- compatible: Should be "cirrus,ep7209-uart".
- reg: Address and length of the register set for the device.
- interrupts: Should contain UART TX and RX interrupt.
- clocks: Should contain UART core clock number.
@@ -20,7 +20,7 @@ Example:
};
uart1: uart@80000480 {
- compatible = "cirrus,clps711x-uart";
+ compatible = "cirrus,ep7312-uart","cirrus,ep7209-uart";
reg = <0x80000480 0x80>;
interrupts = <12 13>;
clocks = <&clks 11>;
diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,smp2p.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,smp2p.txt
index 5cc82b8353d8..af9ca37221ce 100644
--- a/Documentation/devicetree/bindings/soc/qcom/qcom,smp2p.txt
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smp2p.txt
@@ -68,7 +68,7 @@ important.
Value type: <u32>
Definition: must be 2 - denoting the bit in the entry and IRQ flags
-- #qcom,state-cells:
+- #qcom,smem-state-cells:
Usage: required for outgoing entries
Value type: <u32>
Definition: must be 1 - denoting the bit in the entry
@@ -92,7 +92,7 @@ wcnss-smp2p {
wcnss_smp2p_out: master-kernel {
qcom,entry-name = "master-kernel";
- #qcom,state-cells = <1>;
+ #qcom,smem-state-cells = <1>;
};
wcnss_smp2p_in: slave-kernel {
diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,smsm.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,smsm.txt
index a6634c70850d..2993b5a97dd6 100644
--- a/Documentation/devicetree/bindings/soc/qcom/qcom,smsm.txt
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smsm.txt
@@ -51,7 +51,7 @@ important.
Definition: specifies the offset, in words, of the first bit for this
entry
-- #qcom,state-cells:
+- #qcom,smem-state-cells:
Usage: required for local entry
Value type: <u32>
Definition: must be 1 - denotes bit number
@@ -91,7 +91,7 @@ smsm {
apps_smsm: apps@0 {
reg = <0>;
- #qcom,state-cells = <1>;
+ #qcom,smem-state-cells = <1>;
};
wcnss_smsm: wcnss@7 {
diff --git a/Documentation/devicetree/bindings/timer/cirrus,clps711x-timer.txt b/Documentation/devicetree/bindings/timer/cirrus,clps711x-timer.txt
index cd55b52548e4..d4c62e7b1714 100644
--- a/Documentation/devicetree/bindings/timer/cirrus,clps711x-timer.txt
+++ b/Documentation/devicetree/bindings/timer/cirrus,clps711x-timer.txt
@@ -1,7 +1,7 @@
* Cirrus Logic CLPS711X Timer Counter
Required properties:
-- compatible: Shall contain "cirrus,clps711x-timer".
+- compatible: Shall contain "cirrus,ep7209-timer".
- reg : Address and length of the register set.
- interrupts: The interrupt number of the timer.
- clocks : phandle of timer reference clock.
@@ -15,14 +15,14 @@ Example:
};
timer1: timer@80000300 {
- compatible = "cirrus,ep7312-timer", "cirrus,clps711x-timer";
+ compatible = "cirrus,ep7312-timer", "cirrus,ep7209-timer";
reg = <0x80000300 0x4>;
interrupts = <8>;
clocks = <&clks 5>;
};
timer2: timer@80000340 {
- compatible = "cirrus,ep7312-timer", "cirrus,clps711x-timer";
+ compatible = "cirrus,ep7312-timer", "cirrus,ep7209-timer";
reg = <0x80000340 0x4>;
interrupts = <9>;
clocks = <&clks 6>;
diff --git a/MAINTAINERS b/MAINTAINERS
index 7304d2e37a98..d5c45bce85d4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1521,6 +1521,7 @@ M: David Brown <[email protected]>
S: Maintained
+F: Documentation/devicetree/bindings/soc/qcom/
F: arch/arm/boot/dts/qcom-*.dts
F: arch/arm/boot/dts/qcom-*.dtsi
F: arch/arm/mach-qcom/
@@ -2489,6 +2490,7 @@ F: arch/arm64/boot/dts/broadcom/
F: arch/arm/configs/bcm_defconfig
F: drivers/mmc/host/sdhci-bcm-kona.c
F: drivers/clocksource/bcm_kona_timer.c
+F: drivers/power/reset/brcm-kona-reset.c
BROADCOM BCM2835 ARM ARCHITECTURE
M: Stephen Warren <[email protected]>
diff --git a/arch/arm/boot/dts/exynos-mfc-reserved-memory.dtsi b/arch/arm/boot/dts/exynos-mfc-reserved-memory.dtsi
new file mode 100644
index 000000000000..c4d063ae6b74
--- /dev/null
+++ b/arch/arm/boot/dts/exynos-mfc-reserved-memory.dtsi
@@ -0,0 +1,29 @@
+/*
+ * Samsung's Exynos SoC MFC (Video Codec) reserved memory common definition.
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/ {
+ reserved-memory {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ mfc_left: region@51000000 {
+ compatible = "shared-dma-pool";
+ no-map;
+ reg = <0x51000000 0x800000>;
+ };
+
+ mfc_right: region@43000000 {
+ compatible = "shared-dma-pool";
+ no-map;
+ reg = <0x43000000 0x800000>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/exynos4210-origen.dts b/arch/arm/boot/dts/exynos4210-origen.dts
index ad7394c1d67a..f5e4eb23aa21 100644
--- a/arch/arm/boot/dts/exynos4210-origen.dts
+++ b/arch/arm/boot/dts/exynos4210-origen.dts
@@ -18,6 +18,7 @@
#include "exynos4210.dtsi"
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
+#include "exynos-mfc-reserved-memory.dtsi"
/ {
model = "Insignal Origen evaluation board based on Exynos4210";
@@ -288,8 +289,7 @@
};
&mfc {
- samsung,mfc-r = <0x43000000 0x800000>;
- samsung,mfc-l = <0x51000000 0x800000>;
+ memory-region = <&mfc_left>, <&mfc_right>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/exynos4210-smdkv310.dts b/arch/arm/boot/dts/exynos4210-smdkv310.dts
index 94ca7d36ab37..de917f0d907d 100644
--- a/arch/arm/boot/dts/exynos4210-smdkv310.dts
+++ b/arch/arm/boot/dts/exynos4210-smdkv310.dts
@@ -17,6 +17,7 @@
/dts-v1/;
#include "exynos4210.dtsi"
#include <dt-bindings/gpio/gpio.h>
+#include "exynos-mfc-reserved-memory.dtsi"
/ {
model = "Samsung smdkv310 evaluation board based on Exynos4210";
@@ -133,8 +134,7 @@
};
&mfc {
- samsung,mfc-r = <0x43000000 0x800000>;
- samsung,mfc-l = <0x51000000 0x800000>;
+ memory-region = <&mfc_left>, <&mfc_right>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi
index ec7619a384a2..276ac9a7bb82 100644
--- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi
+++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi
@@ -13,6 +13,7 @@
#include "exynos4412.dtsi"
#include "exynos4412-ppmu-common.dtsi"
#include <dt-bindings/gpio/gpio.h>
+#include "exynos-mfc-reserved-memory.dtsi"
/ {
chosen {
@@ -499,6 +500,11 @@
clock-names = "iis", "i2s_opclk0", "i2s_opclk1";
};
+&mfc {
+ memory-region = <&mfc_left>, <&mfc_right>;
+ status = "okay";
+};
+
&mixer {
status = "okay";
};
diff --git a/arch/arm/boot/dts/exynos4412-origen.dts b/arch/arm/boot/dts/exynos4412-origen.dts
index 8bca699b7f20..cd363d7e4e34 100644
--- a/arch/arm/boot/dts/exynos4412-origen.dts
+++ b/arch/arm/boot/dts/exynos4412-origen.dts
@@ -16,6 +16,7 @@
#include "exynos4412.dtsi"
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
+#include "exynos-mfc-reserved-memory.dtsi"
/ {
model = "Insignal Origen evaluation board based on Exynos4412";
@@ -466,8 +467,7 @@
};
&mfc {
- samsung,mfc-r = <0x43000000 0x800000>;
- samsung,mfc-l = <0x51000000 0x800000>;
+ memory-region = <&mfc_left>, <&mfc_right>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/exynos4412-smdk4412.dts b/arch/arm/boot/dts/exynos4412-smdk4412.dts
index a51069f3c03b..9b6d561dbdac 100644
--- a/arch/arm/boot/dts/exynos4412-smdk4412.dts
+++ b/arch/arm/boot/dts/exynos4412-smdk4412.dts
@@ -14,6 +14,7 @@
/dts-v1/;
#include "exynos4412.dtsi"
+#include "exynos-mfc-reserved-memory.dtsi"
/ {
model = "Samsung SMDK evaluation board based on Exynos4412";
@@ -112,8 +113,7 @@
};
&mfc {
- samsung,mfc-r = <0x43000000 0x800000>;
- samsung,mfc-l = <0x51000000 0x800000>;
+ memory-region = <&mfc_left>, <&mfc_right>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/exynos5250-arndale.dts b/arch/arm/boot/dts/exynos5250-arndale.dts
index 85dad29c08dc..39940f4bd556 100644
--- a/arch/arm/boot/dts/exynos5250-arndale.dts
+++ b/arch/arm/boot/dts/exynos5250-arndale.dts
@@ -14,6 +14,7 @@
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/input/input.h>
#include "exynos5250.dtsi"
+#include "exynos-mfc-reserved-memory.dtsi"
/ {
model = "Insignal Arndale evaluation board based on EXYNOS5250";
@@ -516,8 +517,7 @@
};
&mfc {
- samsung,mfc-r = <0x43000000 0x800000>;
- samsung,mfc-l = <0x51000000 0x800000>;
+ memory-region = <&mfc_left>, <&mfc_right>;
};
&mmc_0 {
diff --git a/arch/arm/boot/dts/exynos5250-smdk5250.dts b/arch/arm/boot/dts/exynos5250-smdk5250.dts
index b7992b13c9de..9fac874af5eb 100644
--- a/arch/arm/boot/dts/exynos5250-smdk5250.dts
+++ b/arch/arm/boot/dts/exynos5250-smdk5250.dts
@@ -13,6 +13,7 @@
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include "exynos5250.dtsi"
+#include "exynos-mfc-reserved-memory.dtsi"
/ {
model = "SAMSUNG SMDK5250 board based on EXYNOS5250";
@@ -344,8 +345,7 @@
};
&mfc {
- samsung,mfc-r = <0x43000000 0x800000>;
- samsung,mfc-l = <0x51000000 0x800000>;
+ memory-region = <&mfc_left>, <&mfc_right>;
};
&mmc_0 {
diff --git a/arch/arm/boot/dts/exynos5250-spring.dts b/arch/arm/boot/dts/exynos5250-spring.dts
index ac291f540812..784130bdb6a3 100644
--- a/arch/arm/boot/dts/exynos5250-spring.dts
+++ b/arch/arm/boot/dts/exynos5250-spring.dts
@@ -14,6 +14,7 @@
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/input/input.h>
#include "exynos5250.dtsi"
+#include "exynos-mfc-reserved-memory.dtsi"
/ {
model = "Google Spring";
@@ -425,8 +426,7 @@
};
&mfc {
- samsung,mfc-r = <0x43000000 0x800000>;
- samsung,mfc-l = <0x51000000 0x800000>;
+ memory-region = <&mfc_left>, <&mfc_right>;
};
&mmc_0 {
diff --git a/arch/arm/boot/dts/exynos5420-arndale-octa.dts b/arch/arm/boot/dts/exynos5420-arndale-octa.dts
index 60bc861d0f9d..b8b5f3ae2942 100644
--- a/arch/arm/boot/dts/exynos5420-arndale-octa.dts
+++ b/arch/arm/boot/dts/exynos5420-arndale-octa.dts
@@ -16,6 +16,7 @@
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/input/input.h>
#include <dt-bindings/clock/samsung,s2mps11.h>
+#include "exynos-mfc-reserved-memory.dtsi"
/ {
model = "Insignal Arndale Octa evaluation board based on EXYNOS5420";
@@ -347,8 +348,7 @@
};
&mfc {
- samsung,mfc-r = <0x43000000 0x800000>;
- samsung,mfc-l = <0x51000000 0x800000>;
+ memory-region = <&mfc_left>, <&mfc_right>;
};
&mmc_0 {
diff --git a/arch/arm/boot/dts/exynos5420-peach-pit.dts b/arch/arm/boot/dts/exynos5420-peach-pit.dts
index f9d2e4f1a0e0..d530b4f5f1e9 100644
--- a/arch/arm/boot/dts/exynos5420-peach-pit.dts
+++ b/arch/arm/boot/dts/exynos5420-peach-pit.dts
@@ -16,6 +16,7 @@
#include <dt-bindings/regulator/maxim,max77802.h>
#include "exynos5420.dtsi"
#include "exynos5420-cpus.dtsi"
+#include "exynos-mfc-reserved-memory.dtsi"
/ {
model = "Google Peach Pit Rev 6+";
@@ -695,8 +696,7 @@
};
&mfc {
- samsung,mfc-r = <0x43000000 0x800000>;
- samsung,mfc-l = <0x51000000 0x800000>;
+ memory-region = <&mfc_left>, <&mfc_right>;
};
&mmc_0 {
diff --git a/arch/arm/boot/dts/exynos5420-smdk5420.dts b/arch/arm/boot/dts/exynos5420-smdk5420.dts
index 2e748d19322f..5206f41e548d 100644
--- a/arch/arm/boot/dts/exynos5420-smdk5420.dts
+++ b/arch/arm/boot/dts/exynos5420-smdk5420.dts
@@ -13,6 +13,7 @@
#include "exynos5420.dtsi"
#include "exynos5420-cpus.dtsi"
#include <dt-bindings/gpio/gpio.h>
+#include "exynos-mfc-reserved-memory.dtsi"
/ {
model = "Samsung SMDK5420 board based on EXYNOS5420";
@@ -355,8 +356,7 @@
};
&mfc {
- samsung,mfc-r = <0x43000000 0x800000>;
- samsung,mfc-l = <0x51000000 0x800000>;
+ memory-region = <&mfc_left>, <&mfc_right>;
};
&mmc_0 {
diff --git a/arch/arm/boot/dts/exynos5422-odroidxu3-common.dtsi b/arch/arm/boot/dts/exynos5422-odroidxu3-common.dtsi
index 2a4e10bc8801..7c2335f18bfc 100644
--- a/arch/arm/boot/dts/exynos5422-odroidxu3-common.dtsi
+++ b/arch/arm/boot/dts/exynos5422-odroidxu3-common.dtsi
@@ -17,6 +17,7 @@
#include "exynos5800.dtsi"
#include "exynos5422-cpus.dtsi"
#include "exynos5422-cpu-thermal.dtsi"
+#include "exynos-mfc-reserved-memory.dtsi"
/ {
memory {
@@ -406,8 +407,7 @@
};
&mfc {
- samsung,mfc-r = <0x43000000 0x800000>;
- samsung,mfc-l = <0x51000000 0x800000>;
+ memory-region = <&mfc_left>, <&mfc_right>;
};
&mmc_0 {
diff --git a/arch/arm/boot/dts/exynos5800-peach-pi.dts b/arch/arm/boot/dts/exynos5800-peach-pi.dts
index 62ceb89e073f..1f735963ca98 100644
--- a/arch/arm/boot/dts/exynos5800-peach-pi.dts
+++ b/arch/arm/boot/dts/exynos5800-peach-pi.dts
@@ -16,6 +16,7 @@
#include <dt-bindings/regulator/maxim,max77802.h>
#include "exynos5800.dtsi"
#include "exynos5420-cpus.dtsi"
+#include "exynos-mfc-reserved-memory.dtsi"
/ {
model = "Google Peach Pi Rev 10+";
@@ -670,8 +671,7 @@
};
&mfc {
- samsung,mfc-r = <0x43000000 0x800000>;
- samsung,mfc-l = <0x51000000 0x800000>;
+ memory-region = <&mfc_left>, <&mfc_right>;
};
&mmc_0 {
diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig
index e65aa7d11b20..58b334f5f1d6 100644
--- a/arch/arm/mach-exynos/Kconfig
+++ b/arch/arm/mach-exynos/Kconfig
@@ -19,6 +19,7 @@ menuconfig ARCH_EXYNOS
select EXYNOS_THERMAL
select EXYNOS_PMU
select EXYNOS_SROM
+ select EXYNOS_PM_DOMAINS if PM_GENERIC_DOMAINS
select HAVE_ARM_SCU if SMP
select HAVE_S3C2410_I2C if I2C
select HAVE_S3C2410_WATCHDOG if WATCHDOG
diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile
index 34d29df3e006..9ea6c54645ad 100644
--- a/arch/arm/mach-exynos/Makefile
+++ b/arch/arm/mach-exynos/Makefile
@@ -13,7 +13,6 @@ obj-$(CONFIG_ARCH_EXYNOS) += exynos.o exynos-smc.o firmware.o
obj-$(CONFIG_EXYNOS_CPU_SUSPEND) += pm.o sleep.o
obj-$(CONFIG_PM_SLEEP) += suspend.o
-obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o
obj-$(CONFIG_SMP) += platsmp.o headsmp.o
@@ -23,5 +22,3 @@ AFLAGS_sleep.o :=-Wa,-march=armv7-a$(plus_sec)
obj-$(CONFIG_EXYNOS5420_MCPM) += mcpm-exynos.o
CFLAGS_mcpm-exynos.o += -march=armv7-a
-
-obj-$(CONFIG_S5P_DEV_MFC) += s5p-dev-mfc.o
diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c
index 52ccf247e079..a8620c6eb723 100644
--- a/arch/arm/mach-exynos/exynos.c
+++ b/arch/arm/mach-exynos/exynos.c
@@ -27,7 +27,6 @@
#include <mach/map.h>
#include "common.h"
-#include "mfc.h"
static struct map_desc exynos4_iodesc[] __initdata = {
{
@@ -237,23 +236,6 @@ static char const *const exynos_dt_compat[] __initconst = {
NULL
};
-static void __init exynos_reserve(void)
-{
-#ifdef CONFIG_S5P_DEV_MFC
- int i;
- char *mfc_mem[] = {
- "samsung,mfc-v5",
- "samsung,mfc-v6",
- "samsung,mfc-v7",
- "samsung,mfc-v8",
- };
-
- for (i = 0; i < ARRAY_SIZE(mfc_mem); i++)
- if (of_scan_flat_dt(s5p_fdt_alloc_mfc_mem, mfc_mem[i]))
- break;
-#endif
-}
-
static void __init exynos_dt_fixup(void)
{
/*
@@ -275,6 +257,5 @@ DT_MACHINE_START(EXYNOS_DT, "SAMSUNG EXYNOS (Flattened Device Tree)")
.init_machine = exynos_dt_machine_init,
.init_late = exynos_init_late,
.dt_compat = exynos_dt_compat,
- .reserve = exynos_reserve,
.dt_fixup = exynos_dt_fixup,
MACHINE_END
diff --git a/arch/arm/mach-exynos/mfc.h b/arch/arm/mach-exynos/mfc.h
deleted file mode 100644
index dec93cd5b3c6..000000000000
--- a/arch/arm/mach-exynos/mfc.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright (C) 2013 Samsung Electronics Co.Ltd
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#ifndef __MACH_EXYNOS_MFC_H
-#define __MACH_EXYNOS_MFC_H __FILE__
-
-int __init s5p_fdt_alloc_mfc_mem(unsigned long node, const char *uname,
- int depth, void *data);
-
-#endif /* __MACH_EXYNOS_MFC_H */
diff --git a/arch/arm/mach-exynos/s5p-dev-mfc.c b/arch/arm/mach-exynos/s5p-dev-mfc.c
deleted file mode 100644
index 8ef1f3ee4e98..000000000000
--- a/arch/arm/mach-exynos/s5p-dev-mfc.c
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2010-2011 Samsung Electronics Co.Ltd
- *
- * Base S5P MFC resource and device definitions
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <linux/dma-mapping.h>
-#include <linux/memblock.h>
-#include <linux/ioport.h>
-#include <linux/of_fdt.h>
-#include <linux/of.h>
-
-static struct platform_device s5p_device_mfc_l;
-static struct platform_device s5p_device_mfc_r;
-
-struct s5p_mfc_dt_meminfo {
- unsigned long loff;
- unsigned long lsize;
- unsigned long roff;
- unsigned long rsize;
- char *compatible;
-};
-
-struct s5p_mfc_reserved_mem {
- phys_addr_t base;
- unsigned long size;
- struct device *dev;
-};
-
-static struct s5p_mfc_reserved_mem s5p_mfc_mem[2] __initdata;
-
-
-static void __init s5p_mfc_reserve_mem(phys_addr_t rbase, unsigned int rsize,
- phys_addr_t lbase, unsigned int lsize)
-{
- int i;
-
- s5p_mfc_mem[0].dev = &s5p_device_mfc_r.dev;
- s5p_mfc_mem[0].base = rbase;
- s5p_mfc_mem[0].size = rsize;
-
- s5p_mfc_mem[1].dev = &s5p_device_mfc_l.dev;
- s5p_mfc_mem[1].base = lbase;
- s5p_mfc_mem[1].size = lsize;
-
- for (i = 0; i < ARRAY_SIZE(s5p_mfc_mem); i++) {
- struct s5p_mfc_reserved_mem *area = &s5p_mfc_mem[i];
- if (memblock_remove(area->base, area->size)) {
- printk(KERN_ERR "Failed to reserve memory for MFC device (%ld bytes at 0x%08lx)\n",
- area->size, (unsigned long) area->base);
- area->base = 0;
- }
- }
-}
-
-int __init s5p_fdt_alloc_mfc_mem(unsigned long node, const char *uname,
- int depth, void *data)
-{
- const __be32 *prop;
- int len;
- struct s5p_mfc_dt_meminfo mfc_mem;
-
- if (!data)
- return 0;
-
- if (!of_flat_dt_is_compatible(node, data))
- return 0;
-
- prop = of_get_flat_dt_prop(node, "samsung,mfc-l", &len);
- if (!prop || (len != 2 * sizeof(unsigned long)))
- return 0;
-
- mfc_mem.loff = be32_to_cpu(prop[0]);
- mfc_mem.lsize = be32_to_cpu(prop[1]);
-
- prop = of_get_flat_dt_prop(node, "samsung,mfc-r", &len);
- if (!prop || (len != 2 * sizeof(unsigned long)))
- return 0;
-
- mfc_mem.roff = be32_to_cpu(prop[0]);
- mfc_mem.rsize = be32_to_cpu(prop[1]);
-
- s5p_mfc_reserve_mem(mfc_mem.roff, mfc_mem.rsize,
- mfc_mem.loff, mfc_mem.lsize);
-
- return 1;
-}
diff --git a/arch/arm/mach-shmobile/pm-r8a7779.c b/arch/arm/mach-shmobile/pm-r8a7779.c
index 4174cbcbc467..5c9a93f5e650 100644
--- a/arch/arm/mach-shmobile/pm-r8a7779.c
+++ b/arch/arm/mach-shmobile/pm-r8a7779.c
@@ -23,11 +23,7 @@
static void __init r8a7779_sysc_init(void)
{
- void __iomem *base = rcar_sysc_init(0xffd85000);
-
- /* enable all interrupt sources, but do not use interrupt handler */
- iowrite32(0x0131000e, base + SYSCIER);
- iowrite32(0, base + SYSCIMR);
+ rcar_sysc_init(0xffd85000, 0x0131000e);
}
#else /* CONFIG_PM || CONFIG_SMP */
diff --git a/arch/arm/mach-shmobile/pm-rcar-gen2.c b/arch/arm/mach-shmobile/pm-rcar-gen2.c
index 691ac166a277..7768fd1bce65 100644
--- a/arch/arm/mach-shmobile/pm-rcar-gen2.c
+++ b/arch/arm/mach-shmobile/pm-rcar-gen2.c
@@ -37,11 +37,7 @@
static void __init rcar_gen2_sysc_init(u32 syscier)
{
- void __iomem *base = rcar_sysc_init(0xe6180000);
-
- /* enable all interrupt sources, but do not use interrupt handler */
- iowrite32(syscier, base + SYSCIER);
- iowrite32(0, base + SYSCIMR);
+ rcar_sysc_init(0xe6180000, syscier);
}
#else /* CONFIG_SMP */
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index c5a7de9bc783..3b205e212337 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -132,6 +132,19 @@ config SUNXI_RSB
with various RSB based devices, such as AXP223, AXP8XX PMICs,
and AC100/AC200 ICs.
+# TODO: This uses pm_clk_*() symbols that aren't exported in v4.7 and hence
+# the driver will fail to build as a module. However there are patches to
+# address that queued for v4.8, so this can be turned into a tristate symbol
+# after v4.8-rc1.
+config TEGRA_ACONNECT
+ bool "Tegra ACONNECT Bus Driver"
+ depends on ARCH_TEGRA_210_SOC
+ depends on OF && PM
+ select PM_CLK
+ help
+ Driver for the Tegra ACONNECT bus which is used to interface with
+ the devices inside the Audio Processing Engine (APE) for Tegra210.
+
config UNIPHIER_SYSTEM_BUS
tristate "UniPhier System Bus driver"
depends on ARCH_UNIPHIER && OF
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index ccff007ee7e8..ac84cc4348e3 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -17,5 +17,6 @@ obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o
obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o
obj-$(CONFIG_SUNXI_RSB) += sunxi-rsb.o
obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o
+obj-$(CONFIG_TEGRA_ACONNECT) += tegra-aconnect.o
obj-$(CONFIG_UNIPHIER_SYSTEM_BUS) += uniphier-system-bus.o
obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o
diff --git a/drivers/bus/tegra-aconnect.c b/drivers/bus/tegra-aconnect.c
new file mode 100644
index 000000000000..7e4104b74fa8
--- /dev/null
+++ b/drivers/bus/tegra-aconnect.c
@@ -0,0 +1,112 @@
+/*
+ * Tegra ACONNECT Bus Driver
+ *
+ * Copyright (C) 2016, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_clock.h>
+#include <linux/pm_runtime.h>
+
+static int tegra_aconnect_add_clock(struct device *dev, char *name)
+{
+ struct clk *clk;
+ int ret;
+
+ clk = clk_get(dev, name);
+ if (IS_ERR(clk)) {
+ dev_err(dev, "%s clock not found\n", name);
+ return PTR_ERR(clk);
+ }
+
+ ret = pm_clk_add_clk(dev, clk);
+ if (ret)
+ clk_put(clk);
+
+ return ret;
+}
+
+static int tegra_aconnect_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ if (!pdev->dev.of_node)
+ return -EINVAL;
+
+ ret = pm_clk_create(&pdev->dev);
+ if (ret)
+ return ret;
+
+ ret = tegra_aconnect_add_clock(&pdev->dev, "ape");
+ if (ret)
+ goto clk_destroy;
+
+ ret = tegra_aconnect_add_clock(&pdev->dev, "apb2ape");
+ if (ret)
+ goto clk_destroy;
+
+ pm_runtime_enable(&pdev->dev);
+
+ of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+
+ dev_info(&pdev->dev, "Tegra ACONNECT bus registered\n");
+
+ return 0;
+
+clk_destroy:
+ pm_clk_destroy(&pdev->dev);
+
+ return ret;
+}
+
+static int tegra_aconnect_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+
+ pm_clk_destroy(&pdev->dev);
+
+ return 0;
+}
+
+static int tegra_aconnect_runtime_resume(struct device *dev)
+{
+ return pm_clk_resume(dev);
+}
+
+static int tegra_aconnect_runtime_suspend(struct device *dev)
+{
+ return pm_clk_suspend(dev);
+}
+
+static const struct dev_pm_ops tegra_aconnect_pm_ops = {
+ SET_RUNTIME_PM_OPS(tegra_aconnect_runtime_suspend,
+ tegra_aconnect_runtime_resume, NULL)
+};
+
+static const struct of_device_id tegra_aconnect_of_match[] = {
+ { .compatible = "nvidia,tegra210-aconnect", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tegra_aconnect_of_match);
+
+static struct platform_driver tegra_aconnect_driver = {
+ .probe = tegra_aconnect_probe,
+ .remove = tegra_aconnect_remove,
+ .driver = {
+ .name = "tegra-aconnect",
+ .of_match_table = tegra_aconnect_of_match,
+ .pm = &tegra_aconnect_pm_ops,
+ },
+};
+module_platform_driver(tegra_aconnect_driver);
+
+MODULE_DESCRIPTION("NVIDIA Tegra ACONNECT Bus Driver");
+MODULE_AUTHOR("Jon Hunter <[email protected]>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/clk-clps711x.c b/drivers/clk/clk-clps711x.c
index 1f60b02416a7..adaf109f2fe2 100644
--- a/drivers/clk/clk-clps711x.c
+++ b/drivers/clk/clk-clps711x.c
@@ -184,5 +184,5 @@ static void __init clps711x_clk_init_dt(struct device_node *np)
of_clk_add_provider(np, of_clk_src_onecell_get,
&clps711x_clk->clk_data);
}
-CLK_OF_DECLARE(clps711x, "cirrus,clps711x-clk", clps711x_clk_init_dt);
+CLK_OF_DECLARE(clps711x, "cirrus,ep7209-clk", clps711x_clk_init_dt);
#endif
diff --git a/drivers/clocksource/clps711x-timer.c b/drivers/clocksource/clps711x-timer.c
index cdd86e3525bb..7c65f9e1d347 100644
--- a/drivers/clocksource/clps711x-timer.c
+++ b/drivers/clocksource/clps711x-timer.c
@@ -121,5 +121,5 @@ static void __init clps711x_timer_init(struct device_node *np)
break;
}
}
-CLOCKSOURCE_OF_DECLARE(clps711x, "cirrus,clps711x-timer", clps711x_timer_init);
+CLOCKSOURCE_OF_DECLARE(clps711x, "cirrus,ep7209-timer", clps711x_timer_init);
#endif
diff --git a/drivers/cpufreq/s5pv210-cpufreq.c b/drivers/cpufreq/s5pv210-cpufreq.c
index 06d85917b6d5..392b29a0eb17 100644
--- a/drivers/cpufreq/s5pv210-cpufreq.c
+++ b/drivers/cpufreq/s5pv210-cpufreq.c
@@ -220,7 +220,7 @@ static void s5pv210_set_refresh(enum s5pv210_dmc_port ch, unsigned long freq)
tmp1 /= tmp;
- __raw_writel(tmp1, reg);
+ writel_relaxed(tmp1, reg);
}
static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index)
@@ -301,29 +301,29 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index)
* 1. Temporary Change divider for MFC and G3D
* SCLKA2M(200/1=200)->(200/4=50)Mhz
*/
- reg = __raw_readl(S5P_CLK_DIV2);
+ reg = readl_relaxed(S5P_CLK_DIV2);
reg &= ~(S5P_CLKDIV2_G3D_MASK | S5P_CLKDIV2_MFC_MASK);
reg |= (3 << S5P_CLKDIV2_G3D_SHIFT) |
(3 << S5P_CLKDIV2_MFC_SHIFT);
- __raw_writel(reg, S5P_CLK_DIV2);
+ writel_relaxed(reg, S5P_CLK_DIV2);
/* For MFC, G3D dividing */
do {
- reg = __raw_readl(S5P_CLKDIV_STAT0);
+ reg = readl_relaxed(S5P_CLKDIV_STAT0);
} while (reg & ((1 << 16) | (1 << 17)));
/*
* 2. Change SCLKA2M(200Mhz)to SCLKMPLL in MFC_MUX, G3D MUX
* (200/4=50)->(667/4=166)Mhz
*/
- reg = __raw_readl(S5P_CLK_SRC2);
+ reg = readl_relaxed(S5P_CLK_SRC2);
reg &= ~(S5P_CLKSRC2_G3D_MASK | S5P_CLKSRC2_MFC_MASK);
reg |= (1 << S5P_CLKSRC2_G3D_SHIFT) |
(1 << S5P_CLKSRC2_MFC_SHIFT);
- __raw_writel(reg, S5P_CLK_SRC2);
+ writel_relaxed(reg, S5P_CLK_SRC2);
do {
- reg = __raw_readl(S5P_CLKMUX_STAT1);
+ reg = readl_relaxed(S5P_CLKMUX_STAT1);
} while (reg & ((1 << 7) | (1 << 3)));
/*
@@ -335,19 +335,19 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index)
s5pv210_set_refresh(DMC1, 133000);
/* 4. SCLKAPLL -> SCLKMPLL */
- reg = __raw_readl(S5P_CLK_SRC0);
+ reg = readl_relaxed(S5P_CLK_SRC0);
reg &= ~(S5P_CLKSRC0_MUX200_MASK);
reg |= (0x1 << S5P_CLKSRC0_MUX200_SHIFT);
- __raw_writel(reg, S5P_CLK_SRC0);
+ writel_relaxed(reg, S5P_CLK_SRC0);
do {
- reg = __raw_readl(S5P_CLKMUX_STAT0);
+ reg = readl_relaxed(S5P_CLKMUX_STAT0);
} while (reg & (0x1 << 18));
}
/* Change divider */
- reg = __raw_readl(S5P_CLK_DIV0);
+ reg = readl_relaxed(S5P_CLK_DIV0);
reg &= ~(S5P_CLKDIV0_APLL_MASK | S5P_CLKDIV0_A2M_MASK |
S5P_CLKDIV0_HCLK200_MASK | S5P_CLKDIV0_PCLK100_MASK |
@@ -363,25 +363,25 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index)
(clkdiv_val[index][6] << S5P_CLKDIV0_HCLK133_SHIFT) |
(clkdiv_val[index][7] << S5P_CLKDIV0_PCLK66_SHIFT));
- __raw_writel(reg, S5P_CLK_DIV0);
+ writel_relaxed(reg, S5P_CLK_DIV0);
do {
- reg = __raw_readl(S5P_CLKDIV_STAT0);
+ reg = readl_relaxed(S5P_CLKDIV_STAT0);
} while (reg & 0xff);
/* ARM MCS value changed */
- reg = __raw_readl(S5P_ARM_MCS_CON);
+ reg = readl_relaxed(S5P_ARM_MCS_CON);
reg &= ~0x3;
if (index >= L3)
reg |= 0x3;
else
reg |= 0x1;
- __raw_writel(reg, S5P_ARM_MCS_CON);
+ writel_relaxed(reg, S5P_ARM_MCS_CON);
if (pll_changing) {
/* 5. Set Lock time = 30us*24Mhz = 0x2cf */
- __raw_writel(0x2cf, S5P_APLL_LOCK);
+ writel_relaxed(0x2cf, S5P_APLL_LOCK);
/*
* 6. Turn on APLL
@@ -389,12 +389,12 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index)
* 6-2. Wait untile the PLL is locked
*/
if (index == L0)
- __raw_writel(APLL_VAL_1000, S5P_APLL_CON);
+ writel_relaxed(APLL_VAL_1000, S5P_APLL_CON);
else
- __raw_writel(APLL_VAL_800, S5P_APLL_CON);
+ writel_relaxed(APLL_VAL_800, S5P_APLL_CON);
do {
- reg = __raw_readl(S5P_APLL_CON);
+ reg = readl_relaxed(S5P_APLL_CON);
} while (!(reg & (0x1 << 29)));
/*
@@ -402,39 +402,39 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index)
* to SCLKA2M(200Mhz) in MFC_MUX and G3D MUX
* (667/4=166)->(200/4=50)Mhz
*/
- reg = __raw_readl(S5P_CLK_SRC2);
+ reg = readl_relaxed(S5P_CLK_SRC2);
reg &= ~(S5P_CLKSRC2_G3D_MASK | S5P_CLKSRC2_MFC_MASK);
reg |= (0 << S5P_CLKSRC2_G3D_SHIFT) |
(0 << S5P_CLKSRC2_MFC_SHIFT);
- __raw_writel(reg, S5P_CLK_SRC2);
+ writel_relaxed(reg, S5P_CLK_SRC2);
do {
- reg = __raw_readl(S5P_CLKMUX_STAT1);
+ reg = readl_relaxed(S5P_CLKMUX_STAT1);
} while (reg & ((1 << 7) | (1 << 3)));
/*
* 8. Change divider for MFC and G3D
* (200/4=50)->(200/1=200)Mhz
*/
- reg = __raw_readl(S5P_CLK_DIV2);
+ reg = readl_relaxed(S5P_CLK_DIV2);
reg &= ~(S5P_CLKDIV2_G3D_MASK | S5P_CLKDIV2_MFC_MASK);
reg |= (clkdiv_val[index][10] << S5P_CLKDIV2_G3D_SHIFT) |
(clkdiv_val[index][9] << S5P_CLKDIV2_MFC_SHIFT);
- __raw_writel(reg, S5P_CLK_DIV2);
+ writel_relaxed(reg, S5P_CLK_DIV2);
/* For MFC, G3D dividing */
do {
- reg = __raw_readl(S5P_CLKDIV_STAT0);
+ reg = readl_relaxed(S5P_CLKDIV_STAT0);
} while (reg & ((1 << 16) | (1 << 17)));
/* 9. Change MPLL to APLL in MSYS_MUX */
- reg = __raw_readl(S5P_CLK_SRC0);
+ reg = readl_relaxed(S5P_CLK_SRC0);
reg &= ~(S5P_CLKSRC0_MUX200_MASK);
reg |= (0x0 << S5P_CLKSRC0_MUX200_SHIFT);
- __raw_writel(reg, S5P_CLK_SRC0);
+ writel_relaxed(reg, S5P_CLK_SRC0);
do {
- reg = __raw_readl(S5P_CLKMUX_STAT0);
+ reg = readl_relaxed(S5P_CLKMUX_STAT0);
} while (reg & (0x1 << 18));
/*
@@ -451,13 +451,13 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index)
* and memory refresh parameter should be changed
*/
if (bus_speed_changing) {
- reg = __raw_readl(S5P_CLK_DIV6);
+ reg = readl_relaxed(S5P_CLK_DIV6);
reg &= ~S5P_CLKDIV6_ONEDRAM_MASK;
reg |= (clkdiv_val[index][8] << S5P_CLKDIV6_ONEDRAM_SHIFT);
- __raw_writel(reg, S5P_CLK_DIV6);
+ writel_relaxed(reg, S5P_CLK_DIV6);
do {
- reg = __raw_readl(S5P_CLKDIV_STAT1);
+ reg = readl_relaxed(S5P_CLKDIV_STAT1);
} while (reg & (1 << 15));
/* Reconfigure DRAM refresh counter value */
@@ -497,7 +497,7 @@ static int check_mem_type(void __iomem *dmc_reg)
{
unsigned long val;
- val = __raw_readl(dmc_reg + 0x4);
+ val = readl_relaxed(dmc_reg + 0x4);
val = (val & (0xf << 8));
return val >> 8;
@@ -542,10 +542,10 @@ static int s5pv210_cpu_init(struct cpufreq_policy *policy)
}
/* Find current refresh counter and frequency each DMC */
- s5pv210_dram_conf[0].refresh = (__raw_readl(dmc_base[0] + 0x30) * 1000);
+ s5pv210_dram_conf[0].refresh = (readl_relaxed(dmc_base[0] + 0x30) * 1000);
s5pv210_dram_conf[0].freq = clk_get_rate(dmc0_clk);
- s5pv210_dram_conf[1].refresh = (__raw_readl(dmc_base[1] + 0x30) * 1000);
+ s5pv210_dram_conf[1].refresh = (readl_relaxed(dmc_base[1] + 0x30) * 1000);
s5pv210_dram_conf[1].freq = clk_get_rate(dmc1_clk);
policy->suspend_freq = SLEEP_FREQ;
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 6664f1108c7c..541d3fb7ae43 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -10,7 +10,7 @@ config ARM_PSCI_FW
config ARM_SCPI_PROTOCOL
tristate "ARM System Control and Power Interface (SCPI) Message Protocol"
- depends on ARM_MHU
+ depends on MAILBOX
help
System Control and Power Interface (SCPI) Message Protocol is
defined for the purpose of communication between the Application
@@ -27,6 +27,16 @@ config ARM_SCPI_PROTOCOL
This protocol library provides interface for all the client drivers
making use of the features offered by the SCP.
+config ARM_SCPI_POWER_DOMAIN
+ tristate "SCPI power domain driver"
+ depends on ARM_SCPI_PROTOCOL || COMPILE_TEST
+ default y
+ select PM_GENERIC_DOMAINS if PM
+ select PM_GENERIC_DOMAINS_OF if PM
+ help
+ This enables support for the SCPI power domains which can be
+ enabled or disabled via the SCP firmware
+
config EDD
tristate "BIOS Enhanced Disk Drive calls determine boot disk"
depends on X86
@@ -184,6 +194,7 @@ config FW_CFG_SYSFS_CMDLINE
config QCOM_SCM
bool
depends on ARM || ARM64
+ select RESET_CONTROLLER
config QCOM_SCM_32
def_bool y
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 474bada56fcd..44a59dcfc398 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -3,6 +3,7 @@
#
obj-$(CONFIG_ARM_PSCI_FW) += psci.o
obj-$(CONFIG_ARM_SCPI_PROTOCOL) += arm_scpi.o
+obj-$(CONFIG_ARM_SCPI_POWER_DOMAIN) += scpi_pm_domain.o
obj-$(CONFIG_DMI) += dmi_scan.o
obj-$(CONFIG_DMI_SYSFS) += dmi-sysfs.o
obj-$(CONFIG_EDD) += edd.o
diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c
index 7e3e595c9f30..438893762076 100644
--- a/drivers/firmware/arm_scpi.c
+++ b/drivers/firmware/arm_scpi.c
@@ -210,10 +210,6 @@ struct dvfs_info {
} opps[MAX_DVFS_OPPS];
} __packed;
-struct dvfs_get {
- u8 index;
-} __packed;
-
struct dvfs_set {
u8 domain;
u8 index;
@@ -235,6 +231,11 @@ struct sensor_value {
__le32 hi_val;
} __packed;
+struct dev_pstate_set {
+ u16 dev_id;
+ u8 pstate;
+} __packed;
+
static struct scpi_drvinfo *scpi_info;
static int scpi_linux_errmap[SCPI_ERR_MAX] = {
@@ -431,11 +432,11 @@ static int scpi_clk_set_val(u16 clk_id, unsigned long rate)
static int scpi_dvfs_get_idx(u8 domain)
{
int ret;
- struct dvfs_get dvfs;
+ u8 dvfs_idx;
ret = scpi_send_message(SCPI_CMD_GET_DVFS, &domain, sizeof(domain),
- &dvfs, sizeof(dvfs));
- return ret ? ret : dvfs.index;
+ &dvfs_idx, sizeof(dvfs_idx));
+ return ret ? ret : dvfs_idx;
}
static int scpi_dvfs_set_idx(u8 domain, u8 index)
@@ -526,7 +527,7 @@ static int scpi_sensor_get_info(u16 sensor_id, struct scpi_sensor_info *info)
return ret;
}
-int scpi_sensor_get_value(u16 sensor, u64 *val)
+static int scpi_sensor_get_value(u16 sensor, u64 *val)
{
__le16 id = cpu_to_le16(sensor);
struct sensor_value buf;
@@ -541,6 +542,29 @@ int scpi_sensor_get_value(u16 sensor, u64 *val)
return ret;
}
+static int scpi_device_get_power_state(u16 dev_id)
+{
+ int ret;
+ u8 pstate;
+ __le16 id = cpu_to_le16(dev_id);
+
+ ret = scpi_send_message(SCPI_CMD_GET_DEVICE_PWR_STATE, &id,
+ sizeof(id), &pstate, sizeof(pstate));
+ return ret ? ret : pstate;
+}
+
+static int scpi_device_set_power_state(u16 dev_id, u8 pstate)
+{
+ int stat;
+ struct dev_pstate_set dev_set = {
+ .dev_id = cpu_to_le16(dev_id),
+ .pstate = pstate,
+ };
+
+ return scpi_send_message(SCPI_CMD_SET_DEVICE_PWR_STATE, &dev_set,
+ sizeof(dev_set), &stat, sizeof(stat));
+}
+
static struct scpi_ops scpi_ops = {
.get_version = scpi_get_version,
.clk_get_range = scpi_clk_get_range,
@@ -552,6 +576,8 @@ static struct scpi_ops scpi_ops = {
.sensor_get_capability = scpi_sensor_get_capability,
.sensor_get_info = scpi_sensor_get_info,
.sensor_get_value = scpi_sensor_get_value,
+ .device_get_power_state = scpi_device_get_power_state,
+ .device_set_power_state = scpi_device_set_power_state,
};
struct scpi_ops *get_scpi_ops(void)
diff --git a/drivers/firmware/qcom_scm-32.c b/drivers/firmware/qcom_scm-32.c
index 0883292f640f..c6aeedbdcbb0 100644
--- a/drivers/firmware/qcom_scm-32.c
+++ b/drivers/firmware/qcom_scm-32.c
@@ -23,8 +23,7 @@
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/qcom_scm.h>
-
-#include <asm/cacheflush.h>
+#include <linux/dma-mapping.h>
#include "qcom_scm.h"
@@ -97,44 +96,6 @@ struct qcom_scm_response {
};
/**
- * alloc_qcom_scm_command() - Allocate an SCM command
- * @cmd_size: size of the command buffer
- * @resp_size: size of the response buffer
- *
- * Allocate an SCM command, including enough room for the command
- * and response headers as well as the command and response buffers.
- *
- * Returns a valid &qcom_scm_command on success or %NULL if the allocation fails.
- */
-static struct qcom_scm_command *alloc_qcom_scm_command(size_t cmd_size, size_t resp_size)
-{
- struct qcom_scm_command *cmd;
- size_t len = sizeof(*cmd) + sizeof(struct qcom_scm_response) + cmd_size +
- resp_size;
- u32 offset;
-
- cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL);
- if (cmd) {
- cmd->len = cpu_to_le32(len);
- offset = offsetof(struct qcom_scm_command, buf);
- cmd->buf_offset = cpu_to_le32(offset);
- cmd->resp_hdr_offset = cpu_to_le32(offset + cmd_size);
- }
- return cmd;
-}
-
-/**
- * free_qcom_scm_command() - Free an SCM command
- * @cmd: command to free
- *
- * Free an SCM command.
- */
-static inline void free_qcom_scm_command(struct qcom_scm_command *cmd)
-{
- kfree(cmd);
-}
-
-/**
* qcom_scm_command_to_response() - Get a pointer to a qcom_scm_response
* @cmd: command
*
@@ -168,23 +129,6 @@ static inline void *qcom_scm_get_response_buffer(const struct qcom_scm_response
return (void *)rsp + le32_to_cpu(rsp->buf_offset);
}
-static int qcom_scm_remap_error(int err)
-{
- pr_err("qcom_scm_call failed with error code %d\n", err);
- switch (err) {
- case QCOM_SCM_ERROR:
- return -EIO;
- case QCOM_SCM_EINVAL_ADDR:
- case QCOM_SCM_EINVAL_ARG:
- return -EINVAL;
- case QCOM_SCM_EOPNOTSUPP:
- return -EOPNOTSUPP;
- case QCOM_SCM_ENOMEM:
- return -ENOMEM;
- }
- return -EINVAL;
-}
-
static u32 smc(u32 cmd_addr)
{
int context_id;
@@ -209,45 +153,9 @@ static u32 smc(u32 cmd_addr)
return r0;
}
-static int __qcom_scm_call(const struct qcom_scm_command *cmd)
-{
- int ret;
- u32 cmd_addr = virt_to_phys(cmd);
-
- /*
- * Flush the command buffer so that the secure world sees
- * the correct data.
- */
- secure_flush_area(cmd, cmd->len);
-
- ret = smc(cmd_addr);
- if (ret < 0)
- ret = qcom_scm_remap_error(ret);
-
- return ret;
-}
-
-static void qcom_scm_inv_range(unsigned long start, unsigned long end)
-{
- u32 cacheline_size, ctr;
-
- asm volatile("mrc p15, 0, %0, c0, c0, 1" : "=r" (ctr));
- cacheline_size = 4 << ((ctr >> 16) & 0xf);
-
- start = round_down(start, cacheline_size);
- end = round_up(end, cacheline_size);
- outer_inv_range(start, end);
- while (start < end) {
- asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start)
- : "memory");
- start += cacheline_size;
- }
- dsb();
- isb();
-}
-
/**
* qcom_scm_call() - Send an SCM command
+ * @dev: struct device
* @svc_id: service identifier
* @cmd_id: command identifier
* @cmd_buf: command buffer
@@ -264,42 +172,59 @@ static void qcom_scm_inv_range(unsigned long start, unsigned long end)
* and response buffers is taken care of by qcom_scm_call; however, callers are
* responsible for any other cached buffers passed over to the secure world.
*/
-static int qcom_scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf,
- size_t cmd_len, void *resp_buf, size_t resp_len)
+static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id,
+ const void *cmd_buf, size_t cmd_len, void *resp_buf,
+ size_t resp_len)
{
int ret;
struct qcom_scm_command *cmd;
struct qcom_scm_response *rsp;
- unsigned long start, end;
+ size_t alloc_len = sizeof(*cmd) + cmd_len + sizeof(*rsp) + resp_len;
+ dma_addr_t cmd_phys;
- cmd = alloc_qcom_scm_command(cmd_len, resp_len);
+ cmd = kzalloc(PAGE_ALIGN(alloc_len), GFP_KERNEL);
if (!cmd)
return -ENOMEM;
+ cmd->len = cpu_to_le32(alloc_len);
+ cmd->buf_offset = cpu_to_le32(sizeof(*cmd));
+ cmd->resp_hdr_offset = cpu_to_le32(sizeof(*cmd) + cmd_len);
+
cmd->id = cpu_to_le32((svc_id << 10) | cmd_id);
if (cmd_buf)
memcpy(qcom_scm_get_command_buffer(cmd), cmd_buf, cmd_len);
+ rsp = qcom_scm_command_to_response(cmd);
+
+ cmd_phys = dma_map_single(dev, cmd, alloc_len, DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, cmd_phys)) {
+ kfree(cmd);
+ return -ENOMEM;
+ }
+
mutex_lock(&qcom_scm_lock);
- ret = __qcom_scm_call(cmd);
+ ret = smc(cmd_phys);
+ if (ret < 0)
+ ret = qcom_scm_remap_error(ret);
mutex_unlock(&qcom_scm_lock);
if (ret)
goto out;
- rsp = qcom_scm_command_to_response(cmd);
- start = (unsigned long)rsp;
-
do {
- qcom_scm_inv_range(start, start + sizeof(*rsp));
+ dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len,
+ sizeof(*rsp), DMA_FROM_DEVICE);
} while (!rsp->is_complete);
- end = (unsigned long)qcom_scm_get_response_buffer(rsp) + resp_len;
- qcom_scm_inv_range(start, end);
-
- if (resp_buf)
- memcpy(resp_buf, qcom_scm_get_response_buffer(rsp), resp_len);
+ if (resp_buf) {
+ dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len +
+ le32_to_cpu(rsp->buf_offset),
+ resp_len, DMA_FROM_DEVICE);
+ memcpy(resp_buf, qcom_scm_get_response_buffer(rsp),
+ resp_len);
+ }
out:
- free_qcom_scm_command(cmd);
+ dma_unmap_single(dev, cmd_phys, alloc_len, DMA_TO_DEVICE);
+ kfree(cmd);
return ret;
}
@@ -342,6 +267,41 @@ static s32 qcom_scm_call_atomic1(u32 svc, u32 cmd, u32 arg1)
return r0;
}
+/**
+ * qcom_scm_call_atomic2() - Send an atomic SCM command with two arguments
+ * @svc_id: service identifier
+ * @cmd_id: command identifier
+ * @arg1: first argument
+ * @arg2: second argument
+ *
+ * This shall only be used with commands that are guaranteed to be
+ * uninterruptable, atomic and SMP safe.
+ */
+static s32 qcom_scm_call_atomic2(u32 svc, u32 cmd, u32 arg1, u32 arg2)
+{
+ int context_id;
+
+ register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 2);
+ register u32 r1 asm("r1") = (u32)&context_id;
+ register u32 r2 asm("r2") = arg1;
+ register u32 r3 asm("r3") = arg2;
+
+ asm volatile(
+ __asmeq("%0", "r0")
+ __asmeq("%1", "r0")
+ __asmeq("%2", "r1")
+ __asmeq("%3", "r2")
+ __asmeq("%4", "r3")
+#ifdef REQUIRES_SEC
+ ".arch_extension sec\n"
+#endif
+ "smc #0 @ switch to secure world\n"
+ : "=r" (r0)
+ : "r" (r0), "r" (r1), "r" (r2), "r" (r3)
+ );
+ return r0;
+}
+
u32 qcom_scm_get_version(void)
{
int context_id;
@@ -378,22 +338,6 @@ u32 qcom_scm_get_version(void)
}
EXPORT_SYMBOL(qcom_scm_get_version);
-/*
- * Set the cold/warm boot address for one of the CPU cores.
- */
-static int qcom_scm_set_boot_addr(u32 addr, int flags)
-{
- struct {
- __le32 flags;
- __le32 addr;
- } cmd;
-
- cmd.addr = cpu_to_le32(addr);
- cmd.flags = cpu_to_le32(flags);
- return qcom_scm_call(QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR,
- &cmd, sizeof(cmd), NULL, 0);
-}
-
/**
* qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus
* @entry: Entry point function for the cpus
@@ -423,7 +367,8 @@ int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus)
set_cpu_present(cpu, false);
}
- return qcom_scm_set_boot_addr(virt_to_phys(entry), flags);
+ return qcom_scm_call_atomic2(QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR,
+ flags, virt_to_phys(entry));
}
/**
@@ -434,11 +379,16 @@ int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus)
* Set the Linux entry point for the SCM to transfer control to when coming
* out of a power down. CPU power down may be executed on cpuidle or hotplug.
*/
-int __qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus)
+int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry,
+ const cpumask_t *cpus)
{
int ret;
int flags = 0;
int cpu;
+ struct {
+ __le32 flags;
+ __le32 addr;
+ } cmd;
/*
* Reassign only if we are switching from hotplug entry point
@@ -454,7 +404,10 @@ int __qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus)
if (!flags)
return 0;
- ret = qcom_scm_set_boot_addr(virt_to_phys(entry), flags);
+ cmd.addr = cpu_to_le32(virt_to_phys(entry));
+ cmd.flags = cpu_to_le32(flags);
+ ret = qcom_scm_call(dev, QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR,
+ &cmd, sizeof(cmd), NULL, 0);
if (!ret) {
for_each_cpu(cpu, cpus)
qcom_scm_wb[cpu].entry = entry;
@@ -477,25 +430,133 @@ void __qcom_scm_cpu_power_down(u32 flags)
flags & QCOM_SCM_FLUSH_FLAG_MASK);
}
-int __qcom_scm_is_call_available(u32 svc_id, u32 cmd_id)
+int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, u32 cmd_id)
{
int ret;
__le32 svc_cmd = cpu_to_le32((svc_id << 10) | cmd_id);
__le32 ret_val = 0;
- ret = qcom_scm_call(QCOM_SCM_SVC_INFO, QCOM_IS_CALL_AVAIL_CMD, &svc_cmd,
- sizeof(svc_cmd), &ret_val, sizeof(ret_val));
+ ret = qcom_scm_call(dev, QCOM_SCM_SVC_INFO, QCOM_IS_CALL_AVAIL_CMD,
+ &svc_cmd, sizeof(svc_cmd), &ret_val,
+ sizeof(ret_val));
if (ret)
return ret;
return le32_to_cpu(ret_val);
}
-int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp)
+int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req,
+ u32 req_cnt, u32 *resp)
{
if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT)
return -ERANGE;
- return qcom_scm_call(QCOM_SCM_SVC_HDCP, QCOM_SCM_CMD_HDCP,
+ return qcom_scm_call(dev, QCOM_SCM_SVC_HDCP, QCOM_SCM_CMD_HDCP,
req, req_cnt * sizeof(*req), resp, sizeof(*resp));
}
+
+void __qcom_scm_init(void)
+{
+}
+
+bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral)
+{
+ __le32 out;
+ __le32 in;
+ int ret;
+
+ in = cpu_to_le32(peripheral);
+ ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
+ QCOM_SCM_PAS_IS_SUPPORTED_CMD,
+ &in, sizeof(in),
+ &out, sizeof(out));
+
+ return ret ? false : !!out;
+}
+
+int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral,
+ dma_addr_t metadata_phys)
+{
+ __le32 scm_ret;
+ int ret;
+ struct {
+ __le32 proc;
+ __le32 image_addr;
+ } request;
+
+ request.proc = cpu_to_le32(peripheral);
+ request.image_addr = cpu_to_le32(metadata_phys);
+
+ ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
+ QCOM_SCM_PAS_INIT_IMAGE_CMD,
+ &request, sizeof(request),
+ &scm_ret, sizeof(scm_ret));
+
+ return ret ? : le32_to_cpu(scm_ret);
+}
+
+int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral,
+ phys_addr_t addr, phys_addr_t size)
+{
+ __le32 scm_ret;
+ int ret;
+ struct {
+ __le32 proc;
+ __le32 addr;
+ __le32 len;
+ } request;
+
+ request.proc = cpu_to_le32(peripheral);
+ request.addr = cpu_to_le32(addr);
+ request.len = cpu_to_le32(size);
+
+ ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
+ QCOM_SCM_PAS_MEM_SETUP_CMD,
+ &request, sizeof(request),
+ &scm_ret, sizeof(scm_ret));
+
+ return ret ? : le32_to_cpu(scm_ret);
+}
+
+int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral)
+{
+ __le32 out;
+ __le32 in;
+ int ret;
+
+ in = cpu_to_le32(peripheral);
+ ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
+ QCOM_SCM_PAS_AUTH_AND_RESET_CMD,
+ &in, sizeof(in),
+ &out, sizeof(out));
+
+ return ret ? : le32_to_cpu(out);
+}
+
+int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral)
+{
+ __le32 out;
+ __le32 in;
+ int ret;
+
+ in = cpu_to_le32(peripheral);
+ ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
+ QCOM_SCM_PAS_SHUTDOWN_CMD,
+ &in, sizeof(in),
+ &out, sizeof(out));
+
+ return ret ? : le32_to_cpu(out);
+}
+
+int __qcom_scm_pas_mss_reset(struct device *dev, bool reset)
+{
+ __le32 out;
+ __le32 in = cpu_to_le32(reset);
+ int ret;
+
+ ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_MSS_RESET,
+ &in, sizeof(in),
+ &out, sizeof(out));
+
+ return ret ? : le32_to_cpu(out);
+}
diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c
index bb6555f6d63b..4a0f5ead4fb5 100644
--- a/drivers/firmware/qcom_scm-64.c
+++ b/drivers/firmware/qcom_scm-64.c
@@ -12,7 +12,150 @@
#include <linux/io.h>
#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/types.h>
#include <linux/qcom_scm.h>
+#include <linux/arm-smccc.h>
+#include <linux/dma-mapping.h>
+
+#include "qcom_scm.h"
+
+#define QCOM_SCM_FNID(s, c) ((((s) & 0xFF) << 8) | ((c) & 0xFF))
+
+#define MAX_QCOM_SCM_ARGS 10
+#define MAX_QCOM_SCM_RETS 3
+
+enum qcom_scm_arg_types {
+ QCOM_SCM_VAL,
+ QCOM_SCM_RO,
+ QCOM_SCM_RW,
+ QCOM_SCM_BUFVAL,
+};
+
+#define QCOM_SCM_ARGS_IMPL(num, a, b, c, d, e, f, g, h, i, j, ...) (\
+ (((a) & 0x3) << 4) | \
+ (((b) & 0x3) << 6) | \
+ (((c) & 0x3) << 8) | \
+ (((d) & 0x3) << 10) | \
+ (((e) & 0x3) << 12) | \
+ (((f) & 0x3) << 14) | \
+ (((g) & 0x3) << 16) | \
+ (((h) & 0x3) << 18) | \
+ (((i) & 0x3) << 20) | \
+ (((j) & 0x3) << 22) | \
+ ((num) & 0xf))
+
+#define QCOM_SCM_ARGS(...) QCOM_SCM_ARGS_IMPL(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
+
+/**
+ * struct qcom_scm_desc
+ * @arginfo: Metadata describing the arguments in args[]
+ * @args: The array of arguments for the secure syscall
+ * @res: The values returned by the secure syscall
+ */
+struct qcom_scm_desc {
+ u32 arginfo;
+ u64 args[MAX_QCOM_SCM_ARGS];
+};
+
+static u64 qcom_smccc_convention = -1;
+static DEFINE_MUTEX(qcom_scm_lock);
+
+#define QCOM_SCM_EBUSY_WAIT_MS 30
+#define QCOM_SCM_EBUSY_MAX_RETRY 20
+
+#define N_EXT_QCOM_SCM_ARGS 7
+#define FIRST_EXT_ARG_IDX 3
+#define N_REGISTER_ARGS (MAX_QCOM_SCM_ARGS - N_EXT_QCOM_SCM_ARGS + 1)
+
+/**
+ * qcom_scm_call() - Invoke a syscall in the secure world
+ * @dev: device
+ * @svc_id: service identifier
+ * @cmd_id: command identifier
+ * @desc: Descriptor structure containing arguments and return values
+ *
+ * Sends a command to the SCM and waits for the command to finish processing.
+ * This should *only* be called in pre-emptible context.
+*/
+static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id,
+ const struct qcom_scm_desc *desc,
+ struct arm_smccc_res *res)
+{
+ int arglen = desc->arginfo & 0xf;
+ int retry_count = 0, i;
+ u32 fn_id = QCOM_SCM_FNID(svc_id, cmd_id);
+ u64 cmd, x5 = desc->args[FIRST_EXT_ARG_IDX];
+ dma_addr_t args_phys = 0;
+ void *args_virt = NULL;
+ size_t alloc_len;
+
+ if (unlikely(arglen > N_REGISTER_ARGS)) {
+ alloc_len = N_EXT_QCOM_SCM_ARGS * sizeof(u64);
+ args_virt = kzalloc(PAGE_ALIGN(alloc_len), GFP_KERNEL);
+
+ if (!args_virt)
+ return -ENOMEM;
+
+ if (qcom_smccc_convention == ARM_SMCCC_SMC_32) {
+ __le32 *args = args_virt;
+
+ for (i = 0; i < N_EXT_QCOM_SCM_ARGS; i++)
+ args[i] = cpu_to_le32(desc->args[i +
+ FIRST_EXT_ARG_IDX]);
+ } else {
+ __le64 *args = args_virt;
+
+ for (i = 0; i < N_EXT_QCOM_SCM_ARGS; i++)
+ args[i] = cpu_to_le64(desc->args[i +
+ FIRST_EXT_ARG_IDX]);
+ }
+
+ args_phys = dma_map_single(dev, args_virt, alloc_len,
+ DMA_TO_DEVICE);
+
+ if (dma_mapping_error(dev, args_phys)) {
+ kfree(args_virt);
+ return -ENOMEM;
+ }
+
+ x5 = args_phys;
+ }
+
+ do {
+ mutex_lock(&qcom_scm_lock);
+
+ cmd = ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL,
+ qcom_smccc_convention,
+ ARM_SMCCC_OWNER_SIP, fn_id);
+
+ do {
+ arm_smccc_smc(cmd, desc->arginfo, desc->args[0],
+ desc->args[1], desc->args[2], x5, 0, 0,
+ res);
+ } while (res->a0 == QCOM_SCM_INTERRUPTED);
+
+ mutex_unlock(&qcom_scm_lock);
+
+ if (res->a0 == QCOM_SCM_V2_EBUSY) {
+ if (retry_count++ > QCOM_SCM_EBUSY_MAX_RETRY)
+ break;
+ msleep(QCOM_SCM_EBUSY_WAIT_MS);
+ }
+ } while (res->a0 == QCOM_SCM_V2_EBUSY);
+
+ if (args_virt) {
+ dma_unmap_single(dev, args_phys, alloc_len, DMA_TO_DEVICE);
+ kfree(args_virt);
+ }
+
+ if (res->a0 < 0)
+ return qcom_scm_remap_error(res->a0);
+
+ return 0;
+}
/**
* qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus
@@ -29,13 +172,15 @@ int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus)
/**
* qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus
+ * @dev: Device pointer
* @entry: Entry point function for the cpus
* @cpus: The cpumask of cpus that will use the entry point
*
* Set the Linux entry point for the SCM to transfer control to when coming
* out of a power down. CPU power down may be executed on cpuidle or hotplug.
*/
-int __qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus)
+int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry,
+ const cpumask_t *cpus)
{
return -ENOTSUPP;
}
@@ -52,12 +197,164 @@ void __qcom_scm_cpu_power_down(u32 flags)
{
}
-int __qcom_scm_is_call_available(u32 svc_id, u32 cmd_id)
+int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, u32 cmd_id)
{
- return -ENOTSUPP;
+ int ret;
+ struct qcom_scm_desc desc = {0};
+ struct arm_smccc_res res;
+
+ desc.arginfo = QCOM_SCM_ARGS(1);
+ desc.args[0] = QCOM_SCM_FNID(svc_id, cmd_id) |
+ (ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT);
+
+ ret = qcom_scm_call(dev, QCOM_SCM_SVC_INFO, QCOM_IS_CALL_AVAIL_CMD,
+ &desc, &res);
+
+ return ret ? : res.a1;
}
-int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp)
+int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req,
+ u32 req_cnt, u32 *resp)
{
- return -ENOTSUPP;
+ int ret;
+ struct qcom_scm_desc desc = {0};
+ struct arm_smccc_res res;
+
+ if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT)
+ return -ERANGE;
+
+ desc.args[0] = req[0].addr;
+ desc.args[1] = req[0].val;
+ desc.args[2] = req[1].addr;
+ desc.args[3] = req[1].val;
+ desc.args[4] = req[2].addr;
+ desc.args[5] = req[2].val;
+ desc.args[6] = req[3].addr;
+ desc.args[7] = req[3].val;
+ desc.args[8] = req[4].addr;
+ desc.args[9] = req[4].val;
+ desc.arginfo = QCOM_SCM_ARGS(10);
+
+ ret = qcom_scm_call(dev, QCOM_SCM_SVC_HDCP, QCOM_SCM_CMD_HDCP, &desc,
+ &res);
+ *resp = res.a1;
+
+ return ret;
+}
+
+void __qcom_scm_init(void)
+{
+ u64 cmd;
+ struct arm_smccc_res res;
+ u32 function = QCOM_SCM_FNID(QCOM_SCM_SVC_INFO, QCOM_IS_CALL_AVAIL_CMD);
+
+ /* First try a SMC64 call */
+ cmd = ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64,
+ ARM_SMCCC_OWNER_SIP, function);
+
+ arm_smccc_smc(cmd, QCOM_SCM_ARGS(1), cmd & (~BIT(ARM_SMCCC_TYPE_SHIFT)),
+ 0, 0, 0, 0, 0, &res);
+
+ if (!res.a0 && res.a1)
+ qcom_smccc_convention = ARM_SMCCC_SMC_64;
+ else
+ qcom_smccc_convention = ARM_SMCCC_SMC_32;
+}
+
+bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral)
+{
+ int ret;
+ struct qcom_scm_desc desc = {0};
+ struct arm_smccc_res res;
+
+ desc.args[0] = peripheral;
+ desc.arginfo = QCOM_SCM_ARGS(1);
+
+ ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
+ QCOM_SCM_PAS_IS_SUPPORTED_CMD,
+ &desc, &res);
+
+ return ret ? false : !!res.a1;
+}
+
+int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral,
+ dma_addr_t metadata_phys)
+{
+ int ret;
+ struct qcom_scm_desc desc = {0};
+ struct arm_smccc_res res;
+
+ desc.args[0] = peripheral;
+ desc.args[1] = metadata_phys;
+ desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW);
+
+ ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_INIT_IMAGE_CMD,
+ &desc, &res);
+
+ return ret ? : res.a1;
+}
+
+int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral,
+ phys_addr_t addr, phys_addr_t size)
+{
+ int ret;
+ struct qcom_scm_desc desc = {0};
+ struct arm_smccc_res res;
+
+ desc.args[0] = peripheral;
+ desc.args[1] = addr;
+ desc.args[2] = size;
+ desc.arginfo = QCOM_SCM_ARGS(3);
+
+ ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_MEM_SETUP_CMD,
+ &desc, &res);
+
+ return ret ? : res.a1;
+}
+
+int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral)
+{
+ int ret;
+ struct qcom_scm_desc desc = {0};
+ struct arm_smccc_res res;
+
+ desc.args[0] = peripheral;
+ desc.arginfo = QCOM_SCM_ARGS(1);
+
+ ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
+ QCOM_SCM_PAS_AUTH_AND_RESET_CMD,
+ &desc, &res);
+
+ return ret ? : res.a1;
+}
+
+int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral)
+{
+ int ret;
+ struct qcom_scm_desc desc = {0};
+ struct arm_smccc_res res;
+
+ desc.args[0] = peripheral;
+ desc.arginfo = QCOM_SCM_ARGS(1);
+
+ ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_SHUTDOWN_CMD,
+ &desc, &res);
+
+ return ret ? : res.a1;
+}
+
+int __qcom_scm_pas_mss_reset(struct device *dev, bool reset)
+{
+ struct qcom_scm_desc desc = {0};
+ struct arm_smccc_res res;
+ int ret;
+
+ desc.args[0] = reset;
+ desc.args[1] = 0;
+ desc.arginfo = QCOM_SCM_ARGS(2);
+
+ ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_MSS_RESET, &desc,
+ &res);
+
+ return ret ? : res.a1;
}
diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c
index 45c008d68891..84330c5f05d0 100644
--- a/drivers/firmware/qcom_scm.c
+++ b/drivers/firmware/qcom_scm.c
@@ -10,19 +10,64 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
*/
-
+#include <linux/platform_device.h>
+#include <linux/module.h>
#include <linux/cpumask.h>
#include <linux/export.h>
+#include <linux/dma-mapping.h>
#include <linux/types.h>
#include <linux/qcom_scm.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/clk.h>
+#include <linux/reset-controller.h>
#include "qcom_scm.h"
+struct qcom_scm {
+ struct device *dev;
+ struct clk *core_clk;
+ struct clk *iface_clk;
+ struct clk *bus_clk;
+ struct reset_controller_dev reset;
+};
+
+static struct qcom_scm *__scm;
+
+static int qcom_scm_clk_enable(void)
+{
+ int ret;
+
+ ret = clk_prepare_enable(__scm->core_clk);
+ if (ret)
+ goto bail;
+
+ ret = clk_prepare_enable(__scm->iface_clk);
+ if (ret)
+ goto disable_core;
+
+ ret = clk_prepare_enable(__scm->bus_clk);
+ if (ret)
+ goto disable_iface;
+
+ return 0;
+
+disable_iface:
+ clk_disable_unprepare(__scm->iface_clk);
+disable_core:
+ clk_disable_unprepare(__scm->core_clk);
+bail:
+ return ret;
+}
+
+static void qcom_scm_clk_disable(void)
+{
+ clk_disable_unprepare(__scm->core_clk);
+ clk_disable_unprepare(__scm->iface_clk);
+ clk_disable_unprepare(__scm->bus_clk);
+}
+
/**
* qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus
* @entry: Entry point function for the cpus
@@ -47,7 +92,7 @@ EXPORT_SYMBOL(qcom_scm_set_cold_boot_addr);
*/
int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus)
{
- return __qcom_scm_set_warm_boot_addr(entry, cpus);
+ return __qcom_scm_set_warm_boot_addr(__scm->dev, entry, cpus);
}
EXPORT_SYMBOL(qcom_scm_set_warm_boot_addr);
@@ -72,12 +117,17 @@ EXPORT_SYMBOL(qcom_scm_cpu_power_down);
*/
bool qcom_scm_hdcp_available(void)
{
- int ret;
+ int ret = qcom_scm_clk_enable();
+
+ if (ret)
+ return ret;
- ret = __qcom_scm_is_call_available(QCOM_SCM_SVC_HDCP,
- QCOM_SCM_CMD_HDCP);
+ ret = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_HDCP,
+ QCOM_SCM_CMD_HDCP);
- return (ret > 0) ? true : false;
+ qcom_scm_clk_disable();
+
+ return ret > 0 ? true : false;
}
EXPORT_SYMBOL(qcom_scm_hdcp_available);
@@ -91,6 +141,279 @@ EXPORT_SYMBOL(qcom_scm_hdcp_available);
*/
int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp)
{
- return __qcom_scm_hdcp_req(req, req_cnt, resp);
+ int ret = qcom_scm_clk_enable();
+
+ if (ret)
+ return ret;
+
+ ret = __qcom_scm_hdcp_req(__scm->dev, req, req_cnt, resp);
+ qcom_scm_clk_disable();
+ return ret;
}
EXPORT_SYMBOL(qcom_scm_hdcp_req);
+
+/**
+ * qcom_scm_pas_supported() - Check if the peripheral authentication service is
+ * available for the given peripherial
+ * @peripheral: peripheral id
+ *
+ * Returns true if PAS is supported for this peripheral, otherwise false.
+ */
+bool qcom_scm_pas_supported(u32 peripheral)
+{
+ int ret;
+
+ ret = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
+ QCOM_SCM_PAS_IS_SUPPORTED_CMD);
+ if (ret <= 0)
+ return false;
+
+ return __qcom_scm_pas_supported(__scm->dev, peripheral);
+}
+EXPORT_SYMBOL(qcom_scm_pas_supported);
+
+/**
+ * qcom_scm_pas_init_image() - Initialize peripheral authentication service
+ * state machine for a given peripheral, using the
+ * metadata
+ * @peripheral: peripheral id
+ * @metadata: pointer to memory containing ELF header, program header table
+ * and optional blob of data used for authenticating the metadata
+ * and the rest of the firmware
+ * @size: size of the metadata
+ *
+ * Returns 0 on success.
+ */
+int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size)
+{
+ dma_addr_t mdata_phys;
+ void *mdata_buf;
+ int ret;
+
+ /*
+ * During the scm call memory protection will be enabled for the meta
+ * data blob, so make sure it's physically contiguous, 4K aligned and
+ * non-cachable to avoid XPU violations.
+ */
+ mdata_buf = dma_alloc_coherent(__scm->dev, size, &mdata_phys,
+ GFP_KERNEL);
+ if (!mdata_buf) {
+ dev_err(__scm->dev, "Allocation of metadata buffer failed.\n");
+ return -ENOMEM;
+ }
+ memcpy(mdata_buf, metadata, size);
+
+ ret = qcom_scm_clk_enable();
+ if (ret)
+ goto free_metadata;
+
+ ret = __qcom_scm_pas_init_image(__scm->dev, peripheral, mdata_phys);
+
+ qcom_scm_clk_disable();
+
+free_metadata:
+ dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys);
+
+ return ret;
+}
+EXPORT_SYMBOL(qcom_scm_pas_init_image);
+
+/**
+ * qcom_scm_pas_mem_setup() - Prepare the memory related to a given peripheral
+ * for firmware loading
+ * @peripheral: peripheral id
+ * @addr: start address of memory area to prepare
+ * @size: size of the memory area to prepare
+ *
+ * Returns 0 on success.
+ */
+int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size)
+{
+ int ret;
+
+ ret = qcom_scm_clk_enable();
+ if (ret)
+ return ret;
+
+ ret = __qcom_scm_pas_mem_setup(__scm->dev, peripheral, addr, size);
+ qcom_scm_clk_disable();
+
+ return ret;
+}
+EXPORT_SYMBOL(qcom_scm_pas_mem_setup);
+
+/**
+ * qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware
+ * and reset the remote processor
+ * @peripheral: peripheral id
+ *
+ * Return 0 on success.
+ */
+int qcom_scm_pas_auth_and_reset(u32 peripheral)
+{
+ int ret;
+
+ ret = qcom_scm_clk_enable();
+ if (ret)
+ return ret;
+
+ ret = __qcom_scm_pas_auth_and_reset(__scm->dev, peripheral);
+ qcom_scm_clk_disable();
+
+ return ret;
+}
+EXPORT_SYMBOL(qcom_scm_pas_auth_and_reset);
+
+/**
+ * qcom_scm_pas_shutdown() - Shut down the remote processor
+ * @peripheral: peripheral id
+ *
+ * Returns 0 on success.
+ */
+int qcom_scm_pas_shutdown(u32 peripheral)
+{
+ int ret;
+
+ ret = qcom_scm_clk_enable();
+ if (ret)
+ return ret;
+
+ ret = __qcom_scm_pas_shutdown(__scm->dev, peripheral);
+ qcom_scm_clk_disable();
+
+ return ret;
+}
+EXPORT_SYMBOL(qcom_scm_pas_shutdown);
+
+static int qcom_scm_pas_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ if (idx != 0)
+ return -EINVAL;
+
+ return __qcom_scm_pas_mss_reset(__scm->dev, 1);
+}
+
+static int qcom_scm_pas_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long idx)
+{
+ if (idx != 0)
+ return -EINVAL;
+
+ return __qcom_scm_pas_mss_reset(__scm->dev, 0);
+}
+
+static const struct reset_control_ops qcom_scm_pas_reset_ops = {
+ .assert = qcom_scm_pas_reset_assert,
+ .deassert = qcom_scm_pas_reset_deassert,
+};
+
+
+static int qcom_scm_probe(struct platform_device *pdev)
+{
+ struct qcom_scm *scm;
+ int ret;
+
+ scm = devm_kzalloc(&pdev->dev, sizeof(*scm), GFP_KERNEL);
+ if (!scm)
+ return -ENOMEM;
+
+ scm->core_clk = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(scm->core_clk)) {
+ if (PTR_ERR(scm->core_clk) == -EPROBE_DEFER)
+ return PTR_ERR(scm->core_clk);
+
+ scm->core_clk = NULL;
+ }
+
+ if (of_device_is_compatible(pdev->dev.of_node, "qcom,scm")) {
+ scm->iface_clk = devm_clk_get(&pdev->dev, "iface");
+ if (IS_ERR(scm->iface_clk)) {
+ if (PTR_ERR(scm->iface_clk) != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "failed to acquire iface clk\n");
+ return PTR_ERR(scm->iface_clk);
+ }
+
+ scm->bus_clk = devm_clk_get(&pdev->dev, "bus");
+ if (IS_ERR(scm->bus_clk)) {
+ if (PTR_ERR(scm->bus_clk) != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "failed to acquire bus clk\n");
+ return PTR_ERR(scm->bus_clk);
+ }
+ }
+
+ scm->reset.ops = &qcom_scm_pas_reset_ops;
+ scm->reset.nr_resets = 1;
+ scm->reset.of_node = pdev->dev.of_node;
+ reset_controller_register(&scm->reset);
+
+ /* vote for max clk rate for highest performance */
+ ret = clk_set_rate(scm->core_clk, INT_MAX);
+ if (ret)
+ return ret;
+
+ __scm = scm;
+ __scm->dev = &pdev->dev;
+
+ __qcom_scm_init();
+
+ return 0;
+}
+
+static const struct of_device_id qcom_scm_dt_match[] = {
+ { .compatible = "qcom,scm-apq8064",},
+ { .compatible = "qcom,scm-msm8660",},
+ { .compatible = "qcom,scm-msm8960",},
+ { .compatible = "qcom,scm",},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, qcom_scm_dt_match);
+
+static struct platform_driver qcom_scm_driver = {
+ .driver = {
+ .name = "qcom_scm",
+ .of_match_table = qcom_scm_dt_match,
+ },
+ .probe = qcom_scm_probe,
+};
+
+static int __init qcom_scm_init(void)
+{
+ struct device_node *np, *fw_np;
+ int ret;
+
+ fw_np = of_find_node_by_name(NULL, "firmware");
+
+ if (!fw_np)
+ return -ENODEV;
+
+ np = of_find_matching_node(fw_np, qcom_scm_dt_match);
+
+ if (!np) {
+ of_node_put(fw_np);
+ return -ENODEV;
+ }
+
+ of_node_put(np);
+
+ ret = of_platform_populate(fw_np, qcom_scm_dt_match, NULL, NULL);
+
+ of_node_put(fw_np);
+
+ if (ret)
+ return ret;
+
+ return platform_driver_register(&qcom_scm_driver);
+}
+
+arch_initcall(qcom_scm_init);
+
+static void __exit qcom_scm_exit(void)
+{
+ platform_driver_unregister(&qcom_scm_driver);
+}
+module_exit(qcom_scm_exit);
+
+MODULE_DESCRIPTION("Qualcomm SCM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h
index 2cce75c08b99..3584b00fe7e6 100644
--- a/drivers/firmware/qcom_scm.h
+++ b/drivers/firmware/qcom_scm.h
@@ -19,7 +19,8 @@
#define QCOM_SCM_FLAG_HLOS 0x01
#define QCOM_SCM_FLAG_COLDBOOT_MC 0x02
#define QCOM_SCM_FLAG_WARMBOOT_MC 0x04
-extern int __qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus);
+extern int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry,
+ const cpumask_t *cpus);
extern int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus);
#define QCOM_SCM_CMD_TERMINATE_PC 0x2
@@ -29,14 +30,34 @@ extern void __qcom_scm_cpu_power_down(u32 flags);
#define QCOM_SCM_SVC_INFO 0x6
#define QCOM_IS_CALL_AVAIL_CMD 0x1
-extern int __qcom_scm_is_call_available(u32 svc_id, u32 cmd_id);
+extern int __qcom_scm_is_call_available(struct device *dev, u32 svc_id,
+ u32 cmd_id);
#define QCOM_SCM_SVC_HDCP 0x11
#define QCOM_SCM_CMD_HDCP 0x01
-extern int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt,
- u32 *resp);
+extern int __qcom_scm_hdcp_req(struct device *dev,
+ struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp);
+
+extern void __qcom_scm_init(void);
+
+#define QCOM_SCM_SVC_PIL 0x2
+#define QCOM_SCM_PAS_INIT_IMAGE_CMD 0x1
+#define QCOM_SCM_PAS_MEM_SETUP_CMD 0x2
+#define QCOM_SCM_PAS_AUTH_AND_RESET_CMD 0x5
+#define QCOM_SCM_PAS_SHUTDOWN_CMD 0x6
+#define QCOM_SCM_PAS_IS_SUPPORTED_CMD 0x7
+#define QCOM_SCM_PAS_MSS_RESET 0xa
+extern bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral);
+extern int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral,
+ dma_addr_t metadata_phys);
+extern int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral,
+ phys_addr_t addr, phys_addr_t size);
+extern int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral);
+extern int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral);
+extern int __qcom_scm_pas_mss_reset(struct device *dev, bool reset);
/* common error codes */
+#define QCOM_SCM_V2_EBUSY -12
#define QCOM_SCM_ENOMEM -5
#define QCOM_SCM_EOPNOTSUPP -4
#define QCOM_SCM_EINVAL_ADDR -3
@@ -44,4 +65,22 @@ extern int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt,
#define QCOM_SCM_ERROR -1
#define QCOM_SCM_INTERRUPTED 1
+static inline int qcom_scm_remap_error(int err)
+{
+ switch (err) {
+ case QCOM_SCM_ERROR:
+ return -EIO;
+ case QCOM_SCM_EINVAL_ADDR:
+ case QCOM_SCM_EINVAL_ARG:
+ return -EINVAL;
+ case QCOM_SCM_EOPNOTSUPP:
+ return -EOPNOTSUPP;
+ case QCOM_SCM_ENOMEM:
+ return -ENOMEM;
+ case QCOM_SCM_V2_EBUSY:
+ return -EBUSY;
+ }
+ return -EINVAL;
+}
+
#endif
diff --git a/drivers/firmware/scpi_pm_domain.c b/drivers/firmware/scpi_pm_domain.c
new file mode 100644
index 000000000000..f395dec27113
--- /dev/null
+++ b/drivers/firmware/scpi_pm_domain.c
@@ -0,0 +1,163 @@
+/*
+ * SCPI Generic power domain support.
+ *
+ * Copyright (C) 2016 ARM Ltd.
+ *
+ * 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/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/pm_domain.h>
+#include <linux/scpi_protocol.h>
+
+struct scpi_pm_domain {
+ struct generic_pm_domain genpd;
+ struct scpi_ops *ops;
+ u32 domain;
+ char name[30];
+};
+
+/*
+ * These device power state values are not well-defined in the specification.
+ * In case, different implementations use different values, we can make these
+ * specific to compatibles rather than getting these values from device tree.
+ */
+enum scpi_power_domain_state {
+ SCPI_PD_STATE_ON = 0,
+ SCPI_PD_STATE_OFF = 3,
+};
+
+#define to_scpi_pd(gpd) container_of(gpd, struct scpi_pm_domain, genpd)
+
+static int scpi_pd_power(struct scpi_pm_domain *pd, bool power_on)
+{
+ int ret;
+ enum scpi_power_domain_state state;
+
+ if (power_on)
+ state = SCPI_PD_STATE_ON;
+ else
+ state = SCPI_PD_STATE_OFF;
+
+ ret = pd->ops->device_set_power_state(pd->domain, state);
+ if (ret)
+ return ret;
+
+ return !(state == pd->ops->device_get_power_state(pd->domain));
+}
+
+static int scpi_pd_power_on(struct generic_pm_domain *domain)
+{
+ struct scpi_pm_domain *pd = to_scpi_pd(domain);
+
+ return scpi_pd_power(pd, true);
+}
+
+static int scpi_pd_power_off(struct generic_pm_domain *domain)
+{
+ struct scpi_pm_domain *pd = to_scpi_pd(domain);
+
+ return scpi_pd_power(pd, false);
+}
+
+static int scpi_pm_domain_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct scpi_pm_domain *scpi_pd;
+ struct genpd_onecell_data *scpi_pd_data;
+ struct generic_pm_domain **domains;
+ struct scpi_ops *scpi_ops;
+ int ret, num_domains, i;
+
+ scpi_ops = get_scpi_ops();
+ if (!scpi_ops)
+ return -EPROBE_DEFER;
+
+ if (!np) {
+ dev_err(dev, "device tree node not found\n");
+ return -ENODEV;
+ }
+
+ if (!scpi_ops->device_set_power_state ||
+ !scpi_ops->device_get_power_state) {
+ dev_err(dev, "power domains not supported in the firmware\n");
+ return -ENODEV;
+ }
+
+ ret = of_property_read_u32(np, "num-domains", &num_domains);
+ if (ret) {
+ dev_err(dev, "number of domains not found\n");
+ return -EINVAL;
+ }
+
+ scpi_pd = devm_kcalloc(dev, num_domains, sizeof(*scpi_pd), GFP_KERNEL);
+ if (!scpi_pd)
+ return -ENOMEM;
+
+ scpi_pd_data = devm_kzalloc(dev, sizeof(*scpi_pd_data), GFP_KERNEL);
+ if (!scpi_pd_data)
+ return -ENOMEM;
+
+ domains = devm_kcalloc(dev, num_domains, sizeof(*domains), GFP_KERNEL);
+ if (!domains)
+ return -ENOMEM;
+
+ for (i = 0; i < num_domains; i++, scpi_pd++) {
+ domains[i] = &scpi_pd->genpd;
+
+ scpi_pd->domain = i;
+ scpi_pd->ops = scpi_ops;
+ sprintf(scpi_pd->name, "%s.%d", np->name, i);
+ scpi_pd->genpd.name = scpi_pd->name;
+ scpi_pd->genpd.power_off = scpi_pd_power_off;
+ scpi_pd->genpd.power_on = scpi_pd_power_on;
+
+ /*
+ * Treat all power domains as off at boot.
+ *
+ * The SCP firmware itself may have switched on some domains,
+ * but for reference counting purpose, keep it this way.
+ */
+ pm_genpd_init(&scpi_pd->genpd, NULL, true);
+ }
+
+ scpi_pd_data->domains = domains;
+ scpi_pd_data->num_domains = num_domains;
+
+ of_genpd_add_provider_onecell(np, scpi_pd_data);
+
+ return 0;
+}
+
+static const struct of_device_id scpi_power_domain_ids[] = {
+ { .compatible = "arm,scpi-power-domains", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, scpi_power_domain_ids);
+
+static struct platform_driver scpi_power_domain_driver = {
+ .driver = {
+ .name = "scpi_power_domain",
+ .of_match_table = scpi_power_domain_ids,
+ },
+ .probe = scpi_pm_domain_probe,
+};
+module_platform_driver(scpi_power_domain_driver);
+
+MODULE_AUTHOR("Sudeep Holla <[email protected]>");
+MODULE_DESCRIPTION("ARM SCPI power domain driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/keyboard/clps711x-keypad.c b/drivers/input/keyboard/clps711x-keypad.c
index b637f1af842e..997e3e97f573 100644
--- a/drivers/input/keyboard/clps711x-keypad.c
+++ b/drivers/input/keyboard/clps711x-keypad.c
@@ -101,7 +101,7 @@ static int clps711x_keypad_probe(struct platform_device *pdev)
return -ENOMEM;
priv->syscon =
- syscon_regmap_lookup_by_compatible("cirrus,clps711x-syscon1");
+ syscon_regmap_lookup_by_compatible("cirrus,ep7209-syscon1");
if (IS_ERR(priv->syscon))
return PTR_ERR(priv->syscon);
@@ -181,7 +181,7 @@ static int clps711x_keypad_remove(struct platform_device *pdev)
}
static const struct of_device_id clps711x_keypad_of_match[] = {
- { .compatible = "cirrus,clps711x-keypad", },
+ { .compatible = "cirrus,ep7209-keypad", },
{ }
};
MODULE_DEVICE_TABLE(of, clps711x_keypad_of_match);
diff --git a/drivers/irqchip/irq-clps711x.c b/drivers/irqchip/irq-clps711x.c
index 2223b3f15d68..f913f4db7ae1 100644
--- a/drivers/irqchip/irq-clps711x.c
+++ b/drivers/irqchip/irq-clps711x.c
@@ -234,5 +234,5 @@ static int __init clps711x_intc_init_dt(struct device_node *np,
return _clps711x_intc_init(np, res.start, resource_size(&res));
}
-IRQCHIP_DECLARE(clps711x, "cirrus,clps711x-intc", clps711x_intc_init_dt);
+IRQCHIP_DECLARE(clps711x, "cirrus,ep7209-intc", clps711x_intc_init_dt);
#endif
diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c
index c04973669a47..c9d2009c2476 100644
--- a/drivers/media/platform/exynos-gsc/gsc-core.c
+++ b/drivers/media/platform/exynos-gsc/gsc-core.c
@@ -1124,6 +1124,7 @@ static int gsc_probe(struct platform_device *pdev)
goto err_m2m;
/* Initialize continious memory allocator */
+ vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32));
gsc->alloc_ctx = vb2_dma_contig_init_ctx(dev);
if (IS_ERR(gsc->alloc_ctx)) {
ret = PTR_ERR(gsc->alloc_ctx);
@@ -1153,6 +1154,7 @@ static int gsc_remove(struct platform_device *pdev)
v4l2_device_unregister(&gsc->v4l2_dev);
vb2_dma_contig_cleanup_ctx(gsc->alloc_ctx);
+ vb2_dma_contig_clear_max_seg_size(&pdev->dev);
pm_runtime_disable(&pdev->dev);
gsc_clk_put(gsc);
diff --git a/drivers/media/platform/exynos4-is/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c
index b1c1cea82a27..368f44f24d4c 100644
--- a/drivers/media/platform/exynos4-is/fimc-core.c
+++ b/drivers/media/platform/exynos4-is/fimc-core.c
@@ -1019,6 +1019,7 @@ static int fimc_probe(struct platform_device *pdev)
}
/* Initialize contiguous memory allocator */
+ vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32));
fimc->alloc_ctx = vb2_dma_contig_init_ctx(dev);
if (IS_ERR(fimc->alloc_ctx)) {
ret = PTR_ERR(fimc->alloc_ctx);
@@ -1124,6 +1125,7 @@ static int fimc_remove(struct platform_device *pdev)
fimc_unregister_capture_subdev(fimc);
vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx);
+ vb2_dma_contig_clear_max_seg_size(&pdev->dev);
clk_disable(fimc->clock[CLK_BUS]);
fimc_clk_put(fimc);
diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c
index 979c388ebf60..bd98b56318b7 100644
--- a/drivers/media/platform/exynos4-is/fimc-is.c
+++ b/drivers/media/platform/exynos4-is/fimc-is.c
@@ -847,6 +847,7 @@ static int fimc_is_probe(struct platform_device *pdev)
if (ret < 0)
goto err_pm;
+ vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32));
is->alloc_ctx = vb2_dma_contig_init_ctx(dev);
if (IS_ERR(is->alloc_ctx)) {
ret = PTR_ERR(is->alloc_ctx);
@@ -940,6 +941,7 @@ static int fimc_is_remove(struct platform_device *pdev)
free_irq(is->irq, is);
fimc_is_unregister_subdevs(is);
vb2_dma_contig_cleanup_ctx(is->alloc_ctx);
+ vb2_dma_contig_clear_max_seg_size(dev);
fimc_is_put_clocks(is);
fimc_is_debugfs_remove(is);
release_firmware(is->fw.f_w);
diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c
index dc1b929f7a33..27cb620cb714 100644
--- a/drivers/media/platform/exynos4-is/fimc-lite.c
+++ b/drivers/media/platform/exynos4-is/fimc-lite.c
@@ -1551,6 +1551,7 @@ static int fimc_lite_probe(struct platform_device *pdev)
goto err_sd;
}
+ vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32));
fimc->alloc_ctx = vb2_dma_contig_init_ctx(dev);
if (IS_ERR(fimc->alloc_ctx)) {
ret = PTR_ERR(fimc->alloc_ctx);
@@ -1652,6 +1653,7 @@ static int fimc_lite_remove(struct platform_device *pdev)
pm_runtime_set_suspended(dev);
fimc_lite_unregister_capture_subdev(fimc);
vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx);
+ vb2_dma_contig_clear_max_seg_size(dev);
fimc_lite_clk_put(fimc);
dev_info(dev, "Driver unloaded\n");
diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c
index 612d1ea514f1..d3e3469db8de 100644
--- a/drivers/media/platform/s5p-g2d/g2d.c
+++ b/drivers/media/platform/s5p-g2d/g2d.c
@@ -681,6 +681,7 @@ static int g2d_probe(struct platform_device *pdev)
goto put_clk_gate;
}
+ vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
if (IS_ERR(dev->alloc_ctx)) {
ret = PTR_ERR(dev->alloc_ctx);
@@ -757,6 +758,7 @@ static int g2d_remove(struct platform_device *pdev)
video_unregister_device(dev->vfd);
v4l2_device_unregister(&dev->v4l2_dev);
vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
+ vb2_dma_contig_clear_max_seg_size(&pdev->dev);
clk_unprepare(dev->gate);
clk_put(dev->gate);
clk_unprepare(dev->clk);
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c
index caa19b408551..17bc94092864 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-core.c
+++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c
@@ -2843,6 +2843,7 @@ static int s5p_jpeg_probe(struct platform_device *pdev)
goto device_register_rollback;
}
+ vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
jpeg->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
if (IS_ERR(jpeg->alloc_ctx)) {
v4l2_err(&jpeg->v4l2_dev, "Failed to init memory allocator\n");
@@ -2942,6 +2943,7 @@ static int s5p_jpeg_remove(struct platform_device *pdev)
video_unregister_device(jpeg->vfd_decoder);
video_unregister_device(jpeg->vfd_encoder);
vb2_dma_contig_cleanup_ctx(jpeg->alloc_ctx);
+ vb2_dma_contig_clear_max_seg_size(&pdev->dev);
v4l2_m2m_release(jpeg->m2m_dev);
v4l2_device_unregister(&jpeg->v4l2_dev);
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c
index b16466fe35ee..6ee620ee8cd5 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c
@@ -22,6 +22,7 @@
#include <media/v4l2-event.h>
#include <linux/workqueue.h>
#include <linux/of.h>
+#include <linux/of_reserved_mem.h>
#include <media/videobuf2-v4l2.h>
#include "s5p_mfc_common.h"
#include "s5p_mfc_ctrl.h"
@@ -29,6 +30,7 @@
#include "s5p_mfc_dec.h"
#include "s5p_mfc_enc.h"
#include "s5p_mfc_intr.h"
+#include "s5p_mfc_iommu.h"
#include "s5p_mfc_opr.h"
#include "s5p_mfc_cmd.h"
#include "s5p_mfc_pm.h"
@@ -1043,55 +1045,94 @@ static const struct v4l2_file_operations s5p_mfc_fops = {
.mmap = s5p_mfc_mmap,
};
-static int match_child(struct device *dev, void *data)
+/* DMA memory related helper functions */
+static void s5p_mfc_memdev_release(struct device *dev)
{
- if (!dev_name(dev))
- return 0;
- return !strcmp(dev_name(dev), (char *)data);
+ of_reserved_mem_device_release(dev);
}
-static void *mfc_get_drv_data(struct platform_device *pdev);
-
-static int s5p_mfc_alloc_memdevs(struct s5p_mfc_dev *dev)
+static struct device *s5p_mfc_alloc_memdev(struct device *dev,
+ const char *name, unsigned int idx)
{
- unsigned int mem_info[2] = { };
+ struct device *child;
+ int ret;
- dev->mem_dev_l = devm_kzalloc(&dev->plat_dev->dev,
- sizeof(struct device), GFP_KERNEL);
- if (!dev->mem_dev_l) {
- mfc_err("Not enough memory\n");
- return -ENOMEM;
- }
- device_initialize(dev->mem_dev_l);
- of_property_read_u32_array(dev->plat_dev->dev.of_node,
- "samsung,mfc-l", mem_info, 2);
- if (dma_declare_coherent_memory(dev->mem_dev_l, mem_info[0],
- mem_info[0], mem_info[1],
- DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) {
- mfc_err("Failed to declare coherent memory for\n"
- "MFC device\n");
- return -ENOMEM;
+ child = devm_kzalloc(dev, sizeof(struct device), GFP_KERNEL);
+ if (!child)
+ return NULL;
+
+ device_initialize(child);
+ dev_set_name(child, "%s:%s", dev_name(dev), name);
+ child->parent = dev;
+ child->bus = dev->bus;
+ child->coherent_dma_mask = dev->coherent_dma_mask;
+ child->dma_mask = dev->dma_mask;
+ child->release = s5p_mfc_memdev_release;
+
+ if (device_add(child) == 0) {
+ ret = of_reserved_mem_device_init_by_idx(child, dev->of_node,
+ idx);
+ if (ret == 0)
+ return child;
}
- dev->mem_dev_r = devm_kzalloc(&dev->plat_dev->dev,
- sizeof(struct device), GFP_KERNEL);
- if (!dev->mem_dev_r) {
- mfc_err("Not enough memory\n");
- return -ENOMEM;
+ put_device(child);
+ return NULL;
+}
+
+static int s5p_mfc_configure_dma_memory(struct s5p_mfc_dev *mfc_dev)
+{
+ struct device *dev = &mfc_dev->plat_dev->dev;
+
+ /*
+ * When IOMMU is available, we cannot use the default configuration,
+ * because of MFC firmware requirements: address space limited to
+ * 256M and non-zero default start address.
+ * This is still simplified, not optimal configuration, but for now
+ * IOMMU core doesn't allow to configure device's IOMMUs channel
+ * separately.
+ */
+ if (exynos_is_iommu_available(dev)) {
+ int ret = exynos_configure_iommu(dev, S5P_MFC_IOMMU_DMA_BASE,
+ S5P_MFC_IOMMU_DMA_SIZE);
+ if (ret == 0)
+ mfc_dev->mem_dev_l = mfc_dev->mem_dev_r = dev;
+ return ret;
}
- device_initialize(dev->mem_dev_r);
- of_property_read_u32_array(dev->plat_dev->dev.of_node,
- "samsung,mfc-r", mem_info, 2);
- if (dma_declare_coherent_memory(dev->mem_dev_r, mem_info[0],
- mem_info[0], mem_info[1],
- DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) {
- pr_err("Failed to declare coherent memory for\n"
- "MFC device\n");
- return -ENOMEM;
+
+ /*
+ * Create and initialize virtual devices for accessing
+ * reserved memory regions.
+ */
+ mfc_dev->mem_dev_l = s5p_mfc_alloc_memdev(dev, "left",
+ MFC_BANK1_ALLOC_CTX);
+ if (!mfc_dev->mem_dev_l)
+ return -ENODEV;
+ mfc_dev->mem_dev_r = s5p_mfc_alloc_memdev(dev, "right",
+ MFC_BANK2_ALLOC_CTX);
+ if (!mfc_dev->mem_dev_r) {
+ device_unregister(mfc_dev->mem_dev_l);
+ return -ENODEV;
}
+
return 0;
}
+static void s5p_mfc_unconfigure_dma_memory(struct s5p_mfc_dev *mfc_dev)
+{
+ struct device *dev = &mfc_dev->plat_dev->dev;
+
+ if (exynos_is_iommu_available(dev)) {
+ exynos_unconfigure_iommu(dev);
+ return;
+ }
+
+ device_unregister(mfc_dev->mem_dev_l);
+ device_unregister(mfc_dev->mem_dev_r);
+}
+
+static void *mfc_get_drv_data(struct platform_device *pdev);
+
/* MFC probe function */
static int s5p_mfc_probe(struct platform_device *pdev)
{
@@ -1117,12 +1158,6 @@ static int s5p_mfc_probe(struct platform_device *pdev)
dev->variant = mfc_get_drv_data(pdev);
- ret = s5p_mfc_init_pm(dev);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to get mfc clock source\n");
- return ret;
- }
-
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dev->regs_base = devm_ioremap_resource(&pdev->dev, res);
@@ -1143,32 +1178,25 @@ static int s5p_mfc_probe(struct platform_device *pdev)
goto err_res;
}
- if (pdev->dev.of_node) {
- ret = s5p_mfc_alloc_memdevs(dev);
- if (ret < 0)
- goto err_res;
- } else {
- dev->mem_dev_l = device_find_child(&dev->plat_dev->dev,
- "s5p-mfc-l", match_child);
- if (!dev->mem_dev_l) {
- mfc_err("Mem child (L) device get failed\n");
- ret = -ENODEV;
- goto err_res;
- }
- dev->mem_dev_r = device_find_child(&dev->plat_dev->dev,
- "s5p-mfc-r", match_child);
- if (!dev->mem_dev_r) {
- mfc_err("Mem child (R) device get failed\n");
- ret = -ENODEV;
- goto err_res;
- }
+ ret = s5p_mfc_configure_dma_memory(dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to configure DMA memory\n");
+ return ret;
}
+ ret = s5p_mfc_init_pm(dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to get mfc clock source\n");
+ return ret;
+ }
+
+ vb2_dma_contig_set_max_seg_size(dev->mem_dev_l, DMA_BIT_MASK(32));
dev->alloc_ctx[0] = vb2_dma_contig_init_ctx(dev->mem_dev_l);
if (IS_ERR(dev->alloc_ctx[0])) {
ret = PTR_ERR(dev->alloc_ctx[0]);
goto err_res;
}
+ vb2_dma_contig_set_max_seg_size(dev->mem_dev_r, DMA_BIT_MASK(32));
dev->alloc_ctx[1] = vb2_dma_contig_init_ctx(dev->mem_dev_r);
if (IS_ERR(dev->alloc_ctx[1])) {
ret = PTR_ERR(dev->alloc_ctx[1]);
@@ -1201,14 +1229,6 @@ static int s5p_mfc_probe(struct platform_device *pdev)
vfd->vfl_dir = VFL_DIR_M2M;
snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_DEC_NAME);
dev->vfd_dec = vfd;
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
- if (ret) {
- v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
- video_device_release(vfd);
- goto err_dec_reg;
- }
- v4l2_info(&dev->v4l2_dev,
- "decoder registered as /dev/video%d\n", vfd->num);
video_set_drvdata(vfd, dev);
/* encoder */
@@ -1226,14 +1246,6 @@ static int s5p_mfc_probe(struct platform_device *pdev)
vfd->vfl_dir = VFL_DIR_M2M;
snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_ENC_NAME);
dev->vfd_enc = vfd;
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
- if (ret) {
- v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
- video_device_release(vfd);
- goto err_enc_reg;
- }
- v4l2_info(&dev->v4l2_dev,
- "encoder registered as /dev/video%d\n", vfd->num);
video_set_drvdata(vfd, dev);
platform_set_drvdata(pdev, dev);
@@ -1250,15 +1262,34 @@ static int s5p_mfc_probe(struct platform_device *pdev)
s5p_mfc_init_hw_cmds(dev);
s5p_mfc_init_regs(dev);
+ /* Register decoder and encoder */
+ ret = video_register_device(dev->vfd_dec, VFL_TYPE_GRABBER, 0);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+ video_device_release(dev->vfd_dec);
+ goto err_dec_reg;
+ }
+ v4l2_info(&dev->v4l2_dev,
+ "decoder registered as /dev/video%d\n", dev->vfd_dec->num);
+
+ ret = video_register_device(dev->vfd_enc, VFL_TYPE_GRABBER, 0);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+ video_device_release(dev->vfd_enc);
+ goto err_enc_reg;
+ }
+ v4l2_info(&dev->v4l2_dev,
+ "encoder registered as /dev/video%d\n", dev->vfd_enc->num);
+
pr_debug("%s--\n", __func__);
return 0;
/* Deinit MFC if probe had failed */
err_enc_reg:
- video_device_release(dev->vfd_enc);
-err_enc_alloc:
video_unregister_device(dev->vfd_dec);
err_dec_reg:
+ video_device_release(dev->vfd_enc);
+err_enc_alloc:
video_device_release(dev->vfd_dec);
err_dec_alloc:
v4l2_device_unregister(&dev->v4l2_dev);
@@ -1293,10 +1324,9 @@ static int s5p_mfc_remove(struct platform_device *pdev)
s5p_mfc_release_firmware(dev);
vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]);
vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]);
- if (pdev->dev.of_node) {
- put_device(dev->mem_dev_l);
- put_device(dev->mem_dev_r);
- }
+ s5p_mfc_unconfigure_dma_memory(dev);
+ vb2_dma_contig_clear_max_seg_size(dev->mem_dev_l);
+ vb2_dma_contig_clear_max_seg_size(dev->mem_dev_r);
s5p_mfc_final_pm(dev);
return 0;
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h b/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h
new file mode 100644
index 000000000000..5d1d1c2922e8
--- /dev/null
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2015 Samsung Electronics Co.Ltd
+ * Authors: Marek Szyprowski <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef S5P_MFC_IOMMU_H_
+#define S5P_MFC_IOMMU_H_
+
+#define S5P_MFC_IOMMU_DMA_BASE 0x20000000lu
+#define S5P_MFC_IOMMU_DMA_SIZE SZ_256M
+
+#ifdef CONFIG_EXYNOS_IOMMU
+
+#include <asm/dma-iommu.h>
+
+static inline bool exynos_is_iommu_available(struct device *dev)
+{
+ return dev->archdata.iommu != NULL;
+}
+
+static inline void exynos_unconfigure_iommu(struct device *dev)
+{
+ struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev);
+
+ arm_iommu_detach_device(dev);
+ arm_iommu_release_mapping(mapping);
+}
+
+static inline int exynos_configure_iommu(struct device *dev,
+ unsigned int base, unsigned int size)
+{
+ struct dma_iommu_mapping *mapping = NULL;
+ int ret;
+
+ /* Disable the default mapping created by device core */
+ if (to_dma_iommu_mapping(dev))
+ exynos_unconfigure_iommu(dev);
+
+ mapping = arm_iommu_create_mapping(dev->bus, base, size);
+ if (IS_ERR(mapping)) {
+ pr_warn("Failed to create IOMMU mapping for device %s\n",
+ dev_name(dev));
+ return PTR_ERR(mapping);
+ }
+
+ ret = arm_iommu_attach_device(dev, mapping);
+ if (ret) {
+ pr_warn("Failed to attached device %s to IOMMU_mapping\n",
+ dev_name(dev));
+ arm_iommu_release_mapping(mapping);
+ return ret;
+ }
+
+ return 0;
+}
+
+#else
+
+static inline bool exynos_is_iommu_available(struct device *dev)
+{
+ return false;
+}
+
+static inline int exynos_configure_iommu(struct device *dev,
+ unsigned int base, unsigned int size)
+{
+ return -ENOSYS;
+}
+
+static inline void exynos_unconfigure_iommu(struct device *dev) { }
+
+#endif
+
+#endif /* S5P_MFC_IOMMU_H_ */
diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c
index 7ab5578a0405..123d27107f60 100644
--- a/drivers/media/platform/s5p-tv/mixer_video.c
+++ b/drivers/media/platform/s5p-tv/mixer_video.c
@@ -80,6 +80,7 @@ int mxr_acquire_video(struct mxr_device *mdev,
goto fail;
}
+ vb2_dma_contig_set_max_seg_size(mdev->dev, DMA_BIT_MASK(32));
mdev->alloc_ctx = vb2_dma_contig_init_ctx(mdev->dev);
if (IS_ERR(mdev->alloc_ctx)) {
mxr_err(mdev, "could not acquire vb2 allocator\n");
@@ -152,6 +153,7 @@ void mxr_release_video(struct mxr_device *mdev)
kfree(mdev->output[i]);
vb2_dma_contig_cleanup_ctx(mdev->alloc_ctx);
+ vb2_dma_contig_clear_max_seg_size(mdev->dev);
v4l2_device_unregister(&mdev->v4l2_dev);
}
diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c
index 5361197f3e57..e3e47ace7daf 100644
--- a/drivers/media/v4l2-core/videobuf2-dma-contig.c
+++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c
@@ -753,6 +753,59 @@ void vb2_dma_contig_cleanup_ctx(void *alloc_ctx)
}
EXPORT_SYMBOL_GPL(vb2_dma_contig_cleanup_ctx);
+/**
+ * vb2_dma_contig_set_max_seg_size() - configure DMA max segment size
+ * @dev: device for configuring DMA parameters
+ * @size: size of DMA max segment size to set
+ *
+ * To allow mapping the scatter-list into a single chunk in the DMA
+ * address space, the device is required to have the DMA max segment
+ * size parameter set to a value larger than the buffer size. Otherwise,
+ * the DMA-mapping subsystem will split the mapping into max segment
+ * size chunks. This function sets the DMA max segment size
+ * parameter to let DMA-mapping map a buffer as a single chunk in DMA
+ * address space.
+ * This code assumes that the DMA-mapping subsystem will merge all
+ * scatterlist segments if this is really possible (for example when
+ * an IOMMU is available and enabled).
+ * Ideally, this parameter should be set by the generic bus code, but it
+ * is left with the default 64KiB value due to historical litmiations in
+ * other subsystems (like limited USB host drivers) and there no good
+ * place to set it to the proper value.
+ * This function should be called from the drivers, which are known to
+ * operate on platforms with IOMMU and provide access to shared buffers
+ * (either USERPTR or DMABUF). This should be done before initializing
+ * videobuf2 queue.
+ */
+int vb2_dma_contig_set_max_seg_size(struct device *dev, unsigned int size)
+{
+ if (!dev->dma_parms) {
+ dev->dma_parms = kzalloc(sizeof(dev->dma_parms), GFP_KERNEL);
+ if (!dev->dma_parms)
+ return -ENOMEM;
+ }
+ if (dma_get_max_seg_size(dev) < size)
+ return dma_set_max_seg_size(dev, size);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_dma_contig_set_max_seg_size);
+
+/*
+ * vb2_dma_contig_clear_max_seg_size() - release resources for DMA parameters
+ * @dev: device for configuring DMA parameters
+ *
+ * This function releases resources allocated to configure DMA parameters
+ * (see vb2_dma_contig_set_max_seg_size() function). It should be called from
+ * device drivers on driver remove.
+ */
+void vb2_dma_contig_clear_max_seg_size(struct device *dev)
+{
+ kfree(dev->dma_parms);
+ dev->dma_parms = NULL;
+}
+EXPORT_SYMBOL_GPL(vb2_dma_contig_clear_max_seg_size);
+
MODULE_DESCRIPTION("DMA-contig memory handling routines for videobuf2");
MODULE_AUTHOR("Pawel Osciak <[email protected]>");
MODULE_LICENSE("GPL");
diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
index 81ddb17575a9..133712346911 100644
--- a/drivers/memory/Kconfig
+++ b/drivers/memory/Kconfig
@@ -25,6 +25,17 @@ config ATMEL_SDRAMC
Starting with the at91sam9g45, this controller supports SDR, DDR and
LP-DDR memories.
+config ATMEL_EBI
+ bool "Atmel EBI driver"
+ default y
+ depends on ARCH_AT91 && OF
+ select MFD_SYSCON
+ help
+ Driver for Atmel EBI controller.
+ Used to configure the EBI (external bus interface) when the device-
+ tree is used. This bus supports NANDs, external ethernet controller,
+ SRAMs, ATA devices, etc.
+
config TI_AEMIF
tristate "Texas Instruments AEMIF driver"
depends on (ARCH_DAVINCI || ARCH_KEYSTONE) && OF
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
index cb0b7a1df11a..b20ae38b5bfb 100644
--- a/drivers/memory/Makefile
+++ b/drivers/memory/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_OF) += of_memory.o
endif
obj-$(CONFIG_ARM_PL172_MPMC) += pl172.o
obj-$(CONFIG_ATMEL_SDRAMC) += atmel-sdramc.o
+obj-$(CONFIG_ATMEL_EBI) += atmel-ebi.o
obj-$(CONFIG_TI_AEMIF) += ti-aemif.o
obj-$(CONFIG_TI_EMIF) += emif.o
obj-$(CONFIG_OMAP_GPMC) += omap-gpmc.o
diff --git a/drivers/memory/atmel-ebi.c b/drivers/memory/atmel-ebi.c
new file mode 100644
index 000000000000..f87ad6f5d2dc
--- /dev/null
+++ b/drivers/memory/atmel-ebi.c
@@ -0,0 +1,766 @@
+/*
+ * EBI driver for Atmel chips
+ * inspired by the fsl weim bus driver
+ *
+ * Copyright (C) 2013 Jean-Jacques Hiblot <[email protected]>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/atmel-matrix.h>
+#include <linux/mfd/syscon/atmel-smc.h>
+#include <linux/init.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+
+struct at91sam9_smc_timings {
+ u32 ncs_rd_setup_ns;
+ u32 nrd_setup_ns;
+ u32 ncs_wr_setup_ns;
+ u32 nwe_setup_ns;
+ u32 ncs_rd_pulse_ns;
+ u32 nrd_pulse_ns;
+ u32 ncs_wr_pulse_ns;
+ u32 nwe_pulse_ns;
+ u32 nrd_cycle_ns;
+ u32 nwe_cycle_ns;
+ u32 tdf_ns;
+};
+
+struct at91sam9_smc_generic_fields {
+ struct regmap_field *setup;
+ struct regmap_field *pulse;
+ struct regmap_field *cycle;
+ struct regmap_field *mode;
+};
+
+struct at91sam9_ebi_dev_config {
+ struct at91sam9_smc_timings timings;
+ u32 mode;
+};
+
+struct at91_ebi_dev_config {
+ int cs;
+ union {
+ struct at91sam9_ebi_dev_config sam9;
+ };
+};
+
+struct at91_ebi;
+
+struct at91_ebi_dev {
+ struct list_head node;
+ struct at91_ebi *ebi;
+ u32 mode;
+ int numcs;
+ struct at91_ebi_dev_config configs[];
+};
+
+struct at91_ebi_caps {
+ unsigned int available_cs;
+ const struct reg_field *ebi_csa;
+ void (*get_config)(struct at91_ebi_dev *ebid,
+ struct at91_ebi_dev_config *conf);
+ int (*xlate_config)(struct at91_ebi_dev *ebid,
+ struct device_node *configs_np,
+ struct at91_ebi_dev_config *conf);
+ int (*apply_config)(struct at91_ebi_dev *ebid,
+ struct at91_ebi_dev_config *conf);
+ int (*init)(struct at91_ebi *ebi);
+};
+
+struct at91_ebi {
+ struct clk *clk;
+ struct regmap *smc;
+ struct regmap *matrix;
+
+ struct regmap_field *ebi_csa;
+
+ struct device *dev;
+ const struct at91_ebi_caps *caps;
+ struct list_head devs;
+ union {
+ struct at91sam9_smc_generic_fields sam9;
+ };
+};
+
+static void at91sam9_ebi_get_config(struct at91_ebi_dev *ebid,
+ struct at91_ebi_dev_config *conf)
+{
+ struct at91sam9_smc_generic_fields *fields = &ebid->ebi->sam9;
+ unsigned int clk_rate = clk_get_rate(ebid->ebi->clk);
+ struct at91sam9_ebi_dev_config *config = &conf->sam9;
+ struct at91sam9_smc_timings *timings = &config->timings;
+ unsigned int val;
+
+ regmap_fields_read(fields->mode, conf->cs, &val);
+ config->mode = val & ~AT91_SMC_TDF;
+
+ val = (val & AT91_SMC_TDF) >> 16;
+ timings->tdf_ns = clk_rate * val;
+
+ regmap_fields_read(fields->setup, conf->cs, &val);
+ timings->ncs_rd_setup_ns = (val >> 24) & 0x1f;
+ timings->ncs_rd_setup_ns += ((val >> 29) & 0x1) * 128;
+ timings->ncs_rd_setup_ns *= clk_rate;
+ timings->nrd_setup_ns = (val >> 16) & 0x1f;
+ timings->nrd_setup_ns += ((val >> 21) & 0x1) * 128;
+ timings->nrd_setup_ns *= clk_rate;
+ timings->ncs_wr_setup_ns = (val >> 8) & 0x1f;
+ timings->ncs_wr_setup_ns += ((val >> 13) & 0x1) * 128;
+ timings->ncs_wr_setup_ns *= clk_rate;
+ timings->nwe_setup_ns = val & 0x1f;
+ timings->nwe_setup_ns += ((val >> 5) & 0x1) * 128;
+ timings->nwe_setup_ns *= clk_rate;
+
+ regmap_fields_read(fields->pulse, conf->cs, &val);
+ timings->ncs_rd_pulse_ns = (val >> 24) & 0x3f;
+ timings->ncs_rd_pulse_ns += ((val >> 30) & 0x1) * 256;
+ timings->ncs_rd_pulse_ns *= clk_rate;
+ timings->nrd_pulse_ns = (val >> 16) & 0x3f;
+ timings->nrd_pulse_ns += ((val >> 22) & 0x1) * 256;
+ timings->nrd_pulse_ns *= clk_rate;
+ timings->ncs_wr_pulse_ns = (val >> 8) & 0x3f;
+ timings->ncs_wr_pulse_ns += ((val >> 14) & 0x1) * 256;
+ timings->ncs_wr_pulse_ns *= clk_rate;
+ timings->nwe_pulse_ns = val & 0x3f;
+ timings->nwe_pulse_ns += ((val >> 6) & 0x1) * 256;
+ timings->nwe_pulse_ns *= clk_rate;
+
+ regmap_fields_read(fields->cycle, conf->cs, &val);
+ timings->nrd_cycle_ns = (val >> 16) & 0x7f;
+ timings->nrd_cycle_ns += ((val >> 23) & 0x3) * 256;
+ timings->nrd_cycle_ns *= clk_rate;
+ timings->nwe_cycle_ns = val & 0x7f;
+ timings->nwe_cycle_ns += ((val >> 7) & 0x3) * 256;
+ timings->nwe_cycle_ns *= clk_rate;
+}
+
+static int at91_xlate_timing(struct device_node *np, const char *prop,
+ u32 *val, bool *required)
+{
+ if (!of_property_read_u32(np, prop, val)) {
+ *required = true;
+ return 0;
+ }
+
+ if (*required)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int at91sam9_smc_xslate_timings(struct at91_ebi_dev *ebid,
+ struct device_node *np,
+ struct at91sam9_smc_timings *timings,
+ bool *required)
+{
+ int ret;
+
+ ret = at91_xlate_timing(np, "atmel,smc-ncs-rd-setup-ns",
+ &timings->ncs_rd_setup_ns, required);
+ if (ret)
+ goto out;
+
+ ret = at91_xlate_timing(np, "atmel,smc-nrd-setup-ns",
+ &timings->nrd_setup_ns, required);
+ if (ret)
+ goto out;
+
+ ret = at91_xlate_timing(np, "atmel,smc-ncs-wr-setup-ns",
+ &timings->ncs_wr_setup_ns, required);
+ if (ret)
+ goto out;
+
+ ret = at91_xlate_timing(np, "atmel,smc-nwe-setup-ns",
+ &timings->nwe_setup_ns, required);
+ if (ret)
+ goto out;
+
+ ret = at91_xlate_timing(np, "atmel,smc-ncs-rd-pulse-ns",
+ &timings->ncs_rd_pulse_ns, required);
+ if (ret)
+ goto out;
+
+ ret = at91_xlate_timing(np, "atmel,smc-nrd-pulse-ns",
+ &timings->nrd_pulse_ns, required);
+ if (ret)
+ goto out;
+
+ ret = at91_xlate_timing(np, "atmel,smc-ncs-wr-pulse-ns",
+ &timings->ncs_wr_pulse_ns, required);
+ if (ret)
+ goto out;
+
+ ret = at91_xlate_timing(np, "atmel,smc-nwe-pulse-ns",
+ &timings->nwe_pulse_ns, required);
+ if (ret)
+ goto out;
+
+ ret = at91_xlate_timing(np, "atmel,smc-nwe-cycle-ns",
+ &timings->nwe_cycle_ns, required);
+ if (ret)
+ goto out;
+
+ ret = at91_xlate_timing(np, "atmel,smc-nrd-cycle-ns",
+ &timings->nrd_cycle_ns, required);
+ if (ret)
+ goto out;
+
+ ret = at91_xlate_timing(np, "atmel,smc-tdf-ns",
+ &timings->tdf_ns, required);
+
+out:
+ if (ret)
+ dev_err(ebid->ebi->dev,
+ "missing or invalid timings definition in %s",
+ np->full_name);
+
+ return ret;
+}
+
+static int at91sam9_ebi_xslate_config(struct at91_ebi_dev *ebid,
+ struct device_node *np,
+ struct at91_ebi_dev_config *conf)
+{
+ struct at91sam9_ebi_dev_config *config = &conf->sam9;
+ bool required = false;
+ const char *tmp_str;
+ u32 tmp;
+ int ret;
+
+ ret = of_property_read_u32(np, "atmel,smc-bus-width", &tmp);
+ if (!ret) {
+ switch (tmp) {
+ case 8:
+ config->mode |= AT91_SMC_DBW_8;
+ break;
+
+ case 16:
+ config->mode |= AT91_SMC_DBW_16;
+ break;
+
+ case 32:
+ config->mode |= AT91_SMC_DBW_32;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ required = true;
+ }
+
+ if (of_property_read_bool(np, "atmel,smc-tdf-optimized")) {
+ config->mode |= AT91_SMC_TDFMODE_OPTIMIZED;
+ required = true;
+ }
+
+ tmp_str = NULL;
+ of_property_read_string(np, "atmel,smc-byte-access-type", &tmp_str);
+ if (tmp_str && !strcmp(tmp_str, "write")) {
+ config->mode |= AT91_SMC_BAT_WRITE;
+ required = true;
+ }
+
+ tmp_str = NULL;
+ of_property_read_string(np, "atmel,smc-read-mode", &tmp_str);
+ if (tmp_str && !strcmp(tmp_str, "nrd")) {
+ config->mode |= AT91_SMC_READMODE_NRD;
+ required = true;
+ }
+
+ tmp_str = NULL;
+ of_property_read_string(np, "atmel,smc-write-mode", &tmp_str);
+ if (tmp_str && !strcmp(tmp_str, "nwe")) {
+ config->mode |= AT91_SMC_WRITEMODE_NWE;
+ required = true;
+ }
+
+ tmp_str = NULL;
+ of_property_read_string(np, "atmel,smc-exnw-mode", &tmp_str);
+ if (tmp_str) {
+ if (!strcmp(tmp_str, "frozen"))
+ config->mode |= AT91_SMC_EXNWMODE_FROZEN;
+ else if (!strcmp(tmp_str, "ready"))
+ config->mode |= AT91_SMC_EXNWMODE_READY;
+ else if (strcmp(tmp_str, "disabled"))
+ return -EINVAL;
+
+ required = true;
+ }
+
+ ret = of_property_read_u32(np, "atmel,smc-page-mode", &tmp);
+ if (!ret) {
+ switch (tmp) {
+ case 4:
+ config->mode |= AT91_SMC_PS_4;
+ break;
+
+ case 8:
+ config->mode |= AT91_SMC_PS_8;
+ break;
+
+ case 16:
+ config->mode |= AT91_SMC_PS_16;
+ break;
+
+ case 32:
+ config->mode |= AT91_SMC_PS_32;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ config->mode |= AT91_SMC_PMEN;
+ required = true;
+ }
+
+ ret = at91sam9_smc_xslate_timings(ebid, np, &config->timings,
+ &required);
+ if (ret)
+ return ret;
+
+ return required;
+}
+
+static int at91sam9_ebi_apply_config(struct at91_ebi_dev *ebid,
+ struct at91_ebi_dev_config *conf)
+{
+ unsigned int clk_rate = clk_get_rate(ebid->ebi->clk);
+ struct at91sam9_ebi_dev_config *config = &conf->sam9;
+ struct at91sam9_smc_timings *timings = &config->timings;
+ struct at91sam9_smc_generic_fields *fields = &ebid->ebi->sam9;
+ u32 coded_val;
+ u32 val;
+
+ coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate,
+ timings->ncs_rd_setup_ns);
+ val = AT91SAM9_SMC_NCS_NRDSETUP(coded_val);
+ coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate,
+ timings->nrd_setup_ns);
+ val |= AT91SAM9_SMC_NRDSETUP(coded_val);
+ coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate,
+ timings->ncs_wr_setup_ns);
+ val |= AT91SAM9_SMC_NCS_WRSETUP(coded_val);
+ coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate,
+ timings->nwe_setup_ns);
+ val |= AT91SAM9_SMC_NWESETUP(coded_val);
+ regmap_fields_write(fields->setup, conf->cs, val);
+
+ coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate,
+ timings->ncs_rd_pulse_ns);
+ val = AT91SAM9_SMC_NCS_NRDPULSE(coded_val);
+ coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate,
+ timings->nrd_pulse_ns);
+ val |= AT91SAM9_SMC_NRDPULSE(coded_val);
+ coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate,
+ timings->ncs_wr_pulse_ns);
+ val |= AT91SAM9_SMC_NCS_WRPULSE(coded_val);
+ coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate,
+ timings->nwe_pulse_ns);
+ val |= AT91SAM9_SMC_NWEPULSE(coded_val);
+ regmap_fields_write(fields->pulse, conf->cs, val);
+
+ coded_val = at91sam9_smc_cycle_ns_to_cycles(clk_rate,
+ timings->nrd_cycle_ns);
+ val = AT91SAM9_SMC_NRDCYCLE(coded_val);
+ coded_val = at91sam9_smc_cycle_ns_to_cycles(clk_rate,
+ timings->nwe_cycle_ns);
+ val |= AT91SAM9_SMC_NWECYCLE(coded_val);
+ regmap_fields_write(fields->cycle, conf->cs, val);
+
+ val = DIV_ROUND_UP(timings->tdf_ns, clk_rate);
+ if (val > AT91_SMC_TDF_MAX)
+ val = AT91_SMC_TDF_MAX;
+ regmap_fields_write(fields->mode, conf->cs,
+ config->mode | AT91_SMC_TDF_(val));
+
+ return 0;
+}
+
+static int at91sam9_ebi_init(struct at91_ebi *ebi)
+{
+ struct at91sam9_smc_generic_fields *fields = &ebi->sam9;
+ struct reg_field field = REG_FIELD(0, 0, 31);
+
+ field.id_size = fls(ebi->caps->available_cs);
+ field.id_offset = AT91SAM9_SMC_GENERIC_BLK_SZ;
+
+ field.reg = AT91SAM9_SMC_SETUP(AT91SAM9_SMC_GENERIC);
+ fields->setup = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
+ if (IS_ERR(fields->setup))
+ return PTR_ERR(fields->setup);
+
+ field.reg = AT91SAM9_SMC_PULSE(AT91SAM9_SMC_GENERIC);
+ fields->pulse = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
+ if (IS_ERR(fields->pulse))
+ return PTR_ERR(fields->pulse);
+
+ field.reg = AT91SAM9_SMC_CYCLE(AT91SAM9_SMC_GENERIC);
+ fields->cycle = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
+ if (IS_ERR(fields->cycle))
+ return PTR_ERR(fields->cycle);
+
+ field.reg = AT91SAM9_SMC_MODE(AT91SAM9_SMC_GENERIC);
+ fields->mode = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
+ if (IS_ERR(fields->mode))
+ return PTR_ERR(fields->mode);
+
+ return 0;
+}
+
+static int sama5d3_ebi_init(struct at91_ebi *ebi)
+{
+ struct at91sam9_smc_generic_fields *fields = &ebi->sam9;
+ struct reg_field field = REG_FIELD(0, 0, 31);
+
+ field.id_size = fls(ebi->caps->available_cs);
+ field.id_offset = SAMA5_SMC_GENERIC_BLK_SZ;
+
+ field.reg = AT91SAM9_SMC_SETUP(SAMA5_SMC_GENERIC);
+ fields->setup = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
+ if (IS_ERR(fields->setup))
+ return PTR_ERR(fields->setup);
+
+ field.reg = AT91SAM9_SMC_PULSE(SAMA5_SMC_GENERIC);
+ fields->pulse = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
+ if (IS_ERR(fields->pulse))
+ return PTR_ERR(fields->pulse);
+
+ field.reg = AT91SAM9_SMC_CYCLE(SAMA5_SMC_GENERIC);
+ fields->cycle = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
+ if (IS_ERR(fields->cycle))
+ return PTR_ERR(fields->cycle);
+
+ field.reg = SAMA5_SMC_MODE(SAMA5_SMC_GENERIC);
+ fields->mode = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
+ if (IS_ERR(fields->mode))
+ return PTR_ERR(fields->mode);
+
+ return 0;
+}
+
+static int at91_ebi_dev_setup(struct at91_ebi *ebi, struct device_node *np,
+ int reg_cells)
+{
+ const struct at91_ebi_caps *caps = ebi->caps;
+ struct at91_ebi_dev_config conf = { };
+ struct device *dev = ebi->dev;
+ struct at91_ebi_dev *ebid;
+ int ret, numcs = 0, i;
+ bool apply = false;
+
+ numcs = of_property_count_elems_of_size(np, "reg",
+ reg_cells * sizeof(u32));
+ if (numcs <= 0) {
+ dev_err(dev, "invalid reg property in %s\n", np->full_name);
+ return -EINVAL;
+ }
+
+ ebid = devm_kzalloc(ebi->dev,
+ sizeof(*ebid) + (numcs * sizeof(*ebid->configs)),
+ GFP_KERNEL);
+ if (!ebid)
+ return -ENOMEM;
+
+ ebid->ebi = ebi;
+
+ ret = caps->xlate_config(ebid, np, &conf);
+ if (ret < 0)
+ return ret;
+ else if (ret)
+ apply = true;
+
+ for (i = 0; i < numcs; i++) {
+ u32 cs;
+
+ ret = of_property_read_u32_index(np, "reg", i * reg_cells,
+ &cs);
+ if (ret)
+ return ret;
+
+ if (cs > AT91_MATRIX_EBI_NUM_CS ||
+ !(ebi->caps->available_cs & BIT(cs))) {
+ dev_err(dev, "invalid reg property in %s\n",
+ np->full_name);
+ return -EINVAL;
+ }
+
+ ebid->configs[i].cs = cs;
+
+ if (apply) {
+ conf.cs = cs;
+ ret = caps->apply_config(ebid, &conf);
+ if (ret)
+ return ret;
+ }
+
+ caps->get_config(ebid, &ebid->configs[i]);
+
+ /*
+ * Attach the EBI device to the generic SMC logic if at least
+ * one "atmel,smc-" property is present.
+ */
+ if (ebi->ebi_csa && ret)
+ regmap_field_update_bits(ebi->ebi_csa,
+ BIT(cs), 0);
+ }
+
+ list_add_tail(&ebid->node, &ebi->devs);
+
+ return 0;
+}
+
+static const struct reg_field at91sam9260_ebi_csa =
+ REG_FIELD(AT91SAM9260_MATRIX_EBICSA, 0,
+ AT91_MATRIX_EBI_NUM_CS - 1);
+
+static const struct at91_ebi_caps at91sam9260_ebi_caps = {
+ .available_cs = 0xff,
+ .ebi_csa = &at91sam9260_ebi_csa,
+ .get_config = at91sam9_ebi_get_config,
+ .xlate_config = at91sam9_ebi_xslate_config,
+ .apply_config = at91sam9_ebi_apply_config,
+ .init = at91sam9_ebi_init,
+};
+
+static const struct reg_field at91sam9261_ebi_csa =
+ REG_FIELD(AT91SAM9261_MATRIX_EBICSA, 0,
+ AT91_MATRIX_EBI_NUM_CS - 1);
+
+static const struct at91_ebi_caps at91sam9261_ebi_caps = {
+ .available_cs = 0xff,
+ .ebi_csa = &at91sam9261_ebi_csa,
+ .get_config = at91sam9_ebi_get_config,
+ .xlate_config = at91sam9_ebi_xslate_config,
+ .apply_config = at91sam9_ebi_apply_config,
+ .init = at91sam9_ebi_init,
+};
+
+static const struct reg_field at91sam9263_ebi0_csa =
+ REG_FIELD(AT91SAM9263_MATRIX_EBI0CSA, 0,
+ AT91_MATRIX_EBI_NUM_CS - 1);
+
+static const struct at91_ebi_caps at91sam9263_ebi0_caps = {
+ .available_cs = 0x3f,
+ .ebi_csa = &at91sam9263_ebi0_csa,
+ .get_config = at91sam9_ebi_get_config,
+ .xlate_config = at91sam9_ebi_xslate_config,
+ .apply_config = at91sam9_ebi_apply_config,
+ .init = at91sam9_ebi_init,
+};
+
+static const struct reg_field at91sam9263_ebi1_csa =
+ REG_FIELD(AT91SAM9263_MATRIX_EBI1CSA, 0,
+ AT91_MATRIX_EBI_NUM_CS - 1);
+
+static const struct at91_ebi_caps at91sam9263_ebi1_caps = {
+ .available_cs = 0x7,
+ .ebi_csa = &at91sam9263_ebi1_csa,
+ .get_config = at91sam9_ebi_get_config,
+ .xlate_config = at91sam9_ebi_xslate_config,
+ .apply_config = at91sam9_ebi_apply_config,
+ .init = at91sam9_ebi_init,
+};
+
+static const struct reg_field at91sam9rl_ebi_csa =
+ REG_FIELD(AT91SAM9RL_MATRIX_EBICSA, 0,
+ AT91_MATRIX_EBI_NUM_CS - 1);
+
+static const struct at91_ebi_caps at91sam9rl_ebi_caps = {
+ .available_cs = 0x3f,
+ .ebi_csa = &at91sam9rl_ebi_csa,
+ .get_config = at91sam9_ebi_get_config,
+ .xlate_config = at91sam9_ebi_xslate_config,
+ .apply_config = at91sam9_ebi_apply_config,
+ .init = at91sam9_ebi_init,
+};
+
+static const struct reg_field at91sam9g45_ebi_csa =
+ REG_FIELD(AT91SAM9G45_MATRIX_EBICSA, 0,
+ AT91_MATRIX_EBI_NUM_CS - 1);
+
+static const struct at91_ebi_caps at91sam9g45_ebi_caps = {
+ .available_cs = 0x3f,
+ .ebi_csa = &at91sam9g45_ebi_csa,
+ .get_config = at91sam9_ebi_get_config,
+ .xlate_config = at91sam9_ebi_xslate_config,
+ .apply_config = at91sam9_ebi_apply_config,
+ .init = at91sam9_ebi_init,
+};
+
+static const struct at91_ebi_caps at91sam9x5_ebi_caps = {
+ .available_cs = 0x3f,
+ .ebi_csa = &at91sam9263_ebi0_csa,
+ .get_config = at91sam9_ebi_get_config,
+ .xlate_config = at91sam9_ebi_xslate_config,
+ .apply_config = at91sam9_ebi_apply_config,
+ .init = at91sam9_ebi_init,
+};
+
+static const struct at91_ebi_caps sama5d3_ebi_caps = {
+ .available_cs = 0xf,
+ .get_config = at91sam9_ebi_get_config,
+ .xlate_config = at91sam9_ebi_xslate_config,
+ .apply_config = at91sam9_ebi_apply_config,
+ .init = sama5d3_ebi_init,
+};
+
+static const struct of_device_id at91_ebi_id_table[] = {
+ {
+ .compatible = "atmel,at91sam9260-ebi",
+ .data = &at91sam9260_ebi_caps,
+ },
+ {
+ .compatible = "atmel,at91sam9261-ebi",
+ .data = &at91sam9261_ebi_caps,
+ },
+ {
+ .compatible = "atmel,at91sam9263-ebi0",
+ .data = &at91sam9263_ebi0_caps,
+ },
+ {
+ .compatible = "atmel,at91sam9263-ebi1",
+ .data = &at91sam9263_ebi1_caps,
+ },
+ {
+ .compatible = "atmel,at91sam9rl-ebi",
+ .data = &at91sam9rl_ebi_caps,
+ },
+ {
+ .compatible = "atmel,at91sam9g45-ebi",
+ .data = &at91sam9g45_ebi_caps,
+ },
+ {
+ .compatible = "atmel,at91sam9x5-ebi",
+ .data = &at91sam9x5_ebi_caps,
+ },
+ {
+ .compatible = "atmel,sama5d3-ebi",
+ .data = &sama5d3_ebi_caps,
+ },
+ { /* sentinel */ }
+};
+
+static int at91_ebi_dev_disable(struct at91_ebi *ebi, struct device_node *np)
+{
+ struct device *dev = ebi->dev;
+ struct property *newprop;
+
+ newprop = devm_kzalloc(dev, sizeof(*newprop), GFP_KERNEL);
+ if (!newprop)
+ return -ENOMEM;
+
+ newprop->name = devm_kstrdup(dev, "status", GFP_KERNEL);
+ if (!newprop->name)
+ return -ENOMEM;
+
+ newprop->value = devm_kstrdup(dev, "disabled", GFP_KERNEL);
+ if (!newprop->name)
+ return -ENOMEM;
+
+ newprop->length = sizeof("disabled");
+
+ return of_update_property(np, newprop);
+}
+
+static int at91_ebi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *child, *np = dev->of_node;
+ const struct of_device_id *match;
+ struct at91_ebi *ebi;
+ int ret, reg_cells;
+ struct clk *clk;
+ u32 val;
+
+ match = of_match_device(at91_ebi_id_table, dev);
+ if (!match || !match->data)
+ return -EINVAL;
+
+ ebi = devm_kzalloc(dev, sizeof(*ebi), GFP_KERNEL);
+ if (!ebi)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&ebi->devs);
+ ebi->caps = match->data;
+ ebi->dev = dev;
+
+ clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ ebi->clk = clk;
+
+ ebi->smc = syscon_regmap_lookup_by_phandle(np, "atmel,smc");
+ if (IS_ERR(ebi->smc))
+ return PTR_ERR(ebi->smc);
+
+ /*
+ * The sama5d3 does not provide an EBICSA register and thus does need
+ * to access the matrix registers.
+ */
+ if (ebi->caps->ebi_csa) {
+ ebi->matrix =
+ syscon_regmap_lookup_by_phandle(np, "atmel,matrix");
+ if (IS_ERR(ebi->matrix))
+ return PTR_ERR(ebi->matrix);
+
+ ebi->ebi_csa = regmap_field_alloc(ebi->matrix,
+ *ebi->caps->ebi_csa);
+ if (IS_ERR(ebi->ebi_csa))
+ return PTR_ERR(ebi->ebi_csa);
+ }
+
+ ret = ebi->caps->init(ebi);
+ if (ret)
+ return ret;
+
+ ret = of_property_read_u32(np, "#address-cells", &val);
+ if (ret) {
+ dev_err(dev, "missing #address-cells property\n");
+ return ret;
+ }
+
+ reg_cells = val;
+
+ ret = of_property_read_u32(np, "#size-cells", &val);
+ if (ret) {
+ dev_err(dev, "missing #address-cells property\n");
+ return ret;
+ }
+
+ reg_cells += val;
+
+ for_each_available_child_of_node(np, child) {
+ if (!of_find_property(child, "reg", NULL))
+ continue;
+
+ ret = at91_ebi_dev_setup(ebi, child, reg_cells);
+ if (ret) {
+ dev_err(dev, "failed to configure EBI bus for %s, disabling the device",
+ child->full_name);
+
+ ret = at91_ebi_dev_disable(ebi, child);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return of_platform_populate(np, NULL, NULL, dev);
+}
+
+static struct platform_driver at91_ebi_driver = {
+ .driver = {
+ .name = "atmel-ebi",
+ .of_match_table = at91_ebi_id_table,
+ },
+};
+builtin_platform_driver_probe(at91_ebi_driver, at91_ebi_probe);
diff --git a/drivers/memory/atmel-sdramc.c b/drivers/memory/atmel-sdramc.c
index a3ebc8a87479..53a341f3b305 100644
--- a/drivers/memory/atmel-sdramc.c
+++ b/drivers/memory/atmel-sdramc.c
@@ -1,6 +1,8 @@
/*
* Atmel (Multi-port DDR-)SDRAM Controller driver
*
+ * Author: Alexandre Belloni <[email protected]>
+ *
* Copyright (C) 2014 Atmel
*
* This program is free software: you can redistribute it and/or modify
@@ -20,7 +22,7 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/kernel.h>
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
@@ -48,7 +50,6 @@ static const struct of_device_id atmel_ramc_of_match[] = {
{ .compatible = "atmel,sama5d3-ddramc", .data = &sama5d3_caps, },
{},
};
-MODULE_DEVICE_TABLE(of, atmel_ramc_of_match);
static int atmel_ramc_probe(struct platform_device *pdev)
{
@@ -90,8 +91,4 @@ static int __init atmel_ramc_init(void)
{
return platform_driver_register(&atmel_ramc_driver);
}
-module_init(atmel_ramc_init);
-
-MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Alexandre Belloni <[email protected]>");
-MODULE_DESCRIPTION("Atmel (Multi-port DDR-)SDRAM Controller");
+device_initcall(atmel_ramc_init);
diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c
index af4884ba6b7c..21829880478f 100644
--- a/drivers/memory/omap-gpmc.c
+++ b/drivers/memory/omap-gpmc.c
@@ -20,7 +20,6 @@
#include <linux/ioport.h>
#include <linux/spinlock.h>
#include <linux/io.h>
-#include <linux/module.h>
#include <linux/gpio/driver.h>
#include <linux/interrupt.h>
#include <linux/irqdomain.h>
@@ -1807,7 +1806,6 @@ static const struct of_device_id gpmc_dt_ids[] = {
{ .compatible = "ti,am3352-gpmc" }, /* am335x devices */
{ }
};
-MODULE_DEVICE_TABLE(of, gpmc_dt_ids);
/**
* gpmc_read_settings_dt - read gpmc settings from device-tree
@@ -2155,68 +2153,6 @@ err:
return ret;
}
-static int gpmc_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
-{
- return 1; /* we're input only */
-}
-
-static int gpmc_gpio_direction_input(struct gpio_chip *chip,
- unsigned int offset)
-{
- return 0; /* we're input only */
-}
-
-static int gpmc_gpio_direction_output(struct gpio_chip *chip,
- unsigned int offset, int value)
-{
- return -EINVAL; /* we're input only */
-}
-
-static void gpmc_gpio_set(struct gpio_chip *chip, unsigned int offset,
- int value)
-{
-}
-
-static int gpmc_gpio_get(struct gpio_chip *chip, unsigned int offset)
-{
- u32 reg;
-
- offset += 8;
-
- reg = gpmc_read_reg(GPMC_STATUS) & BIT(offset);
-
- return !!reg;
-}
-
-static int gpmc_gpio_init(struct gpmc_device *gpmc)
-{
- int ret;
-
- gpmc->gpio_chip.parent = gpmc->dev;
- gpmc->gpio_chip.owner = THIS_MODULE;
- gpmc->gpio_chip.label = DEVICE_NAME;
- gpmc->gpio_chip.ngpio = gpmc_nr_waitpins;
- gpmc->gpio_chip.get_direction = gpmc_gpio_get_direction;
- gpmc->gpio_chip.direction_input = gpmc_gpio_direction_input;
- gpmc->gpio_chip.direction_output = gpmc_gpio_direction_output;
- gpmc->gpio_chip.set = gpmc_gpio_set;
- gpmc->gpio_chip.get = gpmc_gpio_get;
- gpmc->gpio_chip.base = -1;
-
- ret = gpiochip_add(&gpmc->gpio_chip);
- if (ret < 0) {
- dev_err(gpmc->dev, "could not register gpio chip: %d\n", ret);
- return ret;
- }
-
- return 0;
-}
-
-static void gpmc_gpio_exit(struct gpmc_device *gpmc)
-{
- gpiochip_remove(&gpmc->gpio_chip);
-}
-
static int gpmc_probe_dt(struct platform_device *pdev)
{
int ret;
@@ -2281,7 +2217,69 @@ static int gpmc_probe_dt_children(struct platform_device *pdev)
{
return 0;
}
-#endif
+#endif /* CONFIG_OF */
+
+static int gpmc_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
+{
+ return 1; /* we're input only */
+}
+
+static int gpmc_gpio_direction_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ return 0; /* we're input only */
+}
+
+static int gpmc_gpio_direction_output(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ return -EINVAL; /* we're input only */
+}
+
+static void gpmc_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
+{
+}
+
+static int gpmc_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ u32 reg;
+
+ offset += 8;
+
+ reg = gpmc_read_reg(GPMC_STATUS) & BIT(offset);
+
+ return !!reg;
+}
+
+static int gpmc_gpio_init(struct gpmc_device *gpmc)
+{
+ int ret;
+
+ gpmc->gpio_chip.parent = gpmc->dev;
+ gpmc->gpio_chip.owner = THIS_MODULE;
+ gpmc->gpio_chip.label = DEVICE_NAME;
+ gpmc->gpio_chip.ngpio = gpmc_nr_waitpins;
+ gpmc->gpio_chip.get_direction = gpmc_gpio_get_direction;
+ gpmc->gpio_chip.direction_input = gpmc_gpio_direction_input;
+ gpmc->gpio_chip.direction_output = gpmc_gpio_direction_output;
+ gpmc->gpio_chip.set = gpmc_gpio_set;
+ gpmc->gpio_chip.get = gpmc_gpio_get;
+ gpmc->gpio_chip.base = -1;
+
+ ret = gpiochip_add(&gpmc->gpio_chip);
+ if (ret < 0) {
+ dev_err(gpmc->dev, "could not register gpio chip: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void gpmc_gpio_exit(struct gpmc_device *gpmc)
+{
+ gpiochip_remove(&gpmc->gpio_chip);
+}
static int gpmc_probe(struct platform_device *pdev)
{
@@ -2437,15 +2435,7 @@ static __init int gpmc_init(void)
{
return platform_driver_register(&gpmc_driver);
}
-
-static __exit void gpmc_exit(void)
-{
- platform_driver_unregister(&gpmc_driver);
-
-}
-
postcore_initcall(gpmc_init);
-module_exit(gpmc_exit);
static struct omap3_gpmc_regs gpmc_context;
diff --git a/drivers/memory/samsung/exynos-srom.c b/drivers/memory/samsung/exynos-srom.c
index 96756fb4d6bd..067f53324901 100644
--- a/drivers/memory/samsung/exynos-srom.c
+++ b/drivers/memory/samsung/exynos-srom.c
@@ -11,7 +11,7 @@
*/
#include <linux/io.h>
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
@@ -91,17 +91,17 @@ static int exynos_srom_configure_bank(struct exynos_srom *srom,
if (width == 2)
cs |= 1 << EXYNOS_SROM_BW__DATAWIDTH__SHIFT;
- bw = __raw_readl(srom->reg_base + EXYNOS_SROM_BW);
+ bw = readl_relaxed(srom->reg_base + EXYNOS_SROM_BW);
bw = (bw & ~(EXYNOS_SROM_BW__CS_MASK << bank)) | (cs << bank);
- __raw_writel(bw, srom->reg_base + EXYNOS_SROM_BW);
+ writel_relaxed(bw, srom->reg_base + EXYNOS_SROM_BW);
- __raw_writel(pmc | (timing[0] << EXYNOS_SROM_BCX__TACP__SHIFT) |
- (timing[1] << EXYNOS_SROM_BCX__TCAH__SHIFT) |
- (timing[2] << EXYNOS_SROM_BCX__TCOH__SHIFT) |
- (timing[3] << EXYNOS_SROM_BCX__TACC__SHIFT) |
- (timing[4] << EXYNOS_SROM_BCX__TCOS__SHIFT) |
- (timing[5] << EXYNOS_SROM_BCX__TACS__SHIFT),
- srom->reg_base + EXYNOS_SROM_BC0 + bank);
+ writel_relaxed(pmc | (timing[0] << EXYNOS_SROM_BCX__TACP__SHIFT) |
+ (timing[1] << EXYNOS_SROM_BCX__TCAH__SHIFT) |
+ (timing[2] << EXYNOS_SROM_BCX__TCOH__SHIFT) |
+ (timing[3] << EXYNOS_SROM_BCX__TACC__SHIFT) |
+ (timing[4] << EXYNOS_SROM_BCX__TCOS__SHIFT) |
+ (timing[5] << EXYNOS_SROM_BCX__TACS__SHIFT),
+ srom->reg_base + EXYNOS_SROM_BC0 + bank);
return 0;
}
@@ -159,16 +159,6 @@ static int exynos_srom_probe(struct platform_device *pdev)
return of_platform_populate(np, NULL, NULL, dev);
}
-static int exynos_srom_remove(struct platform_device *pdev)
-{
- struct exynos_srom *srom = platform_get_drvdata(pdev);
-
- kfree(srom->reg_offset);
- iounmap(srom->reg_base);
-
- return 0;
-}
-
#ifdef CONFIG_PM_SLEEP
static void exynos_srom_save(void __iomem *base,
struct exynos_srom_reg_dump *rd,
@@ -211,21 +201,16 @@ static const struct of_device_id of_exynos_srom_ids[] = {
},
{},
};
-MODULE_DEVICE_TABLE(of, of_exynos_srom_ids);
static SIMPLE_DEV_PM_OPS(exynos_srom_pm_ops, exynos_srom_suspend, exynos_srom_resume);
static struct platform_driver exynos_srom_driver = {
.probe = exynos_srom_probe,
- .remove = exynos_srom_remove,
.driver = {
.name = "exynos-srom",
.of_match_table = of_exynos_srom_ids,
.pm = &exynos_srom_pm_ops,
+ .suppress_bind_attrs = true,
},
};
-module_platform_driver(exynos_srom_driver);
-
-MODULE_AUTHOR("Pankaj Dubey <[email protected]>");
-MODULE_DESCRIPTION("Exynos SROM Controller Driver");
-MODULE_LICENSE("GPL");
+builtin_platform_driver(exynos_srom_driver);
diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c
index a1ae0cc2b86d..a4803ac192bb 100644
--- a/drivers/memory/tegra/mc.c
+++ b/drivers/memory/tegra/mc.c
@@ -186,8 +186,10 @@ static int load_timings(struct tegra_mc *mc, struct device_node *node)
timing = &mc->timings[i++];
err = load_one_timing(mc, timing, child);
- if (err)
+ if (err) {
+ of_node_put(child);
return err;
+ }
}
return 0;
@@ -206,15 +208,13 @@ static int tegra_mc_setup_timings(struct tegra_mc *mc)
for_each_child_of_node(mc->dev->of_node, node) {
err = of_property_read_u32(node, "nvidia,ram-code",
&node_ram_code);
- if (err || (node_ram_code != ram_code)) {
- of_node_put(node);
+ if (err || (node_ram_code != ram_code))
continue;
- }
err = load_timings(mc, node);
+ of_node_put(node);
if (err)
return err;
- of_node_put(node);
break;
}
diff --git a/drivers/memory/tegra/tegra124-emc.c b/drivers/memory/tegra/tegra124-emc.c
index 3dac7be39654..06cc781ebac1 100644
--- a/drivers/memory/tegra/tegra124-emc.c
+++ b/drivers/memory/tegra/tegra124-emc.c
@@ -970,8 +970,10 @@ static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc,
timing = &emc->timings[i++];
err = load_one_timing_from_dt(emc, timing, child);
- if (err)
+ if (err) {
+ of_node_put(child);
return err;
+ }
}
sort(emc->timings, emc->num_timings, sizeof(*timing), cmp_timings,
@@ -995,10 +997,8 @@ tegra_emc_find_node_by_ram_code(struct device_node *node, u32 ram_code)
u32 value;
err = of_property_read_u32(np, "nvidia,ram-code", &value);
- if (err || (value != ram_code)) {
- of_node_put(np);
+ if (err || (value != ram_code))
continue;
- }
return np;
}
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index ed01c0172e4a..04e4fe58fb0c 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -21,6 +21,7 @@
#include <linux/sizes.h>
#include <linux/of_reserved_mem.h>
#include <linux/sort.h>
+#include <linux/slab.h>
#define MAX_RESERVED_REGIONS 16
static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS];
@@ -289,53 +290,95 @@ static inline struct reserved_mem *__find_rmem(struct device_node *node)
return NULL;
}
+struct rmem_assigned_device {
+ struct device *dev;
+ struct reserved_mem *rmem;
+ struct list_head list;
+};
+
+static LIST_HEAD(of_rmem_assigned_device_list);
+static DEFINE_MUTEX(of_rmem_assigned_device_mutex);
+
/**
- * of_reserved_mem_device_init() - assign reserved memory region to given device
+ * of_reserved_mem_device_init_by_idx() - assign reserved memory region to
+ * given device
+ * @dev: Pointer to the device to configure
+ * @np: Pointer to the device_node with 'reserved-memory' property
+ * @idx: Index of selected region
*
- * This function assign memory region pointed by "memory-region" device tree
- * property to the given device.
+ * This function assigns respective DMA-mapping operations based on reserved
+ * memory region specified by 'memory-region' property in @np node to the @dev
+ * device. When driver needs to use more than one reserved memory region, it
+ * should allocate child devices and initialize regions by name for each of
+ * child device.
+ *
+ * Returns error code or zero on success.
*/
-int of_reserved_mem_device_init(struct device *dev)
+int of_reserved_mem_device_init_by_idx(struct device *dev,
+ struct device_node *np, int idx)
{
+ struct rmem_assigned_device *rd;
+ struct device_node *target;
struct reserved_mem *rmem;
- struct device_node *np;
int ret;
- np = of_parse_phandle(dev->of_node, "memory-region", 0);
- if (!np)
- return -ENODEV;
+ if (!np || !dev)
+ return -EINVAL;
+
+ target = of_parse_phandle(np, "memory-region", idx);
+ if (!target)
+ return -EINVAL;
- rmem = __find_rmem(np);
- of_node_put(np);
+ rmem = __find_rmem(target);
+ of_node_put(target);
if (!rmem || !rmem->ops || !rmem->ops->device_init)
return -EINVAL;
+ rd = kmalloc(sizeof(struct rmem_assigned_device), GFP_KERNEL);
+ if (!rd)
+ return -ENOMEM;
+
ret = rmem->ops->device_init(rmem, dev);
- if (ret == 0)
+ if (ret == 0) {
+ rd->dev = dev;
+ rd->rmem = rmem;
+
+ mutex_lock(&of_rmem_assigned_device_mutex);
+ list_add(&rd->list, &of_rmem_assigned_device_list);
+ mutex_unlock(&of_rmem_assigned_device_mutex);
+
dev_info(dev, "assigned reserved memory node %s\n", rmem->name);
+ } else {
+ kfree(rd);
+ }
return ret;
}
-EXPORT_SYMBOL_GPL(of_reserved_mem_device_init);
+EXPORT_SYMBOL_GPL(of_reserved_mem_device_init_by_idx);
/**
* of_reserved_mem_device_release() - release reserved memory device structures
+ * @dev: Pointer to the device to deconfigure
*
* This function releases structures allocated for memory region handling for
* the given device.
*/
void of_reserved_mem_device_release(struct device *dev)
{
- struct reserved_mem *rmem;
- struct device_node *np;
-
- np = of_parse_phandle(dev->of_node, "memory-region", 0);
- if (!np)
- return;
-
- rmem = __find_rmem(np);
- of_node_put(np);
+ struct rmem_assigned_device *rd;
+ struct reserved_mem *rmem = NULL;
+
+ mutex_lock(&of_rmem_assigned_device_mutex);
+ list_for_each_entry(rd, &of_rmem_assigned_device_list, list) {
+ if (rd->dev == dev) {
+ rmem = rd->rmem;
+ list_del(&rd->list);
+ kfree(rd);
+ break;
+ }
+ }
+ mutex_unlock(&of_rmem_assigned_device_mutex);
if (!rmem || !rmem->ops || !rmem->ops->device_release)
return;
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
index 9bb2622c23bf..f38ac90f1aa5 100644
--- a/drivers/power/reset/Kconfig
+++ b/drivers/power/reset/Kconfig
@@ -46,6 +46,16 @@ config POWER_RESET_AXXIA
Say Y if you have an Axxia family SoC.
+config POWER_RESET_BRCMKONA
+ bool "Broadcom Kona reset driver"
+ depends on ARM || COMPILE_TEST
+ default ARCH_BCM_MOBILE
+ help
+ This driver provides restart support for Broadcom Kona chips.
+
+ Say Y here if you have a Broadcom Kona-based board and you wish
+ to have restart support.
+
config POWER_RESET_BRCMSTB
bool "Broadcom STB reset driver"
depends on ARM || MIPS || COMPILE_TEST
diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile
index ab7aa8614d1f..6b6eeb3b4d7f 100644
--- a/drivers/power/reset/Makefile
+++ b/drivers/power/reset/Makefile
@@ -3,6 +3,7 @@ obj-$(CONFIG_POWER_RESET_AT91_POWEROFF) += at91-poweroff.o
obj-$(CONFIG_POWER_RESET_AT91_RESET) += at91-reset.o
obj-$(CONFIG_POWER_RESET_AT91_SAMA5D2_SHDWC) += at91-sama5d2_shdwc.o
obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o
+obj-$(CONFIG_POWER_RESET_BRCMKONA) += brcm-kona-reset.o
obj-$(CONFIG_POWER_RESET_BRCMSTB) += brcmstb-reboot.o
obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o
diff --git a/drivers/power/reset/brcm-kona-reset.c b/drivers/power/reset/brcm-kona-reset.c
new file mode 100644
index 000000000000..8eaa959d8be6
--- /dev/null
+++ b/drivers/power/reset/brcm-kona-reset.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 Broadcom
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/reboot.h>
+
+#define RSTMGR_REG_WR_ACCESS_OFFSET 0
+#define RSTMGR_REG_CHIP_SOFT_RST_OFFSET 4
+
+#define RSTMGR_WR_PASSWORD 0xa5a5
+#define RSTMGR_WR_PASSWORD_SHIFT 8
+#define RSTMGR_WR_ACCESS_ENABLE 1
+
+static void __iomem *kona_reset_base;
+
+static int kona_reset_handler(struct notifier_block *this,
+ unsigned long mode, void *cmd)
+{
+ /*
+ * A soft reset is triggered by writing a 0 to bit 0 of the soft reset
+ * register. To write to that register we must first write the password
+ * and the enable bit in the write access enable register.
+ */
+ writel((RSTMGR_WR_PASSWORD << RSTMGR_WR_PASSWORD_SHIFT) |
+ RSTMGR_WR_ACCESS_ENABLE,
+ kona_reset_base + RSTMGR_REG_WR_ACCESS_OFFSET);
+ writel(0, kona_reset_base + RSTMGR_REG_CHIP_SOFT_RST_OFFSET);
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block kona_reset_nb = {
+ .notifier_call = kona_reset_handler,
+ .priority = 128,
+};
+
+static int kona_reset_probe(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ kona_reset_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(kona_reset_base))
+ return PTR_ERR(kona_reset_base);
+
+ return register_restart_handler(&kona_reset_nb);
+}
+
+static const struct of_device_id of_match[] = {
+ { .compatible = "brcm,bcm21664-resetmgr" },
+ {},
+};
+
+static struct platform_driver bcm_kona_reset_driver = {
+ .probe = kona_reset_probe,
+ .driver = {
+ .name = "brcm-kona-reset",
+ .of_match_table = of_match,
+ },
+};
+
+builtin_platform_driver(bcm_kona_reset_driver);
diff --git a/drivers/pwm/pwm-clps711x.c b/drivers/pwm/pwm-clps711x.c
index 7d335422cfda..26ec24e457b1 100644
--- a/drivers/pwm/pwm-clps711x.c
+++ b/drivers/pwm/pwm-clps711x.c
@@ -155,7 +155,7 @@ static int clps711x_pwm_remove(struct platform_device *pdev)
}
static const struct of_device_id __maybe_unused clps711x_pwm_dt_ids[] = {
- { .compatible = "cirrus,clps711x-pwm", },
+ { .compatible = "cirrus,ep7209-pwm", },
{ }
};
MODULE_DEVICE_TABLE(of, clps711x_pwm_dt_ids);
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index cb58ef0d9b2c..91d5c05d395f 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -1,7 +1,6 @@
menu "SOC (System On Chip) specific Drivers"
source "drivers/soc/bcm/Kconfig"
-source "drivers/soc/brcmstb/Kconfig"
source "drivers/soc/fsl/qe/Kconfig"
source "drivers/soc/mediatek/Kconfig"
source "drivers/soc/qcom/Kconfig"
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index 380230f03874..b75e3bd0a01e 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -3,7 +3,6 @@
#
obj-y += bcm/
-obj-$(CONFIG_SOC_BRCMSTB) += brcmstb/
obj-$(CONFIG_ARCH_DOVE) += dove/
obj-$(CONFIG_MACH_DOVE) += dove/
obj-y += fsl/
diff --git a/drivers/soc/bcm/Kconfig b/drivers/soc/bcm/Kconfig
index 3066edea184d..97156aeed286 100644
--- a/drivers/soc/bcm/Kconfig
+++ b/drivers/soc/bcm/Kconfig
@@ -1,3 +1,5 @@
+menu "Broadcom SoC drivers"
+
config RASPBERRYPI_POWER
bool "Raspberry Pi power domain driver"
depends on ARCH_BCM2835 || COMPILE_TEST
@@ -7,3 +9,16 @@ config RASPBERRYPI_POWER
help
This enables support for the RPi power domains which can be enabled
or disabled via the RPi firmware.
+
+config SOC_BRCMSTB
+ bool "Broadcom STB SoC drivers"
+ depends on ARM
+ select SOC_BUS
+ help
+ Enables drivers for the Broadcom Set-Top Box (STB) series of chips.
+ This option alone enables only some support code, while the drivers
+ can be enabled individually within this menu.
+
+ If unsure, say N.
+
+endmenu
diff --git a/drivers/soc/bcm/Makefile b/drivers/soc/bcm/Makefile
index 63aa3eb23087..dc4fced72d21 100644
--- a/drivers/soc/bcm/Makefile
+++ b/drivers/soc/bcm/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_RASPBERRYPI_POWER) += raspberrypi-power.o
+obj-$(CONFIG_SOC_BRCMSTB) += brcmstb/
diff --git a/drivers/soc/brcmstb/Makefile b/drivers/soc/bcm/brcmstb/Makefile
index 9120b2715d3e..9120b2715d3e 100644
--- a/drivers/soc/brcmstb/Makefile
+++ b/drivers/soc/bcm/brcmstb/Makefile
diff --git a/drivers/soc/brcmstb/biuctrl.c b/drivers/soc/bcm/brcmstb/biuctrl.c
index 9049c076f9a1..3c39415d484f 100644
--- a/drivers/soc/brcmstb/biuctrl.c
+++ b/drivers/soc/bcm/brcmstb/biuctrl.c
@@ -19,6 +19,7 @@
#include <linux/io.h>
#include <linux/of_address.h>
#include <linux/syscore_ops.h>
+#include <linux/soc/brcmstb/brcmstb.h>
#define CPU_CREDIT_REG_OFFSET 0x184
#define CPU_CREDIT_REG_MCPx_WR_PAIRING_EN_MASK 0x70000000
diff --git a/drivers/soc/brcmstb/common.c b/drivers/soc/bcm/brcmstb/common.c
index 94e7335553f4..94e7335553f4 100644
--- a/drivers/soc/brcmstb/common.c
+++ b/drivers/soc/bcm/brcmstb/common.c
diff --git a/drivers/soc/brcmstb/Kconfig b/drivers/soc/brcmstb/Kconfig
deleted file mode 100644
index 7fec3b4c80a1..000000000000
--- a/drivers/soc/brcmstb/Kconfig
+++ /dev/null
@@ -1,10 +0,0 @@
-menuconfig SOC_BRCMSTB
- bool "Broadcom STB SoC drivers"
- depends on ARM
- select SOC_BUS
- help
- Enables drivers for the Broadcom Set-Top Box (STB) series of chips.
- This option alone enables only some support code, while the drivers
- can be enabled individually within this menu.
-
- If unsure, say N.
diff --git a/drivers/soc/qcom/smem_state.c b/drivers/soc/qcom/smem_state.c
index 54261decb369..d5437ca76ed9 100644
--- a/drivers/soc/qcom/smem_state.c
+++ b/drivers/soc/qcom/smem_state.c
@@ -104,26 +104,26 @@ struct qcom_smem_state *qcom_smem_state_get(struct device *dev,
if (con_id) {
index = of_property_match_string(dev->of_node,
- "qcom,state-names",
+ "qcom,smem-state-names",
con_id);
if (index < 0) {
- dev_err(dev, "missing qcom,state-names\n");
+ dev_err(dev, "missing qcom,smem-state-names\n");
return ERR_PTR(index);
}
}
ret = of_parse_phandle_with_args(dev->of_node,
- "qcom,state",
- "#qcom,state-cells",
+ "qcom,smem-states",
+ "#qcom,smem-state-cells",
index,
&args);
if (ret) {
- dev_err(dev, "failed to parse qcom,state property\n");
+ dev_err(dev, "failed to parse qcom,smem-states property\n");
return ERR_PTR(ret);
}
if (args.args_count != 1) {
- dev_err(dev, "invalid #qcom,state-cells\n");
+ dev_err(dev, "invalid #qcom,smem-state-cells\n");
return ERR_PTR(-EINVAL);
}
diff --git a/drivers/soc/qcom/smp2p.c b/drivers/soc/qcom/smp2p.c
index f1eed7f9dd67..f51fb2ea7200 100644
--- a/drivers/soc/qcom/smp2p.c
+++ b/drivers/soc/qcom/smp2p.c
@@ -196,7 +196,7 @@ static irqreturn_t qcom_smp2p_intr(int irq, void *data)
/* Match newly created entries */
for (i = smp2p->valid_entries; i < in->valid_entries; i++) {
list_for_each_entry(entry, &smp2p->inbound, node) {
- memcpy_fromio(buf, in->entries[i].name, sizeof(buf));
+ memcpy(buf, in->entries[i].name, sizeof(buf));
if (!strcmp(buf, entry->name)) {
entry->value = &in->entries[i].value;
break;
@@ -343,12 +343,13 @@ static int qcom_smp2p_outbound_entry(struct qcom_smp2p *smp2p,
/* Allocate an entry from the smem item */
strlcpy(buf, entry->name, SMP2P_MAX_ENTRY_NAME);
- memcpy_toio(out->entries[out->valid_entries].name, buf, SMP2P_MAX_ENTRY_NAME);
- out->valid_entries++;
+ memcpy(out->entries[out->valid_entries].name, buf, SMP2P_MAX_ENTRY_NAME);
/* Make the logical entry reference the physical value */
entry->value = &out->entries[out->valid_entries].value;
+ out->valid_entries++;
+
entry->state = qcom_smem_state_register(node, &smp2p_state_ops, entry);
if (IS_ERR(entry->state)) {
dev_err(smp2p->dev, "failed to register qcom_smem_state\n");
diff --git a/drivers/soc/qcom/smsm.c b/drivers/soc/qcom/smsm.c
index 6b777af1bc19..d0337b2a71c8 100644
--- a/drivers/soc/qcom/smsm.c
+++ b/drivers/soc/qcom/smsm.c
@@ -495,7 +495,7 @@ static int qcom_smsm_probe(struct platform_device *pdev)
if (!smsm->hosts)
return -ENOMEM;
- local_node = of_find_node_with_property(pdev->dev.of_node, "#qcom,state-cells");
+ local_node = of_find_node_with_property(pdev->dev.of_node, "#qcom,smem-state-cells");
if (!local_node) {
dev_err(&pdev->dev, "no state entry\n");
return -EINVAL;
diff --git a/drivers/soc/qcom/wcnss_ctrl.c b/drivers/soc/qcom/wcnss_ctrl.c
index c544f3d2c6ee..520aedd29965 100644
--- a/drivers/soc/qcom/wcnss_ctrl.c
+++ b/drivers/soc/qcom/wcnss_ctrl.c
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 2016, Linaro Ltd.
* Copyright (c) 2015, Sony Mobile Communications Inc.
*
* This program is free software; you can redistribute it and/or modify
@@ -14,8 +15,16 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/soc/qcom/smd.h>
+#include <linux/io.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/soc/qcom/wcnss_ctrl.h>
#define WCNSS_REQUEST_TIMEOUT (5 * HZ)
+#define WCNSS_CBC_TIMEOUT (10 * HZ)
+
+#define WCNSS_ACK_DONE_BOOTING 1
+#define WCNSS_ACK_COLD_BOOTING 2
#define NV_FRAGMENT_SIZE 3072
#define NVBIN_FILE "wlan/prima/WCNSS_qcom_wlan_nv.bin"
@@ -25,17 +34,19 @@
* @dev: device handle
* @channel: SMD channel handle
* @ack: completion for outstanding requests
+ * @cbc: completion for cbc complete indication
* @ack_status: status of the outstanding request
- * @download_nv_work: worker for uploading nv binary
+ * @probe_work: worker for uploading nv binary
*/
struct wcnss_ctrl {
struct device *dev;
struct qcom_smd_channel *channel;
struct completion ack;
+ struct completion cbc;
int ack_status;
- struct work_struct download_nv_work;
+ struct work_struct probe_work;
};
/* message types */
@@ -48,6 +59,11 @@ enum {
WCNSS_UPLOAD_CAL_RESP,
WCNSS_DOWNLOAD_CAL_REQ,
WCNSS_DOWNLOAD_CAL_RESP,
+ WCNSS_VBAT_LEVEL_IND,
+ WCNSS_BUILD_VERSION_REQ,
+ WCNSS_BUILD_VERSION_RESP,
+ WCNSS_PM_CONFIG_REQ,
+ WCNSS_CBC_COMPLETE_IND,
};
/**
@@ -128,7 +144,7 @@ static int wcnss_ctrl_smd_callback(struct qcom_smd_channel *channel,
version->major, version->minor,
version->version, version->revision);
- schedule_work(&wcnss->download_nv_work);
+ complete(&wcnss->ack);
break;
case WCNSS_DOWNLOAD_NV_RESP:
if (count != sizeof(*nvresp)) {
@@ -141,6 +157,10 @@ static int wcnss_ctrl_smd_callback(struct qcom_smd_channel *channel,
wcnss->ack_status = nvresp->status;
complete(&wcnss->ack);
break;
+ case WCNSS_CBC_COMPLETE_IND:
+ dev_dbg(wcnss->dev, "cold boot complete\n");
+ complete(&wcnss->cbc);
+ break;
default:
dev_info(wcnss->dev, "unknown message type %d\n", hdr->type);
break;
@@ -156,20 +176,32 @@ static int wcnss_ctrl_smd_callback(struct qcom_smd_channel *channel,
static int wcnss_request_version(struct wcnss_ctrl *wcnss)
{
struct wcnss_msg_hdr msg;
+ int ret;
msg.type = WCNSS_VERSION_REQ;
msg.len = sizeof(msg);
+ ret = qcom_smd_send(wcnss->channel, &msg, sizeof(msg));
+ if (ret < 0)
+ return ret;
+
+ ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_CBC_TIMEOUT);
+ if (!ret) {
+ dev_err(wcnss->dev, "timeout waiting for version response\n");
+ return -ETIMEDOUT;
+ }
- return qcom_smd_send(wcnss->channel, &msg, sizeof(msg));
+ return 0;
}
/**
* wcnss_download_nv() - send nv binary to WCNSS
- * @work: work struct to acquire wcnss context
+ * @wcnss: wcnss_ctrl state handle
+ * @expect_cbc: indicator to caller that an cbc event is expected
+ *
+ * Returns 0 on success. Negative errno on failure.
*/
-static void wcnss_download_nv(struct work_struct *work)
+static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc)
{
- struct wcnss_ctrl *wcnss = container_of(work, struct wcnss_ctrl, download_nv_work);
struct wcnss_download_nv_req *req;
const struct firmware *fw;
const void *data;
@@ -178,10 +210,10 @@ static void wcnss_download_nv(struct work_struct *work)
req = kzalloc(sizeof(*req) + NV_FRAGMENT_SIZE, GFP_KERNEL);
if (!req)
- return;
+ return -ENOMEM;
ret = request_firmware(&fw, NVBIN_FILE, wcnss->dev);
- if (ret) {
+ if (ret < 0) {
dev_err(wcnss->dev, "Failed to load nv file %s: %d\n",
NVBIN_FILE, ret);
goto free_req;
@@ -207,7 +239,7 @@ static void wcnss_download_nv(struct work_struct *work)
memcpy(req->fragment, data, req->frag_size);
ret = qcom_smd_send(wcnss->channel, req, req->hdr.len);
- if (ret) {
+ if (ret < 0) {
dev_err(wcnss->dev, "failed to send smd packet\n");
goto release_fw;
}
@@ -220,16 +252,58 @@ static void wcnss_download_nv(struct work_struct *work)
} while (left > 0);
ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_REQUEST_TIMEOUT);
- if (!ret)
+ if (!ret) {
dev_err(wcnss->dev, "timeout waiting for nv upload ack\n");
- else if (wcnss->ack_status != 1)
- dev_err(wcnss->dev, "nv upload response failed err: %d\n",
- wcnss->ack_status);
+ ret = -ETIMEDOUT;
+ } else {
+ *expect_cbc = wcnss->ack_status == WCNSS_ACK_COLD_BOOTING;
+ ret = 0;
+ }
release_fw:
release_firmware(fw);
free_req:
kfree(req);
+
+ return ret;
+}
+
+/**
+ * qcom_wcnss_open_channel() - open additional SMD channel to WCNSS
+ * @wcnss: wcnss handle, retrieved from drvdata
+ * @name: SMD channel name
+ * @cb: callback to handle incoming data on the channel
+ */
+struct qcom_smd_channel *qcom_wcnss_open_channel(void *wcnss, const char *name, qcom_smd_cb_t cb)
+{
+ struct wcnss_ctrl *_wcnss = wcnss;
+
+ return qcom_smd_open_channel(_wcnss->channel, name, cb);
+}
+EXPORT_SYMBOL(qcom_wcnss_open_channel);
+
+static void wcnss_async_probe(struct work_struct *work)
+{
+ struct wcnss_ctrl *wcnss = container_of(work, struct wcnss_ctrl, probe_work);
+ bool expect_cbc;
+ int ret;
+
+ ret = wcnss_request_version(wcnss);
+ if (ret < 0)
+ return;
+
+ ret = wcnss_download_nv(wcnss, &expect_cbc);
+ if (ret < 0)
+ return;
+
+ /* Wait for pending cold boot completion if indicated by the nv downloader */
+ if (expect_cbc) {
+ ret = wait_for_completion_timeout(&wcnss->cbc, WCNSS_REQUEST_TIMEOUT);
+ if (!ret)
+ dev_err(wcnss->dev, "expected cold boot completion\n");
+ }
+
+ of_platform_populate(wcnss->dev->of_node, NULL, NULL, wcnss->dev);
}
static int wcnss_ctrl_probe(struct qcom_smd_device *sdev)
@@ -244,25 +318,38 @@ static int wcnss_ctrl_probe(struct qcom_smd_device *sdev)
wcnss->channel = sdev->channel;
init_completion(&wcnss->ack);
- INIT_WORK(&wcnss->download_nv_work, wcnss_download_nv);
+ init_completion(&wcnss->cbc);
+ INIT_WORK(&wcnss->probe_work, wcnss_async_probe);
qcom_smd_set_drvdata(sdev->channel, wcnss);
+ dev_set_drvdata(&sdev->dev, wcnss);
+
+ schedule_work(&wcnss->probe_work);
+
+ return 0;
+}
+
+static void wcnss_ctrl_remove(struct qcom_smd_device *sdev)
+{
+ struct wcnss_ctrl *wcnss = qcom_smd_get_drvdata(sdev->channel);
- return wcnss_request_version(wcnss);
+ cancel_work_sync(&wcnss->probe_work);
+ of_platform_depopulate(&sdev->dev);
}
-static const struct qcom_smd_id wcnss_ctrl_smd_match[] = {
- { .name = "WCNSS_CTRL" },
+static const struct of_device_id wcnss_ctrl_of_match[] = {
+ { .compatible = "qcom,wcnss", },
{}
};
static struct qcom_smd_driver wcnss_ctrl_driver = {
.probe = wcnss_ctrl_probe,
+ .remove = wcnss_ctrl_remove,
.callback = wcnss_ctrl_smd_callback,
- .smd_match_table = wcnss_ctrl_smd_match,
.driver = {
.name = "qcom_wcnss_ctrl",
.owner = THIS_MODULE,
+ .of_match_table = wcnss_ctrl_of_match,
},
};
diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile
index 151fcd3f025b..623039c3514c 100644
--- a/drivers/soc/renesas/Makefile
+++ b/drivers/soc/renesas/Makefile
@@ -1,7 +1,9 @@
obj-$(CONFIG_ARCH_R8A7779) += rcar-sysc.o r8a7779-sysc.o
obj-$(CONFIG_ARCH_R8A7790) += rcar-sysc.o r8a7790-sysc.o
obj-$(CONFIG_ARCH_R8A7791) += rcar-sysc.o r8a7791-sysc.o
+obj-$(CONFIG_ARCH_R8A7792) += rcar-sysc.o r8a7792-sysc.o
# R-Car M2-N is identical to R-Car M2-W w.r.t. power domains.
obj-$(CONFIG_ARCH_R8A7793) += rcar-sysc.o r8a7791-sysc.o
obj-$(CONFIG_ARCH_R8A7794) += rcar-sysc.o r8a7794-sysc.o
obj-$(CONFIG_ARCH_R8A7795) += rcar-sysc.o r8a7795-sysc.o
+obj-$(CONFIG_ARCH_R8A7796) += rcar-sysc.o r8a7796-sysc.o
diff --git a/drivers/soc/renesas/r8a7792-sysc.c b/drivers/soc/renesas/r8a7792-sysc.c
new file mode 100644
index 000000000000..ca7467d7b7ec
--- /dev/null
+++ b/drivers/soc/renesas/r8a7792-sysc.c
@@ -0,0 +1,34 @@
+/*
+ * Renesas R-Car V2H (R8A7792) System Controller
+ *
+ * Copyright (C) 2016 Cogent Embedded Inc.
+ *
+ * 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; version 2 of the License.
+ */
+
+#include <linux/bug.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+
+#include <dt-bindings/power/r8a7792-sysc.h>
+
+#include "rcar-sysc.h"
+
+static const struct rcar_sysc_area r8a7792_areas[] __initconst = {
+ { "always-on", 0, 0, R8A7792_PD_ALWAYS_ON, -1, PD_ALWAYS_ON },
+ { "ca15-scu", 0x180, 0, R8A7792_PD_CA15_SCU, R8A7792_PD_ALWAYS_ON,
+ PD_SCU },
+ { "ca15-cpu0", 0x40, 0, R8A7792_PD_CA15_CPU0, R8A7792_PD_CA15_SCU,
+ PD_CPU_NOCR },
+ { "ca15-cpu1", 0x40, 1, R8A7792_PD_CA15_CPU1, R8A7792_PD_CA15_SCU,
+ PD_CPU_NOCR },
+ { "sgx", 0xc0, 0, R8A7792_PD_SGX, R8A7792_PD_ALWAYS_ON },
+ { "imp", 0x140, 0, R8A7792_PD_IMP, R8A7792_PD_ALWAYS_ON },
+};
+
+const struct rcar_sysc_info r8a7792_sysc_info __initconst = {
+ .areas = r8a7792_areas,
+ .num_areas = ARRAY_SIZE(r8a7792_areas),
+};
diff --git a/drivers/soc/renesas/r8a7796-sysc.c b/drivers/soc/renesas/r8a7796-sysc.c
new file mode 100644
index 000000000000..f700c842b9e1
--- /dev/null
+++ b/drivers/soc/renesas/r8a7796-sysc.c
@@ -0,0 +1,48 @@
+/*
+ * Renesas R-Car M3-W System Controller
+ *
+ * Copyright (C) 2016 Glider bvba
+ *
+ * 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; version 2 of the License.
+ */
+
+#include <linux/bug.h>
+#include <linux/kernel.h>
+
+#include <dt-bindings/power/r8a7796-sysc.h>
+
+#include "rcar-sysc.h"
+
+static const struct rcar_sysc_area r8a7796_areas[] __initconst = {
+ { "always-on", 0, 0, R8A7796_PD_ALWAYS_ON, -1, PD_ALWAYS_ON },
+ { "ca57-scu", 0x1c0, 0, R8A7796_PD_CA57_SCU, R8A7796_PD_ALWAYS_ON,
+ PD_SCU },
+ { "ca57-cpu0", 0x80, 0, R8A7796_PD_CA57_CPU0, R8A7796_PD_CA57_SCU,
+ PD_CPU_NOCR },
+ { "ca57-cpu1", 0x80, 1, R8A7796_PD_CA57_CPU1, R8A7796_PD_CA57_SCU,
+ PD_CPU_NOCR },
+ { "ca53-scu", 0x140, 0, R8A7796_PD_CA53_SCU, R8A7796_PD_ALWAYS_ON,
+ PD_SCU },
+ { "ca53-cpu0", 0x200, 0, R8A7796_PD_CA53_CPU0, R8A7796_PD_CA53_SCU,
+ PD_CPU_NOCR },
+ { "ca53-cpu1", 0x200, 1, R8A7796_PD_CA53_CPU1, R8A7796_PD_CA53_SCU,
+ PD_CPU_NOCR },
+ { "ca53-cpu2", 0x200, 2, R8A7796_PD_CA53_CPU2, R8A7796_PD_CA53_SCU,
+ PD_CPU_NOCR },
+ { "ca53-cpu3", 0x200, 3, R8A7796_PD_CA53_CPU3, R8A7796_PD_CA53_SCU,
+ PD_CPU_NOCR },
+ { "cr7", 0x240, 0, R8A7796_PD_CR7, R8A7796_PD_ALWAYS_ON },
+ { "a3vc", 0x380, 0, R8A7796_PD_A3VC, R8A7796_PD_ALWAYS_ON },
+ { "a2vc0", 0x3c0, 0, R8A7796_PD_A2VC0, R8A7796_PD_A3VC },
+ { "a2vc1", 0x3c0, 1, R8A7796_PD_A2VC1, R8A7796_PD_A3VC },
+ { "3dg-a", 0x100, 0, R8A7796_PD_3DG_A, R8A7796_PD_ALWAYS_ON },
+ { "3dg-b", 0x100, 1, R8A7796_PD_3DG_B, R8A7796_PD_3DG_A },
+ { "a3ir", 0x180, 0, R8A7796_PD_A3IR, R8A7796_PD_ALWAYS_ON },
+};
+
+const struct rcar_sysc_info r8a7796_sysc_info __initconst = {
+ .areas = r8a7796_areas,
+ .num_areas = ARRAY_SIZE(r8a7796_areas),
+};
diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c
index 79dbc770895f..65c8e1eb90c0 100644
--- a/drivers/soc/renesas/rcar-sysc.c
+++ b/drivers/soc/renesas/rcar-sysc.c
@@ -164,15 +164,6 @@ static bool rcar_sysc_power_is_off(const struct rcar_sysc_ch *sysc_ch)
return false;
}
-void __iomem *rcar_sysc_init(phys_addr_t base)
-{
- rcar_sysc_base = ioremap_nocache(base, PAGE_SIZE);
- if (!rcar_sysc_base)
- panic("unable to ioremap R-Car SYSC hardware block\n");
-
- return rcar_sysc_base;
-}
-
struct rcar_sysc_pd {
struct generic_pm_domain genpd;
struct rcar_sysc_ch ch;
@@ -293,6 +284,9 @@ static const struct of_device_id rcar_sysc_matches[] = {
#ifdef CONFIG_ARCH_R8A7791
{ .compatible = "renesas,r8a7791-sysc", .data = &r8a7791_sysc_info },
#endif
+#ifdef CONFIG_ARCH_R8A7792
+ { .compatible = "renesas,r8a7792-sysc", .data = &r8a7792_sysc_info },
+#endif
#ifdef CONFIG_ARCH_R8A7793
/* R-Car M2-N is identical to R-Car M2-W w.r.t. power domains. */
{ .compatible = "renesas,r8a7793-sysc", .data = &r8a7791_sysc_info },
@@ -303,6 +297,9 @@ static const struct of_device_id rcar_sysc_matches[] = {
#ifdef CONFIG_ARCH_R8A7795
{ .compatible = "renesas,r8a7795-sysc", .data = &r8a7795_sysc_info },
#endif
+#ifdef CONFIG_ARCH_R8A7796
+ { .compatible = "renesas,r8a7796-sysc", .data = &r8a7796_sysc_info },
+#endif
{ /* sentinel */ }
};
@@ -322,6 +319,9 @@ static int __init rcar_sysc_pd_init(void)
unsigned int i;
int error;
+ if (rcar_sysc_base)
+ return 0;
+
np = of_find_matching_node_and_match(NULL, rcar_sysc_matches, &match);
if (!np)
return -ENODEV;
@@ -392,10 +392,35 @@ static int __init rcar_sysc_pd_init(void)
domains->domains[area->isr_bit] = &pd->genpd;
}
- of_genpd_add_provider_onecell(np, &domains->onecell_data);
+ error = of_genpd_add_provider_onecell(np, &domains->onecell_data);
out_put:
of_node_put(np);
return error;
}
early_initcall(rcar_sysc_pd_init);
+
+void __init rcar_sysc_init(phys_addr_t base, u32 syscier)
+{
+ u32 syscimr;
+
+ if (!rcar_sysc_pd_init())
+ return;
+
+ rcar_sysc_base = ioremap_nocache(base, PAGE_SIZE);
+
+ /*
+ * Mask all interrupt sources to prevent the CPU from receiving them.
+ * Make sure not to clear reserved bits that were set before.
+ */
+ syscimr = ioread32(rcar_sysc_base + SYSCIMR);
+ syscimr |= syscier;
+ pr_debug("%s: syscimr = 0x%08x\n", __func__, syscimr);
+ iowrite32(syscimr, rcar_sysc_base + SYSCIMR);
+
+ /*
+ * SYSC needs all interrupt sources enabled to control power.
+ */
+ pr_debug("%s: syscier = 0x%08x\n", __func__, syscier);
+ iowrite32(syscier, rcar_sysc_base + SYSCIER);
+}
diff --git a/drivers/soc/renesas/rcar-sysc.h b/drivers/soc/renesas/rcar-sysc.h
index 5e766174c2f4..77dbe861473f 100644
--- a/drivers/soc/renesas/rcar-sysc.h
+++ b/drivers/soc/renesas/rcar-sysc.h
@@ -53,6 +53,8 @@ struct rcar_sysc_info {
extern const struct rcar_sysc_info r8a7779_sysc_info;
extern const struct rcar_sysc_info r8a7790_sysc_info;
extern const struct rcar_sysc_info r8a7791_sysc_info;
+extern const struct rcar_sysc_info r8a7792_sysc_info;
extern const struct rcar_sysc_info r8a7794_sysc_info;
extern const struct rcar_sysc_info r8a7795_sysc_info;
+extern const struct rcar_sysc_info r8a7796_sysc_info;
#endif /* __SOC_RENESAS_RCAR_SYSC_H__ */
diff --git a/drivers/soc/samsung/Kconfig b/drivers/soc/samsung/Kconfig
index d7fc123006a3..245533907d1b 100644
--- a/drivers/soc/samsung/Kconfig
+++ b/drivers/soc/samsung/Kconfig
@@ -10,4 +10,8 @@ config EXYNOS_PMU
bool "Exynos PMU controller driver" if COMPILE_TEST
depends on (ARM && ARCH_EXYNOS) || ((ARM || ARM64) && COMPILE_TEST)
+config EXYNOS_PM_DOMAINS
+ bool "Exynos PM domains" if COMPILE_TEST
+ depends on PM_GENERIC_DOMAINS || COMPILE_TEST
+
endif
diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile
index f64ac4d80564..3619f2ecddaa 100644
--- a/drivers/soc/samsung/Makefile
+++ b/drivers/soc/samsung/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o exynos3250-pmu.o exynos4-pmu.o \
exynos5250-pmu.o exynos5420-pmu.o
+obj-$(CONFIG_EXYNOS_PM_DOMAINS) += pm_domains.o
diff --git a/arch/arm/mach-exynos/pm_domains.c b/drivers/soc/samsung/pm_domains.c
index 875a2bab64f6..f60515eefb66 100644
--- a/arch/arm/mach-exynos/pm_domains.c
+++ b/drivers/soc/samsung/pm_domains.c
@@ -23,9 +23,13 @@
#include <linux/of_platform.h>
#include <linux/sched.h>
-#define INT_LOCAL_PWR_EN 0x7
#define MAX_CLK_PER_DOMAIN 4
+struct exynos_pm_domain_config {
+ /* Value for LOCAL_PWR_CFG and STATUS fields for each domain */
+ u32 local_pwr_cfg;
+};
+
/*
* Exynos specific wrapper around the generic power domain
*/
@@ -38,6 +42,7 @@ struct exynos_pm_domain {
struct clk *clk[MAX_CLK_PER_DOMAIN];
struct clk *pclk[MAX_CLK_PER_DOMAIN];
struct clk *asb_clk[MAX_CLK_PER_DOMAIN];
+ u32 local_pwr_cfg;
};
static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on)
@@ -69,13 +74,13 @@ static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on)
}
}
- pwr = power_on ? INT_LOCAL_PWR_EN : 0;
+ pwr = power_on ? pd->local_pwr_cfg : 0;
__raw_writel(pwr, base);
/* Wait max 1ms */
timeout = 10;
- while ((__raw_readl(base + 0x4) & INT_LOCAL_PWR_EN) != pwr) {
+ while ((__raw_readl(base + 0x4) & pd->local_pwr_cfg) != pwr) {
if (!timeout) {
op = (power_on) ? "enable" : "disable";
pr_err("Power domain %s %s failed\n", domain->name, op);
@@ -119,14 +124,30 @@ static int exynos_pd_power_off(struct generic_pm_domain *domain)
return exynos_pd_power(domain, false);
}
+static const struct exynos_pm_domain_config exynos4210_cfg __initconst = {
+ .local_pwr_cfg = 0x7,
+};
+
+static const struct of_device_id exynos_pm_domain_of_match[] __initconst = {
+ {
+ .compatible = "samsung,exynos4210-pd",
+ .data = &exynos4210_cfg,
+ },
+ { },
+};
+
static __init int exynos4_pm_init_power_domain(void)
{
struct device_node *np;
+ const struct of_device_id *match;
- for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") {
+ for_each_matching_node_and_match(np, exynos_pm_domain_of_match, &match) {
+ const struct exynos_pm_domain_config *pm_domain_cfg;
struct exynos_pm_domain *pd;
int on, i;
+ pm_domain_cfg = match->data;
+
pd = kzalloc(sizeof(*pd), GFP_KERNEL);
if (!pd) {
pr_err("%s: failed to allocate memory for domain\n",
@@ -153,6 +174,7 @@ static __init int exynos4_pm_init_power_domain(void)
pd->pd.power_off = exynos_pd_power_off;
pd->pd.power_on = exynos_pd_power_on;
+ pd->local_pwr_cfg = pm_domain_cfg->local_pwr_cfg;
for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) {
char clk_name[8];
@@ -185,14 +207,14 @@ static __init int exynos4_pm_init_power_domain(void)
clk_put(pd->oscclk);
no_clk:
- on = __raw_readl(pd->base + 0x4) & INT_LOCAL_PWR_EN;
+ on = __raw_readl(pd->base + 0x4) & pd->local_pwr_cfg;
pm_genpd_init(&pd->pd, NULL, !on);
of_genpd_add_provider_simple(np, &pd->pd);
}
/* Assign the child power domains to their parents */
- for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") {
+ for_each_matching_node(np, exynos_pm_domain_of_match) {
struct generic_pm_domain *child_domain, *parent_domain;
struct of_phandle_args args;
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index bb173456bbff..71c834f3847e 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -51,6 +51,7 @@
#define PMC_CNTRL_CPU_PWRREQ_POLARITY (1 << 15) /* CPU pwr req polarity */
#define PMC_CNTRL_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */
#define PMC_CNTRL_INTR_POLARITY (1 << 17) /* inverts INTR polarity */
+#define PMC_CNTRL_MAIN_RST (1 << 4)
#define DPD_SAMPLE 0x020
#define DPD_SAMPLE_ENABLE (1 << 0)
@@ -80,6 +81,14 @@
#define PMC_SENSOR_CTRL_SCRATCH_WRITE (1 << 2)
#define PMC_SENSOR_CTRL_ENABLE_RST (1 << 1)
+#define PMC_RST_STATUS 0x1b4
+#define PMC_RST_STATUS_POR 0
+#define PMC_RST_STATUS_WATCHDOG 1
+#define PMC_RST_STATUS_SENSOR 2
+#define PMC_RST_STATUS_SW_MAIN 3
+#define PMC_RST_STATUS_LP0 4
+#define PMC_RST_STATUS_AOTAG 5
+
#define IO_DPD_REQ 0x1b8
#define IO_DPD_REQ_CODE_IDLE (0 << 30)
#define IO_DPD_REQ_CODE_OFF (1 << 30)
@@ -399,6 +408,7 @@ static int tegra_powergate_power_up(struct tegra_powergate *pg,
disable_clks:
tegra_powergate_disable_clocks(pg);
usleep_range(10, 20);
+
powergate_off:
tegra_powergate_set(pg->id, false);
@@ -436,6 +446,7 @@ assert_resets:
usleep_range(10, 20);
tegra_powergate_reset_deassert(pg);
usleep_range(10, 20);
+
disable_clks:
tegra_powergate_disable_clocks(pg);
@@ -540,6 +551,9 @@ int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk,
struct tegra_powergate pg;
int err;
+ if (!tegra_powergate_is_available(id))
+ return -EINVAL;
+
pg.id = id;
pg.clks = &clk;
pg.num_clks = 1;
@@ -638,9 +652,10 @@ static int tegra_pmc_restart_notify(struct notifier_block *this,
tegra_pmc_writel(value, PMC_SCRATCH0);
- value = tegra_pmc_readl(0);
- value |= 0x10;
- tegra_pmc_writel(value, 0);
+ /* reset everything but PMC_SCRATCH0 and PMC_RST_STATUS */
+ value = tegra_pmc_readl(PMC_CNTRL);
+ value |= PMC_CNTRL_MAIN_RST;
+ tegra_pmc_writel(value, PMC_CNTRL);
return NOTIFY_DONE;
}
@@ -722,13 +737,14 @@ static int tegra_powergate_of_get_clks(struct tegra_powergate *pg,
err:
while (i--)
clk_put(pg->clks[i]);
+
kfree(pg->clks);
return err;
}
static int tegra_powergate_of_get_resets(struct tegra_powergate *pg,
- struct device_node *np)
+ struct device_node *np, bool off)
{
struct reset_control *rst;
unsigned int i, count;
@@ -748,6 +764,16 @@ static int tegra_powergate_of_get_resets(struct tegra_powergate *pg,
err = PTR_ERR(pg->resets[i]);
goto error;
}
+
+ if (off)
+ err = reset_control_assert(pg->resets[i]);
+ else
+ err = reset_control_deassert(pg->resets[i]);
+
+ if (err) {
+ reset_control_put(pg->resets[i]);
+ goto error;
+ }
}
pg->num_resets = count;
@@ -757,6 +783,7 @@ static int tegra_powergate_of_get_resets(struct tegra_powergate *pg,
error:
while (i--)
reset_control_put(pg->resets[i]);
+
kfree(pg->resets);
return err;
@@ -765,16 +792,19 @@ error:
static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np)
{
struct tegra_powergate *pg;
+ int id, err;
bool off;
- int id;
pg = kzalloc(sizeof(*pg), GFP_KERNEL);
if (!pg)
- goto error;
+ return;
id = tegra_powergate_lookup(pmc, np->name);
- if (id < 0)
+ if (id < 0) {
+ dev_err(pmc->dev, "powergate lookup failed for %s: %d\n",
+ np->name, id);
goto free_mem;
+ }
/*
* Clear the bit for this powergate so it cannot be managed
@@ -788,31 +818,64 @@ static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np)
pg->genpd.power_on = tegra_genpd_power_on;
pg->pmc = pmc;
- if (tegra_powergate_of_get_clks(pg, np))
+ off = !tegra_powergate_is_powered(pg->id);
+
+ err = tegra_powergate_of_get_clks(pg, np);
+ if (err < 0) {
+ dev_err(pmc->dev, "failed to get clocks for %s: %d\n",
+ np->name, err);
goto set_available;
+ }
- if (tegra_powergate_of_get_resets(pg, np))
+ err = tegra_powergate_of_get_resets(pg, np, off);
+ if (err < 0) {
+ dev_err(pmc->dev, "failed to get resets for %s: %d\n",
+ np->name, err);
goto remove_clks;
+ }
- off = !tegra_powergate_is_powered(pg->id);
+ if (!IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS))
+ goto power_on_cleanup;
+
+ /*
+ * FIXME: If XHCI is enabled for Tegra, then power-up the XUSB
+ * host and super-speed partitions. Once the XHCI driver
+ * manages the partitions itself this code can be removed. Note
+ * that we don't register these partitions with the genpd core
+ * to avoid it from powering down the partitions as they appear
+ * to be unused.
+ */
+ if (IS_ENABLED(CONFIG_USB_XHCI_TEGRA) &&
+ (id == TEGRA_POWERGATE_XUSBA || id == TEGRA_POWERGATE_XUSBC))
+ goto power_on_cleanup;
pm_genpd_init(&pg->genpd, NULL, off);
- if (of_genpd_add_provider_simple(np, &pg->genpd))
+ err = of_genpd_add_provider_simple(np, &pg->genpd);
+ if (err < 0) {
+ dev_err(pmc->dev, "failed to add genpd provider for %s: %d\n",
+ np->name, err);
goto remove_resets;
+ }
dev_dbg(pmc->dev, "added power domain %s\n", pg->genpd.name);
return;
+power_on_cleanup:
+ if (off)
+ WARN_ON(tegra_powergate_power_up(pg, true));
+
remove_resets:
while (pg->num_resets--)
reset_control_put(pg->resets[pg->num_resets]);
+
kfree(pg->resets);
remove_clks:
while (pg->num_clks--)
clk_put(pg->clks[pg->num_clks]);
+
kfree(pg->clks);
set_available:
@@ -820,16 +883,20 @@ set_available:
free_mem:
kfree(pg);
-
-error:
- dev_err(pmc->dev, "failed to create power domain for %s\n", np->name);
}
-static void tegra_powergate_init(struct tegra_pmc *pmc)
+static void tegra_powergate_init(struct tegra_pmc *pmc,
+ struct device_node *parent)
{
struct device_node *np, *child;
+ unsigned int i;
+
+ /* Create a bitmap of the available and valid partitions */
+ for (i = 0; i < pmc->soc->num_powergates; i++)
+ if (pmc->soc->powergates[i])
+ set_bit(i, pmc->powergates_available);
- np = of_get_child_by_name(pmc->dev->of_node, "powergates");
+ np = of_get_child_by_name(parent, "powergates");
if (!np)
return;
@@ -1205,6 +1272,14 @@ static int tegra_pmc_probe(struct platform_device *pdev)
struct resource *res;
int err;
+ /*
+ * Early initialisation should have configured an initial
+ * register mapping and setup the soc data pointer. If these
+ * are not valid then something went badly wrong!
+ */
+ if (WARN_ON(!pmc->base || !pmc->soc))
+ return -ENODEV;
+
err = tegra_pmc_parse_dt(pmc, pdev->dev.of_node);
if (err < 0)
return err;
@@ -1242,8 +1317,6 @@ static int tegra_pmc_probe(struct platform_device *pdev)
return err;
}
- tegra_powergate_init(pmc);
-
mutex_lock(&pmc->powergates_lock);
iounmap(pmc->base);
pmc->base = base;
@@ -1477,10 +1550,11 @@ static int __init tegra_pmc_early_init(void)
const struct of_device_id *match;
struct device_node *np;
struct resource regs;
- unsigned int i;
bool invert;
u32 value;
+ mutex_init(&pmc->powergates_lock);
+
np = of_find_matching_node_and_match(NULL, tegra_pmc_match, &match);
if (!np) {
/*
@@ -1515,39 +1589,40 @@ static int __init tegra_pmc_early_init(void)
*/
if (of_address_to_resource(np, 0, &regs) < 0) {
pr_err("failed to get PMC registers\n");
+ of_node_put(np);
return -ENXIO;
}
-
- pmc->soc = match->data;
}
pmc->base = ioremap_nocache(regs.start, resource_size(&regs));
if (!pmc->base) {
pr_err("failed to map PMC registers\n");
+ of_node_put(np);
return -ENXIO;
}
- /* Create a bit-map of the available and valid partitions */
- for (i = 0; i < pmc->soc->num_powergates; i++)
- if (pmc->soc->powergates[i])
- set_bit(i, pmc->powergates_available);
+ if (np) {
+ pmc->soc = match->data;
- mutex_init(&pmc->powergates_lock);
+ tegra_powergate_init(pmc, np);
- /*
- * Invert the interrupt polarity if a PMC device tree node exists and
- * contains the nvidia,invert-interrupt property.
- */
- invert = of_property_read_bool(np, "nvidia,invert-interrupt");
+ /*
+ * Invert the interrupt polarity if a PMC device tree node
+ * exists and contains the nvidia,invert-interrupt property.
+ */
+ invert = of_property_read_bool(np, "nvidia,invert-interrupt");
- value = tegra_pmc_readl(PMC_CNTRL);
+ value = tegra_pmc_readl(PMC_CNTRL);
- if (invert)
- value |= PMC_CNTRL_INTR_POLARITY;
- else
- value &= ~PMC_CNTRL_INTR_POLARITY;
+ if (invert)
+ value |= PMC_CNTRL_INTR_POLARITY;
+ else
+ value &= ~PMC_CNTRL_INTR_POLARITY;
- tegra_pmc_writel(value, PMC_CNTRL);
+ tegra_pmc_writel(value, PMC_CNTRL);
+
+ of_node_put(np);
+ }
return 0;
}
diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c
index 5beafd2d2218..ac1328629baa 100644
--- a/drivers/tty/serial/clps711x.c
+++ b/drivers/tty/serial/clps711x.c
@@ -539,7 +539,7 @@ static int uart_clps711x_remove(struct platform_device *pdev)
}
static const struct of_device_id __maybe_unused clps711x_uart_dt_ids[] = {
- { .compatible = "cirrus,clps711x-uart", },
+ { .compatible = "cirrus,ep7209-uart", },
{ }
};
MODULE_DEVICE_TABLE(of, clps711x_uart_dt_ids);
diff --git a/drivers/video/fbdev/clps711x-fb.c b/drivers/video/fbdev/clps711x-fb.c
index 649b32f78c08..ff561073ee4e 100644
--- a/drivers/video/fbdev/clps711x-fb.c
+++ b/drivers/video/fbdev/clps711x-fb.c
@@ -273,7 +273,7 @@ static int clps711x_fb_probe(struct platform_device *pdev)
}
cfb->syscon =
- syscon_regmap_lookup_by_compatible("cirrus,clps711x-syscon1");
+ syscon_regmap_lookup_by_compatible("cirrus,ep7209-syscon1");
if (IS_ERR(cfb->syscon)) {
ret = PTR_ERR(cfb->syscon);
goto out_fb_release;
@@ -376,7 +376,7 @@ static int clps711x_fb_remove(struct platform_device *pdev)
}
static const struct of_device_id clps711x_fb_dt_ids[] = {
- { .compatible = "cirrus,clps711x-fb", },
+ { .compatible = "cirrus,ep7209-fb", },
{ }
};
MODULE_DEVICE_TABLE(of, clps711x_fb_dt_ids);
diff --git a/include/dt-bindings/power/r8a7796-sysc.h b/include/dt-bindings/power/r8a7796-sysc.h
new file mode 100644
index 000000000000..5b4daab44daa
--- /dev/null
+++ b/include/dt-bindings/power/r8a7796-sysc.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 Glider bvba
+ *
+ * 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; version 2 of the License.
+ */
+#ifndef __DT_BINDINGS_POWER_R8A7796_SYSC_H__
+#define __DT_BINDINGS_POWER_R8A7796_SYSC_H__
+
+/*
+ * These power domain indices match the numbers of the interrupt bits
+ * representing the power areas in the various Interrupt Registers
+ * (e.g. SYSCISR, Interrupt Status Register)
+ */
+
+#define R8A7796_PD_CA57_CPU0 0
+#define R8A7796_PD_CA57_CPU1 1
+#define R8A7796_PD_CA53_CPU0 5
+#define R8A7796_PD_CA53_CPU1 6
+#define R8A7796_PD_CA53_CPU2 7
+#define R8A7796_PD_CA53_CPU3 8
+#define R8A7796_PD_CA57_SCU 12
+#define R8A7796_PD_CR7 13
+#define R8A7796_PD_A3VC 14
+#define R8A7796_PD_3DG_A 17
+#define R8A7796_PD_3DG_B 18
+#define R8A7796_PD_CA53_SCU 21
+#define R8A7796_PD_A3IR 24
+#define R8A7796_PD_A2VC0 25
+#define R8A7796_PD_A2VC1 26
+
+/* Always-on power area */
+#define R8A7796_PD_ALWAYS_ON 32
+
+#endif /* __DT_BINDINGS_POWER_R8A7796_SYSC_H__ */
diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h
index ad2f67054372..1779cda99ef7 100644
--- a/include/linux/of_reserved_mem.h
+++ b/include/linux/of_reserved_mem.h
@@ -1,7 +1,8 @@
#ifndef __OF_RESERVED_MEM_H
#define __OF_RESERVED_MEM_H
-struct device;
+#include <linux/device.h>
+
struct of_phandle_args;
struct reserved_mem_ops;
@@ -28,14 +29,17 @@ typedef int (*reservedmem_of_init_fn)(struct reserved_mem *rmem);
_OF_DECLARE(reservedmem, name, compat, init, reservedmem_of_init_fn)
#ifdef CONFIG_OF_RESERVED_MEM
-int of_reserved_mem_device_init(struct device *dev);
+
+int of_reserved_mem_device_init_by_idx(struct device *dev,
+ struct device_node *np, int idx);
void of_reserved_mem_device_release(struct device *dev);
void fdt_init_reserved_mem(void);
void fdt_reserved_mem_save_node(unsigned long node, const char *uname,
phys_addr_t base, phys_addr_t size);
#else
-static inline int of_reserved_mem_device_init(struct device *dev)
+static inline int of_reserved_mem_device_init_by_idx(struct device *dev,
+ struct device_node *np, int idx)
{
return -ENOSYS;
}
@@ -46,4 +50,19 @@ static inline void fdt_reserved_mem_save_node(unsigned long node,
const char *uname, phys_addr_t base, phys_addr_t size) { }
#endif
+/**
+ * of_reserved_mem_device_init() - assign reserved memory region to given device
+ * @dev: Pointer to the device to configure
+ *
+ * This function assigns respective DMA-mapping operations based on the first
+ * reserved memory region specified by 'memory-region' property in device tree
+ * node of the given device.
+ *
+ * Returns error code or zero on success.
+ */
+static inline int of_reserved_mem_device_init(struct device *dev)
+{
+ return of_reserved_mem_device_init_by_idx(dev, dev->of_node, 0);
+}
+
#endif /* __OF_RESERVED_MEM_H */
diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h
index 9e12000914b3..cc32ab852fbc 100644
--- a/include/linux/qcom_scm.h
+++ b/include/linux/qcom_scm.h
@@ -29,6 +29,14 @@ extern bool qcom_scm_hdcp_available(void);
extern int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt,
u32 *resp);
+extern bool qcom_scm_pas_supported(u32 peripheral);
+extern int qcom_scm_pas_init_image(u32 peripheral, const void *metadata,
+ size_t size);
+extern int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr,
+ phys_addr_t size);
+extern int qcom_scm_pas_auth_and_reset(u32 peripheral);
+extern int qcom_scm_pas_shutdown(u32 peripheral);
+
#define QCOM_SCM_CPU_PWR_DOWN_L2_ON 0x0
#define QCOM_SCM_CPU_PWR_DOWN_L2_OFF 0x1
diff --git a/include/linux/scpi_protocol.h b/include/linux/scpi_protocol.h
index 35de50a65665..dc5f989be226 100644
--- a/include/linux/scpi_protocol.h
+++ b/include/linux/scpi_protocol.h
@@ -70,6 +70,8 @@ struct scpi_ops {
int (*sensor_get_capability)(u16 *sensors);
int (*sensor_get_info)(u16 sensor_id, struct scpi_sensor_info *);
int (*sensor_get_value)(u16, u64 *);
+ int (*device_get_power_state)(u16);
+ int (*device_set_power_state)(u16, u8);
};
#if IS_REACHABLE(CONFIG_ARM_SCPI_PROTOCOL)
diff --git a/include/linux/soc/qcom/wcnss_ctrl.h b/include/linux/soc/qcom/wcnss_ctrl.h
new file mode 100644
index 000000000000..a37bc5538f19
--- /dev/null
+++ b/include/linux/soc/qcom/wcnss_ctrl.h
@@ -0,0 +1,8 @@
+#ifndef __WCNSS_CTRL_H__
+#define __WCNSS_CTRL_H__
+
+#include <linux/soc/qcom/smd.h>
+
+struct qcom_smd_channel *qcom_wcnss_open_channel(void *wcnss, const char *name, qcom_smd_cb_t cb);
+
+#endif
diff --git a/include/linux/soc/renesas/rcar-sysc.h b/include/linux/soc/renesas/rcar-sysc.h
index 92fc613ab23d..7b8b280c181b 100644
--- a/include/linux/soc/renesas/rcar-sysc.h
+++ b/include/linux/soc/renesas/rcar-sysc.h
@@ -11,6 +11,6 @@ struct rcar_sysc_ch {
int rcar_sysc_power_down(const struct rcar_sysc_ch *sysc_ch);
int rcar_sysc_power_up(const struct rcar_sysc_ch *sysc_ch);
-void __iomem *rcar_sysc_init(phys_addr_t base);
+void rcar_sysc_init(phys_addr_t base, u32 syscier);
#endif /* __LINUX_SOC_RENESAS_RCAR_SYSC_H__ */
diff --git a/include/media/videobuf2-dma-contig.h b/include/media/videobuf2-dma-contig.h
index 2087c9a68be3..f7dc8401817e 100644
--- a/include/media/videobuf2-dma-contig.h
+++ b/include/media/videobuf2-dma-contig.h
@@ -35,6 +35,8 @@ static inline void *vb2_dma_contig_init_ctx(struct device *dev)
}
void vb2_dma_contig_cleanup_ctx(void *alloc_ctx);
+int vb2_dma_contig_set_max_seg_size(struct device *dev, unsigned int size);
+void vb2_dma_contig_clear_max_seg_size(struct device *dev);
extern const struct vb2_mem_ops vb2_dma_contig_memops;
diff --git a/include/soc/tegra/cpuidle.h b/include/soc/tegra/cpuidle.h
index ea04f4225638..1fae9c7800d1 100644
--- a/include/soc/tegra/cpuidle.h
+++ b/include/soc/tegra/cpuidle.h
@@ -14,7 +14,7 @@
#ifndef __SOC_TEGRA_CPUIDLE_H__
#define __SOC_TEGRA_CPUIDLE_H__
-#ifdef CONFIG_CPU_IDLE
+#if defined(CONFIG_ARM) && defined(CONFIG_CPU_IDLE)
void tegra_cpuidle_pcie_irqs_in_use(void);
#else
static inline void tegra_cpuidle_pcie_irqs_in_use(void)